I am using sql 2008
My data set looks like
Entity Type1 Type2 Balance
1 A R 100
1 B Z 200
1 C R 300
2 A X 1000
2 B Y 2000
My output should look like
Entity A-Type2 A-Balance B-Type2 B-Balance C-Type2 C-Balance
1 R 100 Z 200 R 300
2 X 1000 Y 2000 0
Now I started writing a pivot query, and I think I can get away with MAX because there should be one record per Entity/Type1 combination. But can not figure out how to do two fields in one pivot. Is this possible? Is this something that CTE could help out with?
Easiest is the MAX idea, but with a CASE statement, e.g.:
SELECT
Entity,
MAX(CASE WHEN Type1 = 'A' THEN Type2 ELSE NULL END) AS AType2,
MAX(CASE WHEN Type1 = 'A' THEN Balance ELSE NULL END) AS ABalance,
MAX(CASE WHEN Type1 = 'B' THEN Type2 ELSE NULL END) AS BType2,
MAX(CASE WHEN Type1 = 'B' THEN Balance ELSE NULL END) AS BBalance,
MAX(CASE WHEN Type1 = 'C' THEN Type2 ELSE NULL END) AS CType2,
MAX(CASE WHEN Type1 = 'C' THEN Balance ELSE NULL END) AS CBalance
FROM
...
GROUP BY
Entity
In other words, only use the value when Type1 is a specific value (with other Type1 values getting a null).
You just use conditional aggregation for the pivoting like this:
select Entity,
max(case when Type1 = 'A' then Type2 end) as A_Type2,
max(case when Type1 = 'A' then Balance else 0 end) as A_Balance,
max(case when Type1 = 'B' then Type2 end) as B_Type2,
max(case when Type1 = 'B' then Balance else 0 end) as B_Balance,
max(case when Type1 = 'C' then Type2 end) as C_Type2,
max(case when Type1 = 'C' then Balance else 0 end) as C_Balance
from MyDataSet mds
group by Entity;
Here's doing it with a pivot and a lookup.
SELECT
data.Entity,
ISNULL(a.Type2,'') AS [A-Type2],
ISNULL([A-Balance],0) AS [A-Balance],
ISNULL(b.Type2,'') AS [B-Type2],
ISNULL([B-Balance],0) AS [B-Balance],
ISNULL(c.Type2,'') AS [C-Type2],
ISNULL([C-Balance],0) AS [C-Balance]
FROM
(
SELECT
Entity,
A AS [A-Balance],
B AS [B-Balance],
C AS [C-Balance]
FROM
(
SELECT Entity, Type1, Balance FROM #table
) t
PIVOT (
MAX(Balance)
FOR Type1 IN ([A],[B],[C])
) piv
) data
LEFT OUTER JOIN #table a on a.Type1 = 'A'
AND a.Entity = data.Entity AND a.Balance = [A-Balance]
LEFT OUTER JOIN #table b on b.Type1 = 'B'
AND b.Entity = data.Entity AND b.Balance = [B-Balance]
LEFT OUTER JOIN #table c on c.Type1 = 'C'
AND c.Entity = data.Entity AND c.Balance = [C-Balance]
Related
This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
Closed 3 years ago.
I have a table I loaded that looks like this:
CUSTID VALUETYPE COST
1 A 123
1 B 456
1 C 789
2 B 222
And I need to flatten it out in the same table or insert into a new one to look like this:
CUSTID A B C
1 123 456 789
2 0 222 0
Each row has an identity column not shown.
What would this cursor look like?
Thank you.
I don't see that you need to sum the columns:
select
custid,
max(case when valuetype = 'A' then cost else 0 end) A,
max(case when valuetype = 'B' then cost else 0 end) B,
max(case when valuetype = 'C' then cost else 0 end) C
from tablename
group by custid
Use a query, such as conditional aggregation:
select custid,
sum(case when valuetype = 'A' then cost end) as a,
sum(case when valuetype = 'B' then cost end) as b,
sum(case when valuetype = 'C' then cost end) as c
from t
group by custid;
use case when
select custid , sum(case when valuetype='A' then cost else 0 end) A,
sum(case when valuetype='B' then cost else 0 end) B
,sum(case when valuetype='C' then cost else 0 end) C
from t group by custid
You could make use of a PIVOT
SELECT
CUSTID
,ISNULL(p.A,0) AS A
,ISNULL(p.B,0) AS B
,ISNULL(p.C,0) AS C
FROM t
PIVOT (
SUM(COST) FOR VALUETYPE IN ([A],[B],[C])) p
If you don't mind NULL values in the Results
Select *
From YourTable
Pivot (sum(Cost) for ValueType in ([A],[B],[C])) pvt
Returns
CUSTID A B C
1 123 456 789
2 NULL 222 NULL
Otherwise, You Can Eliminate NULL Values
Select *
From (Select * From YourTable
Union All
Select A.CustID ,B.VALUETYPE,0
From (Select Distinct CustID from YourTable) A
Cross Join (Select Distinct VALUETYPE from YourTable) B
) src
Pivot (sum(Cost) for ValueType in ([A],[B],[C])) pvt
Returns
CUSTID A B C
1 123 456 789
2 0 222 0
Input:
id sem1 sem2 sem3 sem4 sem5 sem6 sem7
1 S O S R null null null
2 O O R R S null null
Desired Output:
id O R S
1 1 1 2
2 2 2 1
If your database supports APPLY/UNPIVOT operator then use this
CROSS APPLY method
SELECT id,
SUM(CASE WHEN val = 'O' THEN 1 ELSE 0 END) O,
SUM(CASE WHEN val = 'R' THEN 1 ELSE 0 END) R,
SUM(CASE WHEN val = 'S' THEN 1 ELSE 0 END) S
FROM mytable
CROSS apply (VALUES (sem1),
(sem2),
(sem3),
(sem4),
(sem5),
(sem6),
(sem7)) cs(val)
GROUP BY id
SQL FIDDLE DEMO
UNPIVOT method
SELECT id,
SUM(CASE WHEN val = 'O' THEN 1 ELSE 0 END) O,
SUM(CASE WHEN val = 'R' THEN 1 ELSE 0 END) R,
SUM(CASE WHEN val = 'S' THEN 1 ELSE 0 END) S
FROM (SELECT *
FROM mytable) a
UNPIVOT (val
FOR col IN ( sem1,
sem2,
sem3,
sem4,
sem5,
sem6,
sem7 )) upv
GROUP BY id
SQL FIDDLE DEMO
I personally prefer CROSS APPLY method over UNPIVOT since it is more readable. Performance wise both will be identical
I have a query that essentially amounts to:
Select query 1
Union
Select query 2
where rowid not in query 1 rowids
Is there a prettier / more performant way to do this? I'm assuming the results of query 1 would be cached and thus utilized in the union... but it's also kinda oogly.
Update with the original query:
SELECT FruitType
, count(CASE WHEN Status = 0 THEN 1 ELSE 0 END) AS Fresh
, count(CASE WHEN Status = 1 THEN 1 ELSE 0 END) AS Ripe
, count(CASE WHEN Status = 2 THEN 1 ELSE 0 END) AS Moldy
FROM FruitTypes FT1
LEfT JOIN Fruits F on F.FTID = FT1.ID
where
Fruit.IsHighPriced = 0
GROUP BY FruitType
Union ALL
select FruitType, 0 as Fresh, 0 as Ripe, 0 as Moldy
FROM FruitTypes ft3
where
ft3.StoreID = #PassedInStoreID
and FruitType NOT IN
(
SELECT FruitType
, count(CASE WHEN Status = 0 THEN 1 ELSE 0 END) AS Fresh
, count(CASE WHEN Status = 1 THEN 1 ELSE 0 END) AS Ripe
, count(CASE WHEN Status = 2 THEN 1 ELSE 0 END) AS Moldy
FROM FruitTypes FT2
LEfT JOIN Fruits F on F.FTID = FT2.ID
where
Fruit.IsHighPriced = 0
GROUP BY FruitType
)
Thanks!
You don't need the second case statement in the NOT in clause. And not Exists is often faster in SQL Server.
SELECT FruitType
, count(CASE WHEN Status = 0 THEN 1 ELSE 0 END) AS Fresh
, count(CASE WHEN Status = 1 THEN 1 ELSE 0 END) AS Ripe
, count(CASE WHEN Status = 2 THEN 1 ELSE 0 END) AS Moldy
FROM FruitTypes FT1
LEfT JOIN Fruits F on F.FTID = FT1.ID
where
Fruit.IsHighPriced = 0
GROUP BY FruitType
Union ALL
select FruitType, 0 as Fresh, 0 as Ripe, 0 as Moldy
FROM FruitTypes ft3
where
ft3.StoreID = #PassedInStoreID
and NOT EXISTS
(
SELECT *
FROM FruitTypes FT2
LEfT JOIN Fruits F on F.FTID = FT2.ID
where
Fruit.IsHighPriced = 0
and ft3.FruitType = FT2.FruitType
)
The prettiest way of writing would probably be by turning query #1 into a view or a function, then using that view or function to call the repetitious code.
Performance could possibly be improved by using query #1 to fill a temp table or table variable, then using that temp table in place of the repititious code.
I have a table like below -
Student ID | History | Maths | Geography
1 A B B
2 C C E
3 D A B
4 E D A
How to find out how many students got A in history, B in maths and E in Geography with a single sql query ?
If you want to get number of students who got A in History in one column, number of students who got B in Maths in second column and number of students who got E in Geography in third then:
select
sum(case when [History] = 'A' then 1 else 0 end) as HistoryA,
sum(case when [Maths] = 'B' then 1 else 0 end) as MathsB,
sum(case when [Geography] = 'E' then 1 else 0 end) as GeographyC
from Table1
If you want to count students who got A in history, B in maths and E in Geography:
select count(*)
from Table1
where [History] = 'A' and [Maths] = 'B' and [Geography] = 'E'
If you want independent counts use:
SELECT SUM(CASE WHEN Condition1 THEN 1 ELSE 0 END) AS 'Condition1'
,SUM(CASE WHEN Condition2 THEN 1 ELSE 0 END) AS 'Condition2'
,SUM(CASE WHEN Condition3 THEN 1 ELSE 0 END) AS 'Condition3'
FROM YourTable
If you want multiple conditions for one count use:
SELECT COUNT(*)
FROM YourTable
WHERE Condition1
AND Condition2
AND Condition3
It sounds like you want multiple independent counts:
SELECT SUM(CASE WHEN History = 'A' THEN 1 ELSE 0 END) AS 'History A'
,SUM(CASE WHEN Maths = 'B' THEN 1 ELSE 0 END) AS 'Maths B'
,SUM(CASE WHEN Geography = 'E' THEN 1 ELSE 0 END) AS 'Geography E'
FROM YourTable
You can try to select from multiple select statements
SELECT t1.*, t2.*, t3.* FROM
(SELECT COUNT(*) AS h FROM students WHERE History = 'A') as t1,
(SELECT COUNT(*) AS m FROM students WHERE Maths = 'B') as t2,
(SELECT COUNT(*) AS g FROM students WHERE Geography = 'E') as t3
I'm trying to do a full join on two SQL queries, below:
1st Query:
SELECT
ID
,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue
,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate
GROUP BY ID
2nd Query:
SELECT
ID
,SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New
,SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend
,SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2
WHERE Date2 >=#StartDate AND Date2<= #EndDate
GROUP BY ID
Result from query1
ID CountAll TValue
-------------------------
id1 24 1020
id2 13 2030
id3 4 120
Result from query 2:
ID New Amend Cancel
--------------------------------
id1 12 4 6
id2 7 6 1
id4 2 1 2
Needed output:
ID TValue CountAll New Amend Cancel Total(countall+new+amend+cancel)
----------------------------------------------------------------------------------------
Id1 1020 24 12 4 6 46
Id2 2030 13 7 6 1 27
id3 120 4 0 0 0 4
Id4 0 0 2 1 2 5
I'll post my current solution if requested, but it is pretty far from working.
I've been doing a bit of research and I think I need to either make a union to join the ID'S, or just do a Full Join. (Second day ever doing sql)
Try this,
SELECT *
FROM
(
SELECT ID ,
SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue,
COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate
GROUP BY ID
) a FULL JOIN
(
SELECT ID ,
SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New ,
SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend ,
SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2
WHERE Date2 >=#StartDate AND Date2<= #EndDate
GROUP BY ID
) b ON a.ID = b.ID
I would write something like below:
select decode (a.id, null, b.id, a.id) as ID, a.TValue, CountAll, b.new, b.Amend, b.cancel
from (SELECT ID ,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END)
AS TValue ,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate GROUP BY ID
) a FULL OUTER JOIN
(SELECT ID , SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END)
AS New ,SUM(CASE WHEN reason = 6
THEN 1 ELSE 0 END) AS Amend ,
SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2 WHERE Date2 >=#StartDate AND Date2<= #EndDate GROUP BY ID
) b
on a.id = b.id
have you tried this...
select isnull (a.id,b.id) as ID, a.TValue, CountAll, b.new, b.Amend, b.cancel
from (SELECT ID ,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue ,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate GROUP BY ID ) a
FULL OUTER JOIN (SELECT ID , SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New ,SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend , SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2 WHERE Date2 >=#StartDate AND Date2<= #EndDate GROUP BY ID ) b on a.id = b.id