SQL Select with joins and calculate percentage - sql

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

Related

Create a score card report based on match results in 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

How to Replace NULL Value with 0 (Zero)?

I've just got myself stuck with some SQL query and I'm quite new on this.
I'm using pivot in my query.
This is my SELECT query:
SELECT *
FROM
(SELECT lg.domainNameID AS [Domain ID], COUNT(lg.domainNameID) AS [Fix Count]
FROM tbl_ATT_Request r
INNER JOIN tbl_ATT_Login lg ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID) slct
and this is the output:
Domain | Fix Count
-------+-----------
1 1
2 1
4 2
5 1
And this is my query with PIVOT.
SELECT *
FROM
(SELECT lg.domainNameID AS [Domain ID], COUNT(lg.domainNameID) AS [Fix Count]
FROM tbl_ATT_Request r
INNER JOIN tbl_ATT_Login lg ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID) slct
PIVOT
(SUM(slct.[Fix Count])
FOR slct.[Domain ID] IN ([1],[2],[3],[4],[5])
) AS pvt
This is the output:
1 | 2 | 3 | 4 | 5
1 1 NULL 2 1
Now my problem is how can I replace the NULL values with 0.
Just use conditional aggregation:
SELECT SUM(CASE WHEN Domain_Id = 1 THEN Fix_Count ELSE 0 END) as d_1,
SUM(CASE WHEN Domain_Id = 2 THEN Fix_Count ELSE 0 END) as d_2,
SUM(CASE WHEN Domain_Id = 3 THEN Fix_Count ELSE 0 END) as d_3,
SUM(CASE WHEN Domain_Id = 4 THEN Fix_Count ELSE 0 END) as d_4,
SUM(CASE WHEN Domain_Id = 5 THEN Fix_Count ELSE 0 END) as d_5
FROM (SELECT lg.domainNameID AS Domain_ID, COUNT(*) AS Fix_Count
FROM tbl_ATT_Request r JOIN
tbl_ATT_Login lg
ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID
) d

3 Query by column and create a new table

I have students, and these students have their meals, morning and evening. I want to print the number of meals each student eats in the morning and evening.
If the number of dishes that student eats in the morning is more than one, I want to print the number in the table and the ID of the food.
FoodType when 1(morning), and when 2(evening)
StudentId FoodId FoodType
3 1 1
3 2 1
3 3 1
3 4 2
4 3 1
4 1 2
4 2 2
4 4 2
5 4 2
5 1 1
6 1 1
6 2 1
6 3 2
6 4 2
Sample out;
StudentId MorningFoodCountOrId EveningFoodCountOrId
3 3 meals 4
4 3 3 meals
5 4 1
6 2 meals 2 meals
Use conditional aggregation. The logic that decides if we print the number of records or their value is not intuitive, but I would phrase it as follows:
select
studentId,
case when sum(case when foodtype = 1 then 1 else 0 end) = 1
then max(case when foodtype = 1 then foodId end)
else sum(case when foodtype = 1 then 1 else 0 end)
end MorningFoodCountOrId
case when sum(case when foodtype = 2 then 1 else 0 end) = 1
then max(case when foodtype = 2 then foodId end)
else sum(case when foodtype = 2 then 1 else 0 end)
end EveningFoodCountOrId
from mytable
group by studentId
You RDMBS should be able to optimize the query by not computing the conditional sums twice.
Note: you did not specify which RDMBS you are using. If this is MySQL, then it is possible to shorten the conditional sums a little, as follows:
select
studentId,
case when sum(foodtype = 1) = 1
then max(case when foodtype = 1 then foodId end)
else sum(foodtype = 1)
MorningFoodCountOrId
case when sum(foodtype = 2) = 1
then max(case when foodtype = 2 then foodId end)
else sum(foodtype = 2)
EveningFoodCountOrId
from mytable
group by studentId
Here's your query, select sum() and case.. will do this
select t1.studentid
, case when t1.m <= 1 then t2.FoodId else concat(t1.m, ' meals') end MorningFoodCountOrId
, case when t1.e <= 1 then t3.FoodId else concat(t1.e, ' meals') end EveningFoodCountOrId
from(
select studentid
, sum(case when FoodType = 1 then 1 else 0 end) as m
, sum(case when FoodType = 2 then 1 else 0 end) as e
from tableA
group by studentid) t1
left join tableA t2 on t2.studentId = t1.studentId and t1.m = 1 and t2.FoodType = 1
left join tableA t3 on t3.studentId = t1.studentId and t1.e = 1 and t3.FoodType = 2
order by t1.studentid
see dbfiddle
in postgresql
, case when t1.m <= 1 then t2.FoodId::text else concat(t1.m, ' meals') end MorningFoodCountOrId
, case when t1.e <= 1 then t3.FoodId::text else concat(t1.e, ' meals') end EveningFoodCountOrId

Why does this query have two selects?

I have this query :
SELECT WorkId, RegisterDate, sum(RoomType1) As RoomType1, sum(RoomType2) As RoomType2, sum(RoomType3) As RoomType3, sum(RoomType4) As RoomType4, sum(RoomType5) As RoomType5, sum(RoomType6) As RoomType6, sum(RoomType7) As RoomType7, sum(RoomType8) As RoomType8
FROM (
SELECT dbo.[Work].WorkId, dbo.[Work].RegisterDate,
case dbo.Floor.RoomType when 1 then 1 else 0 end as RoomType1,
case dbo.Kat.RoomType when 2 then 1 else 0 end as RoomType2,
FROM dbo.Belediye INNER JOIN
dbo.[Is] ON dbo.Municipality.MunicipalityId= dbo.[Is].MunicipalityWorkId INNER JOIN
dbo.Look ON dbo.[Work].LookWorkId = dbo.Look.LookId ,
WHERE (dbo.Look.LocationIS NOT NULL)
) E
GROUP BY WorkId,
This query works as expected, but I can't understand why it has two selects, why does it need them? Please explain it to me. Thanks.
As you suspected this query dont need two selects and could be rewritten without sub-query:
SELECT i.IsId,
i.KayitTarihi,
SUM(case k.OdaTipi when 1 then 1 else 0 end) as RoomType1,
SUM(case k.OdaTipi when 2 then 1 else 0 end) as RoomType2,
SUM(case k.OdaTipi when 3 then 1 else 0 end) as RoomType3,
SUM(case k.OdaTipi when 4 then 1 else 0 end) as RoomType4,
SUM(case k.OdaTipi when 5 then 1 else 0 end) as RoomType5,
SUM(case k.OdaTipi when 6 then 1 else 0 end) as RoomType6,
SUM(case k.OdaTipi when 7 then 1 else 0 end) as RoomType7,
SUM(case k.OdaTipi when 8 then 1 else 0 end) as RoomType8
FROM dbo.Belediye b
INNER JOIN dbo.[Is] i
ON b.BelediyeId = i.BelediyeIsId
INNER JOIN dbo.YerGorme yg
ON i.YerGormeIsId = yg.YerGormeId
INNER JOIN dbo.Kat k
ON yg.YerGormeId = k.YerGorme_YerGormeId
WHERE yg.Lokasyon IS NOT NULL
GROUP BY i.IsId, i.KayitTarihi
Note: use table aliases

Get Data Of Third Column Based on Two other Columns

This is a sample table
ID STOREA STOREB STOREC AB BC CA ABC
--- ------- ------ ------- -- -- --- ---
10 1 0 0
10 0 1 0
10 0 1 0
29 0 1 0
29 0 0 1
29 1 0 0
Each row corresponds to a purchase made at either of Store A or B or C. Customer 10 shops at A and B but not c. So I want AB=1 BC=0 CA=0 ABC=0 for all ID=10 rows and for ID=29, he shops at all 3, so I need AB=1 BC=1 CA=1 ABC=1 for all rows where ID=29 (using ORACLE SQL)
I would like to update the columns in the table.
Here is one way you can do this. I don't think you can use JOINs in Oracle with UPDATE statements -- however, you can accomplish the same thing by using MERGE:
MERGE
INTO yourtable
USING (
select id as idnew,
case when a + b = 2 then 1 else 0 end abnew,
case when b + c = 2 then 1 else 0 end bcnew,
case when a + c = 2 then 1 else 0 end acnew,
case when a + b + c = 3 then 1 else 0 end abcnew
from (
select
id,
max(case storea when 1 then 1 else 0 end) A,
max(case storeb when 1 then 1 else 0 end) B,
max(case storec when 1 then 1 else 0 end) C
from yourtable
group by id
) a
)
ON (id = idnew)
WHEN MATCHED THEN
UPDATE
SET ab = abnew,
bc = bcnew,
ac = acnew,
abc = abcnew
SQL Fiddle Demo
Here is how you can do this as a select:
update (select id, storea, storeb, storec, AB as new_AB, BC as new_BC, AC as new_AC, ABC as new_ABC
from t join
(select id,
(case when max(storeA) = 1 and max(storeB) = 1 then 1 else 0 end) as AB,
(case when max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as BC,
(case when max(storeA) = 1 and max(storeC) = 1 then 1 else 0 end) as AC,
(case when max(storeA) = 1 and max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as ABC
from t
group by id
) tsum
on t.id = tsum.id
)
set AB = new_AB, AC = new_AC, BC = new_BC, ABC = new_ABC;
I think this might work:
select id, storea, storeb, storec, AB, BC, AC, ABC
from t join
(select id,
(case when max(storeA) = 1 and max(storeB) = 1 then 1 else 0 end) as AB,
(case when max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as BC,
(case when max(storeA) = 1 and max(storeC) = 1 then 1 else 0 end) as AC,
(case when max(storeA) = 1 and max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as ABC
from t
group by id
) tsum
on t.id = tsum.id
)
set AB = new_AB, AC = new_AC, BC = new_BC, ABC = new_ABC;