Help with a SQL query joining multiple tables - sql

First I will explain the case, I have a table tbl_game with a structure as such. This table contains, the time where the game was started and pair playing the game
| id | time | pair_id |
-----------+--------------+ ---------------
1 | 123123123 | 1 |
2 | 123168877 | 1 |
and I have another table tbl_throws which holds the score for each player. In case you are wondering, this a basic dice rolling game
| id | game_id | player_id | score |
-----------+--------------+---------------+---------|
| 1 | 1 | 1 | 2 |
| 2 | 1 | 2 | 5 |
| 3 | 1 | 1 | 9 |
| 4 | 1 | 2 | 11 |
| 5 | 2 | 1 | 7 |
| 6 | 2 | 2 | 6 |
Now, id here is the throw id, not the game id. Here each player with player_id 1 and 2 has throws the dice twice and got the respective score as presented all in same game and just one time in another
Now, using these two table, I need to create a record set, that the total score of each player in one game
| game_id | game_time | player1_total | player2_total|
|------------+-----------+---------------+--------------|
| 1 | 123123123 | 11 | 16 |
| 2 | 123168877 | 7 | 6 |
I tried lots of mumbo jumbo queries, but nothing is giving corrent result?
What is the correct query for this?
Update
Since, most of the answers were bounded by a fact that, player1id and player2id had to be known or fixed.
So may be the information I am about to provide will help to clear the confusion.
there is another table, which holds the information of the player. tbl_pupil
Structure is like the following
| id | unique_id | name |
|---------+---------------+----------|
| 1 | 001 | some |
| 2 | 002 | another |
and these player are collectively called, a pair in another table tbl_pair
| id | player1 | player2 |
|---------+---------------+----------|
| 1 | 1 | 2 |
So, now
select
g.id
g.time
p1.id as player1id
p1.name as player1name
t.score as player1score
p2.id as player2id
p2.name as player2name
t.score as player2score
FROM
tbl_game g,
inner join tbl_pair as pair on g.pair_id = pair.id
inner join tbl_pupil as p1 on p1.id = pair.player1
inner join tbl_pupil as p2 on p2.id = pair.player2
inner join tbl_throw as t on g.id = t.game_id
This is my preliminary query, which brings the record set, on a way as such
| id | time | player1id | player1name | player1score | player2id | player2name | player2score |
----------------------------------------------------------------------------------------------------------------------------
| 1 | 12 | 1 | some | 5 | 2 | another | 2 |
| 1 | 12 | 1 | some | 5 | 2 | another | 5 |
| 1 | 12 | 1 | some | 9 | 2 | another | 9 |
| 1 | 12 | 1 | some | 11 | 2 | another | 11 |
Now I am just showing the results of one game id by the way. I don't save sufficient knowledge, to group the above record into one, with player1 separate sum score in one column and playe2's separate sum of score in another column.

Try this:
SELECT
tbl_game.id AS game_id,
tbl_game.time AS game_time,
SUM(CASE WHEN player_id = 1 THEN score ELSE 0 END) AS player1_total,
SUM(CASE WHEN player_id = 2 THEN score ELSE 0 END) AS player2_total
FROM tbl_game JOIN tbl_thorws ON tbl_game.id = tbl_throws.game_id
GROUP BY tbl_game.id

This is similar to some of the other answers, but crucially doesn't depend on the player IDs being 1 and 2.
select
game_id = g.id,
game_time = g.time,
player1_total = SUM(case t.player_id when p.player1_id then t.score else 0 end),
player2_total = SUM(case t.player_id when p.player2_id then t.score else 0 end)
from
tbl_game g
join tbl_throws t on g.id = t.game_id
join ( --Get the player IDs for this game
select
game_id,
player1_id = MIN(player_id),
player2_id = MAX(player_id)
from
tbl_throws
group by game_id
) p
on p.game_id = t.game_id
group by
g.id, g.time
Just for fun I've generalized the above out to allow more > 2 players:
The 2 CTE tables just show the test data I'm using
;WITH tbl_game as (
select ID = 1, time = 123123123, pair_id = 1
union select ID = 2, time = 123168877, pair_id = 1
),
tbl_throws as (
select id = 1, game_id = 1, player_id = 1, score = 2
union select id = 2, game_id = 1, player_id = 2, score = 5
union select id = 2, game_id = 1, player_id = 3, score = 5
union select id = 3, game_id = 1, player_id = 1, score = 9
union select id = 4, game_id = 1, player_id = 2, score = 11
union select id = 5, game_id = 2, player_id = 1, score = 7
union select id = 6, game_id = 2, player_id = 2, score = 6
)
select
game_id = g.id,
game_time = g.time,
player1_id = MAX(case x.player_no when 1 then t.player_id else 0 end),
player1_total = SUM(case x.player_no when 1 then t.score else 0 end),
player1_id = MAX(case x.player_no when 2 then t.player_id else 0 end),
player2_total = SUM(case x.player_no when 2 then t.score else 0 end),
player3_id = MAX(case x.player_no when 3 then t.player_id else 0 end),
player3_total = SUM(case x.player_no when 3 then t.score else 0 end),
player4_id = MAX(case x.player_no when 4 then t.player_id else 0 end),
player4_total = SUM(case x.player_no when 4 then t.score else 0 end)
/* Add more rows for the number of players permitted in a single game */
from
tbl_game g
join tbl_throws t on g.id = t.game_id
cross apply (
select player_no = COUNT(distinct player_id)
from tbl_throws sub
where sub.player_id <= t.player_id
and Sub.game_id = t.game_id
) x
group by
g.id, g.time

You need to inner join the two tables, and aggregate your scores. To do the basic pivot you are after I used a CASE statement to aggregate by player.
SELECT G.Id as Game_Id,
G.time as Game_Time,
SUM(CASE WHEN t.Player_id = 1 THEN t.score ELSE 0 END) as Player1_total,
SUM(CASE WHEN t.Player_id = 2 THEN t.score ELSE 0 END) as Player2_total
FROM tbl_game G
INNER JOIN tbl_throws T
ON g.id = t.game_id
GROUP BY g.ID, g.time

I think this should do pretty much what you want to do.
SELECT
tbl_game.id as game_id,
tbl_game.time as game_time,
SUM(player1.score) as player1_total,
SUM(player2.score) as player2_total
FROM tbl_game
INNER JOIN tbl_throws player1 ON player1.game_id = tbl_game.id AND player1.player_id = 1
INNER JOIN tbl_throws player2 ON player2.game_id = tbl_game.id AND player2.player_id = 2
GROUP BY tbl_game.id, tbl_game.time

SELECT t.game_id
, t.game_time
, ( SELECT SUM(t.score)
FROM tbl_throws AS t
WHERE t.game_id = g.id
AND player_id = 1
) AS player1_total
, ( SELECT SUM(t.score)
FROM tbl_throws AS t
WHERE t.game_id = g.id
AND player_id = 2
) AS player2_total
FROM tbl_game AS g

Related

SQL Server 2014 change multiple string rows to columns

I have below data and it will have multiple titles against multiple genres. Every genre has 7 top titles.
Genre | MarketingTitle
------+----------------
Drama | Drama 1
Drama | Drama 2
...
Drama | Drama 7
I want output to look something like
Genre | Title 1 | Title 2 | Title 3 | ... | Title7
-------+---------+---------+---------+-----+---------
Drama | Drama1 | Drama2 | Drama3 | ... | Drama7
Comedy | Comedy1 | Comedy2 | Comedy3 | ... | Comedy7
I tried pivot table but its just not working
Select
GenreName, [Drama], [Comedy]
from
(select
g.name as GenreName, p.MarketingTitle as MarketingTitle
from
programme p
inner join
Genre g on g.Id = p.GenreId
where
topTitle = 1) c
pivot
(max(MarketingTitle)
for MarketingTitle in ([Drama], [Comedy])
) As pvt
Everything is returned as null and I am pretty sure this query is wrong.
Even below output is desirable but I cant seem to make query work. any help is appreciated.
Drama | Comedy | ... | otherGenres
--------+---------+-----+------------
drama1 | comedy1 | ... |
drama2 | comedy2 | ... |
.. .
drama7 | comedy7 | ... |
Try conditional aggregation instead
select
GenreName, Title1 = max(case when rn = 1 then MarketingTitle end)
, Title2 = max(case when rn = 2 then MarketingTitle end)
, Title3 = max(case when rn = 3 then MarketingTitle end)
, Title4 = max(case when rn = 4 then MarketingTitle end)
, Title5 = max(case when rn = 5 then MarketingTitle end)
, Title6 = max(case when rn = 6 then MarketingTitle end)
, Title7 = max(case when rn = 7 then MarketingTitle end)
from (
select g.name as GenreName, p.MarketingTitle as MarketingTitle
, rn = row_number() over (partition by g.name order by p.MarketingTitle)
from programme p
inner join Genre g on g.Id = p.GenreId
where top = 1
) t
group by GenreName

Can't display matches won, matches played and matches drawed by each player

CREATE TABLE matches (
match_id BIGSERIAL PRIMARY KEY,
tournamentid INTEGER,
player1_id INTEGER,
player2_id INTEGER CHECK (player1_id < player2_id),
result INTEGER CHECK (result IN (0, 1, 2)),
FOREIGN KEY(tournamentid, player1_id) REFERENCES enroll(tournament, player_id),
FOREIGN KEY(tournamentid, player2_id) REFERENCES enroll(tournament, player_id),
UNIQUE(tournamentid, player1_id, player2_id)
);
CREATE VIEW player_standings AS (
SELECT tournaments.tournament_id,
tournaments.tournament_name,
enroll.player_id,
players.name,
CASE
WHEN matches.result = 1 THEN COUNT(matches.player1_id)
WHEN matches.result = 2 THEN COUNT(matches.player2_id)
END AS wins,
COUNT(enroll.player_id IN (matches.player1_id, matches.player2_id)) AS match_played
FROM players
INNER JOIN enroll ON enroll.player_id = players.id
INNER JOIN tournaments ON tournaments.tournament_id = enroll.tournament
LEFT JOIN matches ON (matches.player1_id = enroll.player_id) or
(matches.player2_id = enroll.player_id)
GROUP BY tournaments.tournament_id, tournaments.tournament_name,
enroll.player_id, players.name, matches.result
ORDER BY tournaments.tournament_id, wins DESC
);
I can't seem to get the number of matches won correctly. I think the problem has to do with the left join with the matches table.
The idea is to read from the column result from the matches table where the results are interpreted as:
0 = draw
1 = player1 won
2 = player2 won
With the current schema, I can get the number of matches played for each players correctly but not the number of matches played.
I'm also hoping to add the number of tied matches without having to break up into multiple views or tables, if possible. any advice?
Since you didn't include all of your schema I made some educated guess and came up with this view that I think should work. I included some extra counts for losses and draws as well as I found it easier to verify the result with all results present in the view.
CREATE VIEW player_standings AS (
SELECT
tournaments.tournament_id as t_id
, tournaments.tournament_name
, enroll.player_id
, players.name
, COUNT(
CASE
WHEN enroll.player_id = matches.player1_id AND matches.result = 1 THEN 1
WHEN enroll.player_id = matches.player2_id AND matches.result = 2 THEN 1
END) AS wins
, COUNT(
CASE
WHEN enroll.player_id = matches.player1_id AND matches.result = 2 THEN 1
WHEN enroll.player_id = matches.player2_id AND matches.result = 1 THEN 1
END) AS losses
, COUNT(CASE WHEN matches.result = 0 THEN 1 END) AS draws
, COUNT(match_id) AS matches_played
FROM players
INNER JOIN enroll ON enroll.player_id = players.id
INNER JOIN tournaments ON tournaments.tournament_id = enroll.tournament
LEFT JOIN matches ON matches.tournamentid = tournaments.tournament_id
AND enroll.player_id IN (matches.player1_id, matches.player2_id)
GROUP BY
tournaments.tournament_id,
tournaments.tournament_name,
enroll.player_id,
players.name
ORDER BY
tournaments.tournament_id,
wins DESC,
matches_played DESC
);
Here is the markdown output from the SQL Fiddle I created:
SQL Fiddle
PostgreSQL 9.3 Schema Setup:
create table players (
id int primary key,
name varchar(20)
);
insert into players values
(1, 'Player 1'),(2, 'Player 2'),
(3, 'Player 3'),(4, 'Player 4'),(5, 'Player 5');
create table tournaments (
tournament_id int primary key,
tournament_name varchar(20)
);
insert into tournaments values (1, 'Tournament 1'),(2, 'Tournament 2');
create table enroll (
tournament int,
player_id int,
primary key (tournament, player_id),
foreign key (tournament) references tournaments(tournament_id),
foreign key (player_id) references players(id)
);
insert into enroll values
(1,1),(1,2),(1,3),(1,4),(1,5),
(2,1),(2,2),(2,3),(2,4),(2,5);
CREATE TABLE matches (
match_id bigserial PRIMARY KEY,
tournamentid INTEGER,
player1_id INTEGER,
player2_id INTEGER CHECK (player1_id < player2_id),
result INTEGER CHECK (result IN (0, 1, 2)),
FOREIGN KEY(tournamentid, player1_id) REFERENCES enroll(tournament, player_id),
FOREIGN KEY(tournamentid, player2_id) REFERENCES enroll(tournament, player_id),
UNIQUE(tournamentid, player1_id, player2_id)
);
insert into matches (tournamentid, player1_id, player2_id, result) values
(1, 1, 2, 1) -- 1 win 2 loss
,(1, 1, 3, 1) -- 1 win 3 loss
,(1, 2, 3, 2) -- 2 win 2 loss
,(1, 1, 5, 1) -- 1 win 5 loss
,(2, 2, 4, 0) -- 2 draw 4 draw
,(2, 1, 2, 1) -- 1 win 2 loss
,(2, 3, 4, 2) -- 4 win 3 loss
;
CREATE VIEW player_standings AS (
SELECT
tournaments.tournament_id as t_id
, tournaments.tournament_name
, enroll.player_id
, players.name
, COUNT(
CASE
WHEN enroll.player_id = matches.player1_id AND matches.result = 1 THEN 1
WHEN enroll.player_id = matches.player2_id AND matches.result = 2 THEN 1
END) AS wins
, COUNT(
CASE
WHEN enroll.player_id = matches.player1_id AND matches.result = 2 THEN 1
WHEN enroll.player_id = matches.player2_id AND matches.result = 1 THEN 1
END) AS losses
, COUNT(CASE WHEN matches.result = 0 THEN 1 END) AS draws
, COUNT(match_id) AS matches_played
FROM players
INNER JOIN enroll ON enroll.player_id = players.id
INNER JOIN tournaments ON tournaments.tournament_id = enroll.tournament
LEFT JOIN matches ON matches.tournamentid = tournaments.tournament_id
AND enroll.player_id IN (matches.player1_id, matches.player2_id)
GROUP BY
tournaments.tournament_id,
tournaments.tournament_name,
enroll.player_id,
players.name
ORDER BY
tournaments.tournament_id,
wins DESC,
matches_played DESC
);
Query 1:
select * from player_standings
Results:
| t_id | tournament_name | player_id | name | wins | losses | draws | matches_played |
|------|-----------------|-----------|----------|------|--------|-------|----------------|
| 1 | Tournament 1 | 1 | Player 1 | 3 | 0 | 0 | 3 |
| 1 | Tournament 1 | 3 | Player 3 | 1 | 1 | 0 | 2 |
| 1 | Tournament 1 | 2 | Player 2 | 0 | 2 | 0 | 2 |
| 1 | Tournament 1 | 5 | Player 5 | 0 | 1 | 0 | 1 |
| 1 | Tournament 1 | 4 | Player 4 | 0 | 0 | 0 | 0 |
| 2 | Tournament 2 | 4 | Player 4 | 1 | 0 | 1 | 2 |
| 2 | Tournament 2 | 1 | Player 1 | 1 | 0 | 0 | 1 |
| 2 | Tournament 2 | 2 | Player 2 | 0 | 1 | 1 | 2 |
| 2 | Tournament 2 | 3 | Player 3 | 0 | 1 | 0 | 1 |
| 2 | Tournament 2 | 5 | Player 5 | 0 | 0 | 0 | 0 |

row counter with condition in two different columns

I have the following tables with sport results (e.g. football):
tblGoals (RowId, GameRowIdm PlayerRowId, TeamRowId, GoalMinute)
RowId | GameRowId | PlayerRowId | TeamRowId | GoalMinute
--------------------------------------------------------
1 | 1 | 1 | 1 | 25
2 | 1 | 2 | 2 | 45
3 | 1 | 3 | 1 | 66
tblPlayers (RowId, PlayerName)
RowId | PlayerName
------------------
1 | John Snow
2 | Frank Underwood
3 | Jack Bauer
tblGames (RowId, TeamHomeRowId, TeamGuestRowId)
RowId | TeamHomeRowId | TeamGuestRowId | GameDate
---------------------------------------------------
1 | 1 | 2 | 2015-01-01
Now I want get a list of all goals. The list should look like this:
GoalMinute | PlayerName | GoalsHome | GoalsGuest
-----------------------------------------------------
25 | John Snow | 1 | 0
45 | Frank Underwood | 1 | 1
66 | Jack Bauer | 2 | 1
GoalsHome and GoalsGuest should be a counter of the shot goals for the team. So e.g. if you check the last row, the result is 2:1 for home team.
To get this list of goals, I used this statement:
SELECT t_gol.GoalMinute,
t_ply.PlayerName,
CASE WHEN
t_gol.TeamRowId = t_gam.TeamHomeRowId
THEN ROW_NUMBER() OVER (PARTITION BY t_gam.TeamHomeRowId ORDER BY t_gam.TeamHomeRowId)
END AS GoalsHome,
CASE WHEN
t_gol.TeamRowId = t_gam.TeamGuestRowId
THEN ROW_NUMBER() OVER (PARTITION BY t_gam.TeamGuestRowId ORDER BY t_gam.TeamGuestRowId)
END AS GoalsGuest
FROM dbo.tblGoalsFussball AS t_gol
LEFT JOIN dbo.tblPlayersFussball AS t_ply ON (t_ply.RowId = t_gol.PlayerRowId)
LEFT JOIN dbo.tblGames AS t_gam ON (t_gam.RowId = t_gol.GameRowId)
WHERE t_gol.GameRowId = #match_row
But what I get is this here:
GoalMinute | PlayerName | GoalsHome | GoalsGuest
-----------------------------------------------------
25 | John Snow | 1 | NULL
45 | Frank Underwood | NULL | 2
66 | Jack Bauer | 3 | NULL
Maybe ROW_NUMBER() is the wrong approach?
I would do the running total using sum() as a windowed aggregate function with the over ... clause, which works in SQL Server 2012+.
select
g.RowId, g.GameDate, t.GoalMinute, p.PlayerName,
GoalsHome = COALESCE(SUM(case when TeamRowId = g.TeamHomeRowId then 1 end) OVER (PARTITION BY gamerowid ORDER BY goalminute),0),
GoalsGuest = COALESCE(SUM(case when TeamRowId = g.TeamGuestRowId then 1 end) OVER (PARTITION BY gamerowid ORDER BY goalminute),0)
from tblGoals t
join tblPlayers p on t.PlayerRowId = p.RowId
join tblGames g on t.GameRowId = g.RowId
order by t.GameRowId, t.GoalMinute
Another approach (that also works in older versions) is to use a self-join and sum up the rows with lower goalminutes. For ease of reading I've used a common table expression to split the goals into two columns for home and guest team:
;with t as (
select
g.GoalMinute, g.PlayerRowId, g.GameRowId,
case when TeamRowId = ga.TeamHomeRowId then 1 end HomeGoals,
case when TeamRowId = ga.TeamGuestRowId then 1 end GuestGoals
from tblGoals g
join tblGames ga on g.GameRowId = ga.RowId
)
select
g.RowId, g.GameDate, t.GoalMinute, p.PlayerName,
GoalsHome = (select sum(coalesce(HomeGoals,0)) from t t2 where t2.GoalMinute <= t.GoalMinute and t2.GameRowId = t.GameRowId),
GoalsGuest = (select sum(coalesce(GuestGoals,0)) from t t2 where t2.GoalMinute <= t.GoalMinute and t2.GameRowId = t.GameRowId)
from t
join tblPlayers p on t.PlayerRowId = p.RowId
join tblGames g on t.GameRowId = g.RowId
order by t.GameRowId, t.GoalMinute
The CTE isn't necessary though, you could just as well use a derived table
Sample SQL Fiddle
I think the easiest way is with subqueries..
SELECT
tgs.GoalMinute,
tpl.PlayerName,
( SELECT
COUNT(t.RowId)
FROM
tblgoals AS t
WHERE t.GoalMinute <= tgs.GoalMinute
AND t.GameRowId = tgm.RowId
AND t.TeamRowId = tgm.TeamHomeRowId
) AS HomeGoals,
( SELECT
COUNT(t.RowId)
FROM
tblgoals AS t
WHERE t.GoalMinute <= tgs.GoalMinute
AND t.GameRowId = tgm.RowId
AND t.TeamRowId = tgm.TeamGuestRowId
) AS GuestGoals
FROM
tblgoals AS tgs
JOIN tblplayers AS tpl ON tgs.RowId = tpl.RowId
JOIN tblGames AS tgm ON tgm.RowId = tgs.GameRowId
ORDER BY tgs.GoalMinute

Select data from three table in sql

I have three table Like
Student : Sid, SName, SEmail
Fees_Type : Fid, FName, FPrice
StudentFees : Sid(FK from Student),Fid(FK from Fees_Type), FDate
Data of Each Table :
Student :
SID |SName | SEmail
1 | ABC | ABC#www.com
2 | XYZ | xyz#www.com
Fees_Type:
Fid | FName | FPrice
1 | Chess | 100
2 | Cricket | 200
StudentFees:
Sid | Fid| FDate
1 | 1 | 5/2
1 | 2 | 6/2
2 | 1 | 7/2
2 | 2 | 8/2
1 | 1 | 6/2
Now I want to Get data Like
SID|SName|SEmail | Total_Chess_Played|Total_Cricket_Played | ToTal_Fees
1 | ABC |ABC#www.com | 2 | 1 | 400
2 | XYZ |xyz#www.com | 1 | 1 | 300
I have tried these following query but can not get Group by or perfect result
SELECT s.sId, SEmail, SName, FName ,FPrice
FROM Student s
INNER JOIN StudentFees sf ON s.sId = sf.EId
INNER JOIN Fees_Type f ON f.fId = sf.fId
WHERE MONTH(pr.TDDate) = MONTH(dateadd(dd, 1, GetDate())) AND
YEAR(pr.TDDate) = YEAR(dateadd(dd, -1, GetDate()))
I am new in SQL. So Please Help Me.
Thank You.
You could do something like this:
SELECT
Student.SID,
Student.SName,
Student.SEmail,
SUM(CASE WHEN Fees_Type.FName='Chess' THEN 1 ELSE 0 END) AS Total_Chess_Played,
SUM(CASE WHEN Fees_Type.FName='Cricket' THEN 1 ELSE 0 END) AS Total_Cricket_Played,
SUM(Fees_Type.FPrice) AS ToTal_Fees
FROM
Student
JOIN StudentFees ON Student.sId = StudentFees.EId
JOIN Fees_Type ON Fees_Type.fId = StudentFees.fId
WHERE
MONTH(StudentFees.TDDate) = MONTH(dateadd(dd, 1, GetDate())) AND
YEAR(StudentFees.TDDate) = YEAR(dateadd(dd, -1, GetDate()))
GROUP BY
Student.SID,
Student.SName,
Student.SEmail
try this
SELECT
s.SID,
s.SName,
s.SEmail,
SUM(CASE WHEN ft.FName='Chess' THEN 1 ELSE 0 END) AS Total_Chess_Played,
SUM(CASE WHEN ft.FName='Cricket' THEN 1 ELSE 0 END) AS Total_Cricket_Played,
SUM(ft.FPrice) AS ToTal_Fees
FROM
Student s
JOIN StudentFees sf ON s.sId = sf.Sid
JOIN Fees_Type ft ON ft.fId = sf.fId
GROUP BY
s.SID,
s.SName,
s.SEmail

SQL query: self join on subquery necessitates creating a separate (non-temporary) table?

I'm working on what is for me a complicated query, and I've managed to get the information I need, but seem to be forced to create a table to accomplish it. I'm using MySQL, so I can't use WITH, I can't use a view because my SELECT contains a subquery in the FROM clause, and I can't use a temporary table because I need to self-join. Am I missing something?
Background:
a reservation can have 1 or more reservation_detail (foreign key rel'p on reservation_id)
a reservation_detail has a quantity and a ticket_type (foreign key rel'p on ticket_type)
Here's the first part of my current solution:
CREATE TABLE
tmp
SELECT
t.reservation_id,
t.ticket_type,
COALESCE(rd.quantity,0) AS qty
FROM (
SELECT *
FROM
(ticket_type tt, reservation r)
) t
LEFT JOIN
reservation_detail rd
ON
t.reservation_id = rd.reservation_id
AND
t.ticket_type = rd.ticket_type;
This gives me a table that looks like the following, where for each combination of a reservation_id and a ticket_type, I have a qty.
+----------------+-------------+------+
| reservation_id | ticket_type | qty |
+----------------+-------------+------+
| 1 | ADULT | 2 |
| 1 | CHILD | 2 |
| 1 | INFANT | 0 |
| 2 | ADULT | 1 |
| 2 | CHILD | 0 |
| 2 | INFANT | 0 |
| 3 | ADULT | 1 |
| 3 | CHILD | 0 |
| 3 | INFANT | 0 |
+----------------+-------------+------+
Now I can self join thrice on this table to get what I'm really looking for...
SELECT
t1.reservation_id,
t1.qty AS num_adults,
t2.qty AS num_children,
t3.qty AS num_infants
FROM
tmp t1
LEFT JOIN
tmp t2
ON
t1.reservation_id = t2.reservation_id
LEFT JOIN
tmp t3
ON
t2.reservation_id = t3.reservation_id
WHERE
t1.ticket_type = 'ADULT'
AND
t2.ticket_type = 'CHILD'
AND
t3.ticket_type = 'INFANT';
...which is one row for each reservation showing the qty for each of the three ticket types.
+----------------+------------+--------------+-------------+
| reservation_id | num_adults | num_children | num_infants |
+----------------+------------+--------------+-------------+
| 1 | 2 | 2 | 0 |
| 2 | 1 | 0 | 0 |
| 3 | 1 | 0 | 0 |
+----------------+------------+--------------+-------------+
I hope this is enough information. Please leave a comment if it's not.
If your query is considering only these 3 types: ADULT, CHILD, INFANT; you don't have to use table ticket_type.
SELECT
r.reservation_id,
COALESCE(rd_adult.quantity,0) AS num_adults,
COALESCE(rd_child.quantity,0) AS num_children,
COALESCE(rd_infant.quantity,0) AS num_infants
FROM
reservation r
LEFT JOIN
reservation_detail rd_adult
ON r.reservation_id = rd_adult.reservation_id
and rd_adult.ticket_type = 'ADULT'
LEFT JOIN
reservation_detail rd_child
ON r.reservation_id = rd_child.reservation_id
and rd_child.ticket_type = 'CHILD'
LEFT JOIN
reservation_detail rd_infant
ON r.reservation_id = rd_infant.reservation_id
and rd_infant.ticket_type = 'INFANT'
Since table reservation_detail contains all the fields you need, you don't need to join the other tables and create a temp table.
Try this:
SELECT distinct
t.reservation_id,
COALESCE(t1.qty,0) AS num_adults,
COALESCE(t2.qty,0) AS num_children,
COALESCE(t3.qty,0) AS num_infants
FROM reservation t
LEFT JOIN reservation_detail t1 ON t.reservation_id = t1.reservation_id AND t1.ticket_type = 'ADULT'
LEFT JOIN reservation_detail t2 ON t.reservation_id = t2.reservation_id AND t2.ticket_type = 'CHILD'
LEFT JOIN reservation_detail t3 ON t.reservation_id = t3.reservation_id AND t3.ticket_type = 'INFANT';
If you want to stick with your first query, you can sub this for the 2nd:
SELECT reservation_id,
SUM(CASE WHEN ticket_type='ADULT' THEN qty ELSE 0 END) AS adults,
SUM(CASE WHEN ticket_type='CHILD' THEN qty ELSE 0 END) AS children,
SUM(CASE WHEN ticket_type='INFANT' THEN qty ELSE 0 END) AS infants,
FROM tmp
GROUP BY reservation_id;
However, I'm wondering a bit about your schema. You are storing qty, a calculated value. Have you considered just having a row for each ticket instance. If you do that then no tmp table is required, though you'd do the pivot similarly to the above.
a simple GROUP BY should be OK:
SELECT t.reservation_id,
SUM(CASE
WHEN ticket_type = 'ADULT' THEN
COALESCE(rd.quantity, 0)
ELSE
0
END) num_adults,
SUM(CASE
WHEN ticket_type = 'CHILD' THEN
COALESCE(rd.quantity, 0)
ELSE
0
END) num_children,
SUM(CASE
WHEN ticket_type = 'INFANT' THEN
COALESCE(rd.quantity, 0)
ELSE
0
END) num_infants
FROM (SELECT * FROM (ticket_type tt, reservation r)) t
LEFT JOIN reservation_detail rd ON t.reservation_id = rd.reservation_id
AND t.ticket_type = rd.ticket_type
GROUP BY t.reservation_id