How to Pivot one columns multiple times? - sql

A table is
ID YEAR Name Value
1 2014 A 10
2 2014 B 20
1 2013 A 30
Want a table like below:
ID 2014 2013 A B
1 10 20 40 0
2 20 0 0 20
Bascially want to Pivot Value column for both Year and Name. How can I do this?

I would do this with conditional aggregation:
select id,
sum(case when year = 2014 then value else 0 end) as [2014],
sum(case when year = 2013 then value else 0 end) as [2013],
sum(case when name = 'A' then value else 0 end) as [A],
sum(case when name = 'B' then value else 0 end) as [B]
from table t
group by id;

Here is the way using pivot
SELECT id,
Isnull([2014], 0) [2014],
Isnull([2013], 0) [2013],
Isnull([A], 0) [A],
Isnull([B], 0) [B]
FROM (SELECT *,
Sum(value)OVER(partition BY Name) tot
FROM Yourtable) a
PIVOT (Max(Value)
FOR year IN ([2014],
[2013]))pv
PIVOT (Max(tot)
FOR name IN ([A],[B]))pv1
SQLFIDDLE DEMO

Related

Adding a dummy identifier to data that varies by position and value

I am working on a project in SQL Server with diagnosis codes and a patient can have up to 4 codes but not necessarily more than 1 and a patient cannot repeat a code more than once. However, codes can occur in any order. My goal is to be able to count how many times a Diagnosis code appears in total, as well as how often it appears in a set position.
My data currently resembles the following:
PtKey
Order #
Order Date
Diagnosis1
Diagnosis2
Diagnosis3
Diagnosis 4
345
1527
7/12/20
J44.9
R26.2
NULL
NULL
367
1679
7/12/20
R26.2
H27.2
G47.34
NULL
325
1700
7/12/20
G47.34
NULL
NULL
NULL
327
1710
7/12/20
I26.2
J44.9
G47.34
NULL
I would think the best approach would be to create a dummy column here that would match up the diagnosis by position. For example, Diagnosis 1 with A, and Diagnosis 2 with B, etc.
My current plan is to rollup the diagnosis using an unpivot:
UNPIVOT ( Diag for ColumnALL IN (Diagnosis1, Diagnosis2, Diagnosis3, Diagnosis4)) as unpvt
However, this still doesn’t provide a way to count the diagnoses by position on a sales order.
I want it to look like this:
Diagnosis
Total Count
Diag1 Count
Diag2 Count
Diag3 Count
Diag4 Count
J44.9
2
1
1
0
0
R26.2
1
1
0
0
0
H27.2
1
0
1
0
0
I26.2
1
1
0
0
0
G47.34
3
1
0
2
0
You can unpivot using apply and aggregate:
select v.diagnosis, count(*) as cnt,
sum(case when pos = 1 then 1 else 0 end) as pos_1,
sum(case when pos = 2 then 1 else 0 end) as pos_2,
sum(case when pos = 3 then 1 else 0 end) as pos_3,
sum(case when pos = 4 then 1 else 0 end) as pos_4
from data d cross apply
(values (diagnosis1, 1),
(diagnosis2, 2),
(diagnosis3, 3),
(diagnosis4, 4)
) v(diagnosis, pos)
where diagnosis is not null;
Another way is to use UNPIVOT to transform the columns into groupable entities:
SELECT Diagnosis, [Total Count] = COUNT(*),
[Diag1 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis1' THEN 1 ELSE 0 END),
[Diag2 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis2' THEN 1 ELSE 0 END),
[Diag3 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis3' THEN 1 ELSE 0 END),
[Diag4 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis4' THEN 1 ELSE 0 END)
FROM
(
SELECT * FROM #x UNPIVOT (Diagnosis FOR DiagGroup IN
([Diagnosis1],[Diagnosis2],[Diagnosis3],[Diagnosis4])) up
) AS x GROUP BY Diagnosis;
Example db<>fiddle
You can also manually unpivot via UNION before doing the conditional aggregation:
SELECT Diagnosis, COUNT(*) As Total Count
, SUM(CASE WHEN Position = 1 THEN 1 ELSE 0 END) As [Diag1 Count]
, SUM(CASE WHEN Position = 2 THEN 1 ELSE 0 END) As [Diag2 Count]
, SUM(CASE WHEN Position = 3 THEN 1 ELSE 0 END) As [Diag3 Count]
, SUM(CASE WHEN Position = 4 THEN 1 ELSE 0 END) As [Diag4 Count]
FROM
(
SELECT PtKey, Diagnosis1 As Diagnosis, 1 As Position
FROM [MyTable]
UNION ALL
SELECT PtKey, Diagnosis2 As Diagnosis, 2 As Position
FROM [MyTable]
WHERE Diagnosis2 IS NOT NULL
UNION ALL
SELECT PtKey, Diagnosis3 As Diagnosis, 3 As Position
FROM [MyTable]
WHERE Diagnosis3 IS NOT NULL
UNION ALL
SELECT PtKey, Diagnosis4 As Diagnosis, 4 As Position
FROM [MyTable]
WHERE Diagnosis4 IS NOT NULL
) d
GROUP BY Diagnosis
Borrowing Aaron's fiddle, to avoid needing to rebuild the schema from scratch, and we get this:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=d1f7f525e175f0f066dd1749c49cc46d

How to extract and pivot in sql

I have tables like following
I treid to sum score in pivoted style..
product date score
A 2020/8/1 1
B 2018/8/1 2
B 2018/9/1 1
C 2017/9/1 2
I'd like to transform them to the following pivotedone.
The index is YEAR(t.date) and columns = product
date A B C
2017 0 0 2
2018 0 3 0
2019 0 0 0
2020 1 0 0
Are there any effective way to achieve this?
Thanks
We can handle this by joining a calendar table containing all years of interest to your current table, aggregating by year, and then using conditional aggregation to find the sum of scores for each product.
WITH years AS (
SELECT 2017 AS year FROM dual UNION ALL
SELECT 2018 FROM dual UNION ALL
SELECT 2019 FROM dual UNION ALL
SELECT 2020 FROM dual
)
SELECT
y.year,
SUM(CASE WHEN t.product = 'A' THEN t.score ELSE 0 END) AS A,
SUM(CASE WHEN t.product = 'B' THEN t.score ELSE 0 END) AS B,
SUM(CASE WHEN t.product = 'C' THEN t.score ELSE 0 END) AS C
FROM years y
LEFT JOIN yourTable t
ON y.year = EXTRACT(YEAR FROM t."date")
GROUP BY
y.year
ORDER BY
y.year;
Demo
One option would be using PIVOT Clause after determining the year range, and joining outerly with your data source and setting the null scores as zeroes :
WITH years AS
(SELECT MIN(EXTRACT(year from "date")) AS min_year,
MAX(EXTRACT(year from "date")) AS max_year
FROM tab)
SELECT year, NVL(A,0) AS A, NVL(B,0) AS B, NVL(C,0) AS C
FROM (SELECT l.year, product, SUM(score) AS score
FROM tab t --> your original data source
RIGHT JOIN (SELECT level + min_year - 1 AS year
FROM years
CONNECT BY level BETWEEN 1 AND max_year - min_year + 1) l
ON l.year = EXTRACT(year from "date")
GROUP BY l.year, product)
PIVOT (SUM(score) FOR product IN('A' AS "A", 'B' AS "B", 'C' AS "C"))
ORDER BY year;
YEAR A B C
---- - - -
2017 0 0 2
2018 0 3 0
2019 0 0 0
2020 1 0 0
Demo

conditional aggregation on two different groups in SQL

Here is my data
COUNTYID POLLUTANT TYPE EMISSION
1 A 1
1 A 2
1 B 1
1 B 2
2 A 1
2 A 2
2 B 1
2 B 2
3 A 1
3 A 2
3 B 1
3 B 2
if I do
SELECT sum(EMISSION) from table where POLLUTANT = 'A' group by COUNTYID;
I would get pollution from Polutant 'A'. how can I write a query to get following data:
column 1 with sum of A, column 2 with sum of B, column 3 with sum of A and B?
Thank you
You can use case for filter the value you need
select COUNTYID, sum(case when POLLUTANT='A'then EMISSION else 0 END) tot_a
, sum(case when POLLUTANT='B'then EMISSION else 0 END) tot_b
, sum(EMISSION) tot_a_b
from my_table
group by COUNTYID
You can use conditional aggregation. This moves the filtering conditions from the where clause to the sum()s:
select countyid,
sum(case when emission = 'A' then emission else 0 end) as A,
sum(case when emission = 'B' then emission else 0 end) as B,
sum(emission) as total
from t
group by countyid;
I would like to give some idea. We can use pivot table for answer your question
SELECT * from dbo.[Table_1]
PIVOT
(Sum([type_emmssion]) for [polutant] in ([A], [B]) ) as PivotTable
group by [CountryId] ;
you have to use case statemet:
SELECT
SUM( CASE WHEN POLLUTANT = 'A' THEN EMISSION ELSE 0 END) AS A_EMISSION
SUM( CASE WHEN POLLUTANT = 'B' THEN EMISSION ELSE 0 END) AS B_EMISSION
SUM(EMISSION) AS total_emission
FROM table
GROUP BY COUNTYID;

SQL Server (2012): Pivot (or Group by?) on Multiple Columns

I have the following table:
month seating_id dept_id
Jan 1 5
Jan 8 9
Jan 5 3
Jan 7 2
Jan 1 5
Feb 1 9
Feb 8 9
Feb 5 3
Feb 7 2
Feb 7 1
I want to count each type of seating_id and dept_id for each month, so the result would look something like this:
month seating_id_1 seating_id_5 seating_id_7 seating_id_8 dept_id_1 dept_id_2 dept_id_3 dept_id_5 dept_id_9
Jan 2 1 1 1 0 1 1 2 1
Feb 0 1 2 1 1 1 1 0 2
I've experimented with unpivot/pivot and GROUP BY but haven't been able to achieve the desired results. Note, I would like to perform this as a SELECT statement, and not PROCEDURE call, if possible.
Let me know if you need any other info.
In case you need to go Dynamic
Example
Declare #SQL varchar(max) = '
Select *
From (
Select month,B.*
From YourTable A
Cross Apply (values (concat(''seating_id_'',seating_id),seating_id)
,(concat(''dept_id_'',dept_id),dept_id)
) b(item,value)
) A
Pivot (count([Value]) For [Item] in (' + Stuff((Select Distinct concat(',[seating_id_',seating_id,']') from YourTable For XML Path('')),1,1,'')
+','+
Stuff((Select Distinct concat(',[dept_id_',dept_id,']') from YourTable For XML Path('')),1,1,'')
+ ') ) p'
Exec(#SQL);
Returns
The Generated SQL Looks like this
Select *
From (
Select month,B.*
From YourTable A
Cross Apply (values (concat('seating_id_',seating_id),seating_id)
,(concat('dept_id_',dept_id),dept_id)
) b(item,value)
) A
Pivot (count([Value]) For [Item] in ([seating_id_1],[seating_id_5],[seating_id_7],[seating_id_8],[dept_id_1],[dept_id_2],[dept_id_3],[dept_id_5],[dept_id_9]) ) p
i have an answer you might not like but it does it:
select month
, sum(case seating_id when 1 then 1 else 0 end) as seating_id_1
, sum(case seating_id when 2 then 1 else 0 end) as seating_id_2
...
, sum(case dept_id when 1 then 1 else 0 end) as dept_id_1
, sum(case dept_id when 2 then 1 else 0 end) as dept_id_2
...
from YourTable
group by month

SQL Embedded Case

I am new to Sql. The logic of those tables without IF condition always annoys me. I have a table (MyTable) like this:
ID ProjectID ClassType ClassYear Amount
1 1 A 2014 0.00
2 1 A 2014 0.00
3 1 B 2014 300.00
5 1 B 2013 100.00
6 1 C 2015 200.00
7 1 A 2013 0.00
8 1 B 2015 200.00
9 1 B 2014 500.00
11 1 B 2015 230.00
....
My original code as following works fine:
Some code
from
(
Select projectID, sum(currentyear) as CurrentYear, sum(prioryear) as PriorYear, sum(postyear) as PostYear
from
(select ProjectID,
case when Classyear = 2014 then Amount else 0 end as currentyear,
case when ClassYear <2014 then Amount else 0 end as prioryear,
case when ClassYear > 2014 then Amount else 0 end as postyear
from MyTable
where classtype = 'A'
) as subtable
group by subtable.ProjectID
)
Right now I want to add more option for the classtype in the above and can not figure out how. The logic should be:
if total amount from classtype A in a project is 0 (in other word, those three numbers are all 0)then the where condition should change to classtype = 'B'. If classtype B are still all 0 then use the classtype C. (It will be OK if C are still 0).
Where should I put the new case condition?
we can get total using GROUP BY for each class type and then filter appropriate value.
SELECT *
FROM
(
select *, ROW_NUMBER() over ( partition by projectId order by classType) as seq FROM
(
Select projectID, classType, sum(Amount) as Total,
sum(case when Classyear = 2014 then Amount else 0 end) as CurrentYear,
sum(case when ClassYear <2014 then Amount else 0 end) as PriorYear,
sum( case when ClassYear > 2014 then Amount else 0 end) as PostYear
from
myTable
group by ProjectID, classType
) T
Where (T.Total >0 and classType <> 'C') or classType ='C'
) C
where seq =1