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
I have below table structure and I would like to obtain the result in the following form:
First, this is my item table output:
orderID code action id level description Price solvedChoice
--------------------------------------------------------------------------
321 622 RECIPE 0 0 SPICM1 15.5 NULL
321 10 RECIPE 0 1 SPICKN 17 NULL
321 7091 RECIPE 0 1 RFRY 8.5 NULL
321 521 CHOICE 0 1 R-COKE 7.5 10000003
321 612 RECIPE 1 0 BIGTM1 20.5 NULL
321 13 RECIPE 1 1 BTASTY 21 NULL
321 7091 RECIPE 1 1 RFRY 8.5 NULL
321 522 CHOICE 1 1 R-FANT 7.5 10000003
321 608 RECIPE 2 0 ROYAL1 18.5 NULL
321 11 RECIPE 2 1 MCROYA 18 NULL
321 7091 RECIPE 2 1 RFRY 8.5 NULL
321 411 CHOICE 2 1 ARWA 7.5 10000003
321 612 RECIPE 3 0 BIGTM1 20.5 NULL
321 13 RECIPE 3 1 BTASTY 21 NULL
321 7091 RECIPE 3 1 RFRY 8.5 NULL
321 524 CHOICE 3 1 R-SPRT 7.5 10000003
I want to get what select under each meal, for example id = 0, represent one meal with their sub-level (components) and we can see the choice made was R-Coke while for id =1 , the choice made is R-FANT.
The output should be like this:
R-COKE R-FANT ARWA R-SPRT
--------------------------------------
SPICM1 1 0 0 0
BIGTM1 0 1 0 1
ROYAL1 0 0 1 0
This looks like two levels of aggregation to me:
select col1,
sum(r_coke) as r_coke,
sum(r_fant) as r_fant,
sum(arwa) as arwa,
sum(r_sprt) as r_sprt
from (select max(case when level = 0 then description end) as col1,
sum(case when description = 'R-COKE' then 1 else 0 end) as r_coke,
sum(case when description = 'R-FANT' then 1 else 0 end) as r_fant,
sum(case when description = 'ARWA' then 1 else 0 end) as arwa,
sum(case when description = 'R-SPRT' then 1 else 0 end) as r_sprt
from t
group by id
) x
group by col1;
Or, perhaps more simply, using window functions:
select col1,
sum(case when description = 'R-COKE' then 1 else 0 end) as r_coke,
sum(case when description = 'R-FANT' then 1 else 0 end) as r_fant,
sum(case when description = 'ARWA' then 1 else 0 end) as arwa,
sum(case when description = 'R-SPRT' then 1 else 0 end) as r_sprt
from (select t.*,
max(case when level = 0 then description end) over (partition by id) as col1
from t
) t
group by col1;
Just following input and output provided
select orderID, x, [R-COKE], [R-FANT], [ARWA], [R-SPRT]
from (
select orderID, id
, max(case when level = 0 then description end) x
, max(case when level = 1 and solvedChoice is not null then description end) y
from mytable
group by orderID, id
) t
pivot (count(id) for y in ([R-COKE], [R-FANT], [ARWA], [R-SPRT]) ) pvt;
You could join the table to itself. Something like this
drop TABLE if exists #MyItemTable;
go
CREATE TABLE #MyItemTable
(
orderID INT,
code int,
action char(6),
id int,
level int,
description varchar(10),
price money,
solvedChoice int
)
INSERT INTO #MyItemTable (orderID, code, action, id , level, description, Price, solvedChoice)
VALUES
(321, 622 ,'RECIPE',0,0,'SPICM1',15.5 ,NULL)
,(321, 10 ,'RECIPE',0,1,'SPICKN',17 ,NULL )
,(321, 7091,'RECIPE',0,1,'RFRY ',8.5 ,NULL )
,(321, 521 ,'CHOICE',0,1,'R-COKE',7.5 ,10000003)
,(321, 612 ,'RECIPE',1,0,'BIGTM1',20.5 ,NULL )
,(321, 13 ,'RECIPE',1,1,'BTASTY',21 ,NULL )
,(321, 7091,'RECIPE',1,1,'RFRY ',8.5 ,NULL )
,(321, 522 ,'CHOICE',1,1,'R-FANT',7.5 ,10000003)
,(321, 608 ,'RECIPE',2,0,'ROYAL1',18.5 ,NULL )
,(321, 11 ,'RECIPE',2,1,'MCROYA',18 ,NULL )
,(321, 7091,'RECIPE',2,1,'RFRY ',8.5 ,NULL )
,(321, 411 ,'CHOICE',2,1,'ARWA ',7.5 ,10000003)
,(321, 612 ,'RECIPE',3,0,'BIGTM1',20.5 ,NULL )
,(321, 13 ,'RECIPE',3,1,'BTASTY',21 ,NULL )
,(321, 7091,'RECIPE',3,1,'RFRY ',8.5 ,NULL )
,(321, 524 ,'CHOICE',3,1,'R-SPRT',7.5 ,10000003);
select i.[description],
sum(case when i2.[description] = 'R-COKE' then 1 else 0 end) as r_coke,
sum(case when i2.[description] = 'R-FANT' then 1 else 0 end) as r_fant,
sum(case when i2.[description] = 'ARWA' then 1 else 0 end) as arwa,
sum(case when i2.[description] = 'R-SPRT' then 1 else 0 end) as r_sprt
from #MyItemTable i
left join #MyItemTable i2 on i.id=i2.id
and i2.[action]='CHOICE'
where i.[level]=0
group by i.[description];
description r_coke r_fant arwa r_sprt
BIGTM1 0 1 0 1
ROYAL1 0 0 1 0
SPICM1 1 0 0 0
The aim is to get 1 result for each type of order, that is represented by level=0, id is the order identifier.
You need to first normalize the results by querying the orders and the CHOICE items separately, then you can correlate them with a join.
Once you have identified the spearate order and item records, then we can easily target them with aggregates, in this case a simple COUNT
COUNT works well in this context because it will exclude NULL values.
SELECT [order].description
, COUNT(DISTINCT [order].id) as [Orders]
, COUNT(CASE WHEN item.description = 'R-COKE' THEN 1 END) as [R-COKE]
, COUNT(CASE WHEN item.description = 'R-FANT' THEN 1 END) as [R-FANT]
, COUNT(CASE WHEN item.description = 'ARWA' THEN 1 END) as [ARWA]
, COUNT(CASE WHEN item.description = 'R-SPRT' THEN 1 END) as [R-SPRT]
FROM tblOrders [order]
INNER JOIN tblOrders item ON item.id = [order].id AND item.level = 1
WHERE [order].level = 0
GROUP BY [order].description
Try it out in this fiddle: http://sqlfiddle.com/#!18/81ec5/1
To further highlight the groupings, I have included a count of the separate orders, seeing we are counting the drinks as well.
table 1 looks like this:
filekey hourstype hours
123 1 40
123 2 5
123 3 6
123 4 7
123 5 8
needed output should look like this:
filekey hours1 hours2 otherhourstype otherhourstotal
123 40 5 '' ''
123 '' '' 3 6
123 '' '' 4 7
123 '' '' 5 8
hours1 and hours2 occupy the same row, all other hours occupy their own row
there is one other possible format that can work:
filekey hours1 hours2 difhrstype difhrstotal difhourstype difhrstotal
123 40 5 3 6 4 7
in this scenario start with lowest hours type, then total extended through columns instead of rows with one row per filekey. I'm not sure how to make this one happen either. especially because there can be up to 8 hourstypes each of which may or may not exist for a given filekey
Try this for scenario 1:
CREATE TABLE #TMP(filekey INT, hourstype INT, [hours] INT)
INSERT INTO #TMP VALUES
(123,1,40)
,(123,2,5)
,(123,3,6)
,(123,4,7)
,(123,5,8)
SELECT
T.filekey
,SUM(CASE WHEN hourstype = 1 THEN [hours] ELSE 0 END) AS hours1
,SUM(CASE WHEN hourstype = 2 THEN [hours] ELSE 0 END) AS hours2
,CASE WHEN hourstype > 2 THEN [hourstype] ELSE 0 END AS otherhourstype
,CASE WHEN hourstype > 2 THEN [hours] ELSE 0 END AS otherhourstotal
FROM
#TMP T
GROUP BY
T.filekey
,CASE WHEN hourstype > 2 THEN [hourstype] ELSE 0 END
,CASE WHEN hourstype > 2 THEN [hours] ELSE 0 END
UNION a query that will produce the first row over a query that will produce the other three rows.
Hard-code the NULLs (or blanks or whatever you want) for the unused columns in each query.
Try the following:
select
pivottable.filekey,
[1] as hours1,
[2] as hours2,
[3] as hours3,
[4] as hours4,
[5] as hours5,
[6] as hours6,
[7] as hours7,
[8] as hours8
from table1
PIVOT (
sum(hours)
FOR hourstype IN ([1],[2],[3],[4],[5],[6],[7],[8])
) as pivottable
I have report that was coded I was told some time ago, and it shows data for a 'Ratings' and 'Result' column across the span of a year.
Currently the script is using Case statements to acheive this, however I believe this is what the Pivot Function is for and could save some lines of code. Can anyone assist me in changing the logic to use a pivot function?Current Logic:
Select
EmployeeID,
Employee,
--Datetype,
EmpGroupID,
PA,
EmpLevel,
ObjectiveName as Objective,
Objectiveid AS Objectiveid,
Weighting,
ReportingYear,
max(Case When r.DateType = 'M'
and Month(r.StartDate) = 1
and r.result IS NOT NULL
Then r.Result
When r.DateType = 'M'
and Month(r.StartDate) = 1
and r.result IS NULL
and r.rating is NOT NULL THEN COALESCE(rtovr.ratingShortDesc,rt.ratingShortDesc)
Else null
END) as Value_January_1,
MAX(Case When r.DateType = 'M'
and Month(r.StartDate) = 1
Then dbo.udfGetRating(case when r.RatingOverride is null
then r.Rating
else r.RatingOverride
end) + '|' + CAST(UserOverride AS CHAR(1)) + '|' + rt.ratingShortDesc
Else null
END) as Rating_January_1,
From #tblResults R
WHERE r.datetype IN ('M','Y')
... follows that case logic in the select all the way to Dec.
Example Data on table #tblResult:
EmployeeID Employee Datetype EmpGroupID PA EmpLevel ObjectiveName Objectiveid Weighting RPTYear ObjSortOrder ObjGrpSortOrder Result Rating RatingOverride startdate UserOverride
552940 test Q 664 Sr Rep 1 Overall Rating -1 1 2012 0 0 2 2.00 NULL 2012-01-01 0
552940 test Q 664 Sr Rep 1 Overall Rating -1 1 2012 0 0 2.39 2.00 NULL 2012-04-01 0
Trying to get a basic understanding of T-SQL here in SQL Server 2008. Suppose I have a table named "Issues" with columns such as:
Priority User
1 Foo
1 Foo
2 Foo
5 Foo
4 Bar
5 Bar
1 Bar
1 Fuz
and I wish to display a count of the Priority for each User, along with a breakdown of each Priority, such that the resulting table might be named "Breakdown" might look like
User Total 1 2 3 4 5
Foo 4 2 1 0 0 1
Bar 3 1 0 0 1 1
Fuz 1 1 0 0 0 0
I was thinking I might declare variables and write my query something like
DECLARE #P1 INT
DECLARE #P2 INT
DECLARE #P3 INT
DECLARE #P4 INT
DECLARE #P5 INT
SELECT COUNT(id) AS Total,UserName,
CASE Priority
WHEN 1 Then #P1 = #P1 + 1
WHEN 2 Then #P2 = #P2 + 1
WHEN 3 Then #P3 = #P3 + 1
WHEN 4 Then #P4 = #P4 + 1
WHEN 5 Then #P5 = #P5 + 1
END,
FROM Breakdown
GROUP BY UserName
but I'm pretty sure I'm on the wrong track. Does anyone have any suggestions?
Thanks, and sorry for the noobish question; but I'm not sure exactly what to google for here...
-R.
Use:
SELECT i.user,
COUNT(i.priority) AS total,
SUM(CASE WHEN i.priority = 1 THEN 1 ELSE 0 END) AS 1,
SUM(CASE WHEN i.priority = 2 THEN 1 ELSE 0 END) AS 2,
SUM(CASE WHEN i.priority = 3 THEN 1 ELSE 0 END) AS 3,
SUM(CASE WHEN i.priority = 4 THEN 1 ELSE 0 END) AS 4,
SUM(CASE WHEN i.priority = 5 THEN 1 ELSE 0 END) AS 5
FROM ISSUES i
GROUP BY i.user
It's a pivot query, converting row data into columnar data.
Not a noob/beginner issue to deal with. SQL Server 2005+ added the (now ANSI) PIVOT/UNPIVOT syntax, but this is portable to most databases (because few currently support PIVOT/UNPIVOT).
You need to SELECT one column for each column you want in your result set. In your SQL, you're only selecting three columns. Try:
SELECT UserName,
Count(*) AS Total,
SUM(CASE Priority WHEN 1 THEN 1 ELSE 0 END) AS P1_Total,
SUM(CASE Priority WHEN 2 THEN 1 ELSE 0 END) AS P2_Total,
SUM(CASE Priority WHEN 3 THEN 1 ELSE 0 END) AS P3_Total,
SUM(CASE Priority WHEN 4 THEN 1 ELSE 0 END) AS P4_Total,
SUM(CASE Priority WHEN 5 THEN 1 ELSE 0 END) AS P5_Total
FROM Issues
GROUP BY UserName