Create a score card report based on match results in SQL - sql

Here's my sample input of match results from a tournament and the result column indicates whether team 1 or team 2 won the game (0 stands for a draw).
score table -
-----------------
t1 t2 result
-----------------
us aus 1
aus sa 2
sa us 0
us nz 1
Desired output -
---------------------------------
team played won lost draw
---------------------------------
aus 2 0 2 0
us 3 2 0 1
nz 1 0 1 0
sa 2 1 0 1
Here's my solution that I came up with, but Is it possible to solve it without using a UNION operator?
select
t1 as team,
count(1) as played,
sum(case when result=1 then 1 else 0 end) as won,
sum(case when result=2 then 1 else 0 end) as lost,
sum(case when result=0 then 1 else 0 end) as draw
from
(select
t1,
t2,
result
from score
union
select
t2,
t1,
case when result=1 then 2
when result=2 then 1
else 0 end as result
from score
) t
group by t.t1

You could UNPIVOT your data via a CROSS APPLY and then perform the aggregation on the results
Example
Select Team
,played = count(1)
,won = sum(case when Val=1 then 1 else 0 end)
,lost = sum(case when Val=2 then 1 else 0 end)
,draw = sum(case when Val=0 then 1 else 0 end)
From score A
Cross Apply ( values (T1,Result )
,(T2,case when result=1 then 2 when result=2 then 1 else 0 end)
) B(Team,Val)
Group By Team
Results
team played won lost draw
aus 2 0 2 0
nz 1 0 1 0
sa 2 1 0 1
us 3 2 0 1

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

SQL Select with joins and calculate percentage

I have three tables and I'm trying to make a select statement to give me a result like the one below
Teams:
ID Name
1 A
2 B
3 C
Players:
ID Name TeamID
1 P1 1
2 P2 1
3 P3 2
Goals: (goaltype: H for home, A for away, T for training)
ID PID goaltype
1 1 A
2 1 A
3 1 H
4 2 A
5 2 H
6 3 A
7 3 T
Result will be Like:
Team totalGoals home away trainig percentage[(home/total)*100]
A 5 2 3 0 40%
B 2 0 1 1 0
C 0 0 0 0 0
This is my current query:
select t.name,
count(g.id) as totalGoals,
sum(case when g.GTYPE = 'H' then 1 else 0 end) as home,
sum(case when g.GTYPE = 'A' then 1 else 0 end) as away,
sum(case when g.GTYPE = 'T' then 1 else 0 end) as training,
--(home/totalGoals) as percentage
from teams t
left join players p on p.TeamID = t.id
left join goals g on g.pid = p.id
group by t.name
You can use conditional aggregation to get the results you want:
SELECT t.Name AS Team,
COUNT(g.goaltype) AS totalGoals,
SUM(CASE WHEN g.goaltype = 'H' THEN 1 ELSE 0 END) AS home,
SUM(CASE WHEN g.goaltype = 'A' THEN 1 ELSE 0 END) AS away,
SUM(CASE WHEN g.goaltype = 'T' THEN 1 ELSE 0 END) AS training,
CASE WHEN COUNT(g.goaltype) = 0 THEN 0
ELSE 100.0 * SUM(CASE WHEN g.goaltype = 'H' THEN 1 ELSE 0 END) /
COUNT(g.goaltype)
END AS percentage
FROM Teams t
LEFT JOIN Players p ON p.TeamID = t.ID
LEFT JOIN Goals g ON g.PID = p.ID
GROUP BY t.Name
ORDER BY t.Name
Output:
team totalgoals home away training percentage
A 5 2 3 0 40
B 2 0 1 1 0
C 0 0 0 0 0
Demo on SQLFiddle

How can we count sub query use group by without pivot?

I need to create query that has below result.
I add my table for example for better understanding.
QTable:
QID Name
-------
1 x
1 y
2 z
FTable:
QID FID
-------
1 1
1 2
2 3
Inner join with :
FID Type
-------
1 1
1 2
1 3
1 4
2 2
2 3
2 3
3 1
3 3
3 3
I need to result like this:
result:
QID FID-Count Type(1)-count Type(2)-count Type(3)-count Type(4)-count
-----------------------------------------------------------------
1 2 1 2 3 1
2 1 1 0 2 0
Thank u for help.
Ususally you can emulate a pivot with GROUP BY and SUM+CASE:
SELECT
T1.QID,
COUNT(DISTINCT T1.FID) as FIDCOUNT,
SUM(CASE WHEN Type=1 THEN 1 ELSE 0 END) as Type1Cnt,
SUM(CASE WHEN Type=2 THEN 1 ELSE 0 END) as Type2Cnt,
SUM(CASE WHEN Type=3 THEN 1 ELSE 0 END) as Type3Cnt,
SUM(CASE WHEN Type=4 THEN 1 ELSE 0 END) as Type4Cnt
FROM QFTable as T1
JOIN T2 ON
T1.FID=T2.FID
GROUP BY T1.QID
ORDER BY QID
SQLFiddle demo

Counting values from a column and grouping and sorting by a second value

Let's say I have this table:
NAME YEAR SCORE
_____________________
User1 2010 1
User2 2011 3
User3 2012 2
User4 2013 1
User5 2012 1
User6 2011 3
User7 2010 4
User8 2011 1
I would like to create a query whose output looks like this from this data:
YEAR 1 2 3 4
________________________
2010 1 0 0 1
2011 1 0 2 0
2012 1 1 0 0
2013 1 0 0 0
Where the values in the numbered columns is the count of how many times that score appears in the given year. This seems as if it should be easy but I am having trouble figuring out how to wrap my head around writing this query in a way that is static. Ideas?
select YEAR,
count(CASE WHEN SCORE = 1 THEN 1 END) AS `1`,
count(CASE WHEN SCORE = 2 THEN 1 END) AS `2`,
count(CASE WHEN SCORE = 3 THEN 1 END) AS `3`,
count(CASE WHEN SCORE = 4 THEN 1 END) AS `4`
from TABLE
group by year
For a set of Four values
SELECT YEAR, SUM(CASE WHEN SCORE = 1 THEN 1 ELSE 0 END) AS `1`,
SUM(CASE WHEN SCORE = 2 THEN 1 ELSE 0 END) AS `2`,
SUM(CASE WHEN SCORE = 3 THEN 1 ELSE 0 END) AS `3`,
SUM(CASE WHEN SCORE = 4 THEN 1 ELSE 0 END) AS `4`
FROM TABLE
GROUP BY YEAR

Need Help on Oracle Select Query

I am finding difficulty to frame a select query.
PFB, for the table and corresponding data:
ID DLS MATCH_STATUS LAST_UPDATE_TIME BO CH FT
1 0 0 09-07-2013 00:00:00 IT TE NA
1 1 1 09-07-2013 00:01:01 IT TE NA
2 0 0 09-07-2013 10:00:00 IP TE NA
3 0 0 09-07-2013 11:00:00 IT YT NA
3 2 2 09-07-2013 11:01:00 IT YT NA
Here
Match_Status 0-->Initial Record
1-->Singel Match
2-->Multi Match
For every record there will be a initial entry with match_status 0 and subsequent matching process end other number such as 1,2 will be update.
I am trying to retrieve records such as total record , waiting match ,single match and multi match group by BO, CH and FT
Below is the expected out put:
BO CH FT TOTAL_RECORD AWAITNG_MATCH SINGLE_MATCH MULTI_MATCH
IT TE NA 1 0 1 0
IP TE NA 1 1 0 0
IT YT NA 1 0 0 2
I have tried below query :
select BO,CH,FT,sum(case when match_status=0 then 1 else 0 end) as TOTAL_RECORD,
sum(case when match_status = 0 then 1 else 0 end) as AWAITING_MATCH,
sum(case when match_status = 1 then 1 else 0 end) as SINGLE_MATCH,
sum(case when match_status = 2 then 1 else 0 end) as MULTI_MATCH from
table1 where last_update_time >= current_timestamp-1
group by BO,CH,FT;
problem with the above query is, awaiting_match is getting populated same as total record as I understand because of match_status=0
Similarly I tried with
select BO,CH,FT,sum(case when match_status=0 then 1 else 0 end) as TOTAL_RECORD,
select (sum(case when t1.ms=0 then 1 else 0 end) from
(select max(match_status) as ms from table1 where last_update_time >= current_timestamp-1 group by id)t1) )awaiting_match,
sum(case when match_status = 1 then 1 else 0 end) as SINGLE_MATCH,
sum(case when match_status = 2 then 1 else 0 end) as MULTI_MATCH from
table1 where last_update_time >= current_timestamp-1
group by BO,CH,FT;
problem with the approach is awaiting_match is getting populated with the same value for subsequent row entry.
Please help me with a suitable query for the desired format.
Thanks a lot in advance.
It seems that you want the last match status. I am guessing that this is actually the maximum of the statuses. If so, the following solves the problem by first grouping on id and then doing the grouping to summarize:
select BO, CH, FT,
count(*) as TOTAL_RECORD,
sum(case when lastms = 0 then 1 else 0 end) as AWAITING_MATCH,
sum(case when lastms = 1 then 1 else 0 end) as SINGLE_MATCH,
sum(case when lastms = 2 then 1 else 0 end) as MULTI_MATCH
from (select id, bo, ch, ft, MAX(match_status) as lastms
from table1
where last_update_time >= current_timestamp-1
group by id, bo, ch, ft
) t
group by BO, CH, FT;
If you actually want the last update to provide the status for the id, then you can use row_number() to enumerate the rows for each id, order by update time descending, and choose the first one:
select BO, CH, FT,
count(*) as TOTAL_RECORD,
sum(case when lastms = 0 then 1 else 0 end) as AWAITING_MATCH,
sum(case when lastms = 1 then 1 else 0 end) as SINGLE_MATCH,
sum(case when lastms = 2 then 1 else 0 end) as MULTI_MATCH
from (select id, bo, ch, ft, match_status,
ROW_NUMBER() over (partition by id order by last_update_time desc) as seqnum
from table1
where last_update_time >= current_timestamp-1
) t
where seqnum = 1
group by BO, CH, FT;