If I have the following table:
Cust Prod Qty
===== ==== ===
Bob Apple 3
Bob Orange 2
Bob Banana 4
Rob Apple 2
Rob Orange 1
Bob Apple 2
How can I get the following result with the table data as the column names:
Prod Bob Rob
====== === ===
Apple 5 2
Orange 2 1
Banana 4 null
You can use PIVOT in MSSQL or the following way:
SELECT
PROD,
SUM(CASE WHEN Cust='Bob' THEN Qty ELSE 0 END) as Bob,
SUM(CASE WHEN Cust='Rob' THEN Qty ELSE 0 END) as Rob
FROM T
GROUP BY PROD
SQLFiddle demo
SELECT * from t
pivot
(
sum(Qty)
for [Cust] in ([Bob],[Rob])
)as p;
GROUP BY PROD
fiddle demo
Related
Looking for assistance in writing a SQL Query to return data in a particular format. My table has the following structure
Table name: ExpiryTable
FRUIT | EXPIRY
--------+--------------
Apple | Monday
Apple | Wednesday
Banana | Tuesday
Orange | Monday
Orange | Tuesday
Pear | Monday
Pear | Tuesday
Pear | Wednesday
The output I need would show the following, where the fruits go across the result set as columns. This means that this week there are 4 fruits, next week there could be 7 fruits, so it has to factor in that the columns may expand or collapse.
| Apple | Banana | Orange | Pear
----------+--------+---------+---------+-------
Monday | 1 | 0 | 1 | 1
Tuesday | 0 | 1 | 1 | 1
Wednesday | 1 | 0 | 0 | 1
With regards to the rows, the days will actually never change. There will only ever be a Monday, Tuesday and Wednesday in the expiry cycle.
I do have another table which contains the complete list of available fruits sold, if that helps with the SQL statement in any way.
Table name: FruitMaster
Fruit
-------
Apple
Orange
Pear
Watermelon
Banana
Orange
Rockmelon
Tangerine
Kiwi-fruit
All I have thought of to tackle this is selecting the distinct expiry from ExpiryTable and then LEFT JOIN the aggregate of each fruit, however I am not sure how I would factor in where this week there is 4 fruits expiring, and next week there is more or less. If it was always a fixed list of Fruits I could get way with this technique, but alas its not.
Any assistance would really help me in getting started on this one.
You may use pivoting logic here:
SELECT
EXPIRY,
COUNT(CASE WHEN FRUIT = 'Apple' THEN 1 END) AS Apple,
COUNT(CASE WHEN FRUIT = 'Banana' THEN 1 END) AS Banana,
COUNT(CASE WHEN FRUIT = 'Orange' THEN 1 END) AS Orange,
COUNT(CASE WHEN FRUIT = 'Pear' THEN 1 END) AS Pear
FROM yourTable
GROUP BY
EXPIRY;
I have a table:
Customer Purchase
John 5
John 8
John 3
John 1
Sally 3
Sally 5
Sally 2
I want to return two records per customer ignoring the top purchase:
John 5
John 3
Sally 3
Sally 2
With ROW_NUMBER() window function:
select t.customer, t.purchase
from (
select *, row_number() over (partition by customer order by purchase desc) rn
from tablename
) t
where t.rn between 2 and 3
I have three tables in MS SQL Server 2014. Each of them holds a couple of numeric values, a description and a date. For the sake of brevety, let's assume the following tables:
table "beverages"
day beverage amount
---------- -------- ------
2018-12-01 water 2
2018-12-01 tea 1
2018-12-01 coffee 7
2018-12-02 water 4
2018-12-02 tea 2
table "meals"
day meal amount
---------- ------ ------
2018-12-01 burger 1
2018-12-01 bread 2
2018-12-02 steak 1
table "fruit"
day fruit amount
---------- ------ ------
2018-12-01 apple 4
2018-12-01 banana 1
2018-12-02 apple 2
Then I have another table holding only a list of dates.
table "dates"
day
----------
2018-12-01
2018-12-02
What I need is a query that returns one row for each of the rows in the dates table, and in each row has the date, the total amount of beverages, the total amount of meals and the total amount of fruit for that day. I do not care for the different types of beverages, meals and fruit, just the sum. The result should be:
expected result
day beverages meals fruit
---------- ----------- ----------- -----------
2018-12-01 10 3 5
2018-12-02 6 1 2
But instead I receive
received result
day beverages meals fruit
---------- ----------- ----------- -----------
2018-12-01 40 18 30
2018-12-02 6 2 4
I already know what the problem is, just not how to fix it. Even worse, I'm sure that I knew the answer once, but now I can't even figure the right search terms to make Google tell me...
When I do the query like this (I used table variables for testing)
SELECT
[d].[day]
,SUM([b].[amount]) AS [beverages]
,SUM([m].[amount]) AS [meals]
,SUM([f].[amount]) AS [fruit]
FROM #dates AS [d]
LEFT OUTER JOIN #beverages AS [b]
ON [d].[day] = [b].[day]
LEFT OUTER JOIN #meals AS [m]
ON [d].[day] = [m].[day]
LEFT OUTER JOIN #fruit AS [f]
ON [d].[day] = [f].[day]
GROUP BY [d].[day]
it sums each row from the different tables more than once, because it returns every possible combination of the three tables. Removing the SUM() and GROUP BY proves that:
day beverages meals fruit
---------- ----------- ----------- -----------
2018-12-01 2 1 4
2018-12-01 2 1 1
2018-12-01 2 2 4
2018-12-01 2 2 1
2018-12-01 1 1 4
2018-12-01 1 1 1
2018-12-01 1 2 4
2018-12-01 1 2 1
2018-12-01 7 1 4
2018-12-01 7 1 1
2018-12-01 7 2 4
2018-12-01 7 2 1
2018-12-02 4 1 2
2018-12-02 2 1 2
So, what do I need to change in the query to make it sum the values for each of the three tables without multiplying it with the number of the rows in the other tables?
Group the Tables before joining like so:
SELECT
[d].[day]
,[b].[amount] AS [beverages]
,[m].[amount] AS [meals]
,[f].[amount] AS [fruit]
FROM #dates AS [d]
LEFT OUTER JOIN (SELECT day, SUM(amount) as amount FROM #beverages GROUP BY day) AS [b]
ON [d].[day] = [b].[day]
LEFT OUTER JOIN (SELECT day, SUM(amount) as amount FROM #meals GROUP BY day) AS [m]
ON [d].[day] = [m].[day]
LEFT OUTER JOIN (SELECT day, SUM(amount) as amount FROM #fruit GROUP BY day) AS [f]
ON [d].[day] = [f].[day]
How about a PIVOT instead?
Example
Select *
From (
Select day,Item='beverage',amount from beverages
Union All
Select day,Item='meals' ,amount from meals
Union All
Select day,Item='fruit' ,amount from fruit
) src
Pivot ( sum(amount) for Item in ([beverages],[meals],[fruit]) ) pvt
I have a dataset which is of following nature. I would like to replace the names in "MAKE" column if the column contains "PQR" per unique country.
country MAKE
1 USA PQR
2 USA ABC
3 UK PQR
4 UK DEF
5 JPN DEF
6 JPN LMN
Desired Output:
country MAKE
1 USA PQR
2 USA PQR
3 UK PQR
4 UK PQR
5 JPN OTHERS
5 JPN OTHERS
One option is conditional aggregation with analytic functions:
SELECT
country,
CASE WHEN SUM(CASE WHEN MAKE = 'PQR' THEN 1 ELSE 0 END) OVER (PARTITION BY country) > 0
THEN 'PQR' ELSE 'OTHERS' END AS MAKE
FROM yourTable
ORDER BY
country;
Demo
I have 5 tables:
contracts, contracts_data, contracts_anexes, anexes, anexes_data
Table contracts columns :
id_contract | date_sign
------------+-----------
1 | 2013-01-03
2 | 2013-06-05
3 | 2014-10-12
Table contracts_data columns :
id_contract | price
------------+------
1 | 100
2 | 200
3 | 300
Table uontracts_anexes columns :
id_contract | id_anex
------------+--------
1 | 1
1 | 2
2 | 3
Table anexes columns :
id_anex | date_of_sign
--------+--------------
1 | 2014-01-03
2 | 2014-06-05
3 | 2015-01-12
Table anexes_Data columns :
id_anex | price
--------+------
1 | 200
2 | 300
3 | 400
Now I need to select price (from contracts_data or anexes_data) where the date of sign is most recent (max date_sign from contracts and anexes), but not all id_contract are in table contracts_anexes (not all contracts have a annex), and one contract (id_contract) may have multiple anexes (multiple rows in contracts_anexes table)
For example
for id_contract = 1 I need to return price 300 and date 2014-06-05,
for id_contract = 2 I need to return price 400 and date 2015-01-12
for id_contract = 3 I need to return price 300 and date 2014-10-12
You could use UNION ALL together with ROW_NUMBER:
;WITH CteUnion AS(
SELECT
id_contract = c.id_contract,
price = cd.price,
date_sign = c.date_sign
FROM contracts c
LEFT JOIN contracts_data cd
ON cd.id_contract = c.id_contract
UNION ALL
SELECT
id_contract = c.id_contract,
price = ad.price,
date_sign = a.date_sign
FROM contracts c
LEFT JOIN contracts_anexes ca
ON ca.id_contract = c.id_contract
LEFT JOIN anexes a
ON a.id_anex = ca.id_anex
LEFT JOIN anexes_data ad
ON ad.id_anex = a.id_anex
)
SELECT
id_contract,
price,
date_sign
FROM(
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY id_contract ORDER BY date_sign DESC)
FROM CteUnion
)c
WHERE RN = 1
See SQL Fiddle.