Query to show old and new values from another table - sql

I have 3 tables in SQL Server 2012.
Table History is the history of all the changes made to the values in Table A. It can have many changes done to PRICE, LOT, INTEREST, but in most cases the value is only changed once.
Table A
AID PRICE LOT INTEREST
------------------------
1 1500 10 0.5
2 2500 20 1.5
Table B
BID AID
--------
11 1
22 2
Table History.
BID ChangeField OldValue NewValue ChangeDate
------------------------------------------------------------
11 PRICE 1700 1500 1/1/22
11 LOT 15 10 12/15/21
11 update_flag M 1/1/22
I need a query that shows Table A with the old and new values from Table History. If there are more than 1 changes, then for the Old value, get the most recent previous value.
Example:
AID OldPRICE NewPRICE OldLot NewLot OldInterest NewInterest
----------------------------------------------------------------
1 1700 1500 15 10 0.5 0.5
2 2500 2500 20 20 1.5 1.5
How can I do that ?
Thank you.

First you need to join all three tables and aggregate per AID. Once you have that, the query needs to selectively pick the values from the history should they exist.
For example:
select
a.aid,
max(case when h.changefield = 'PRICE' then coalesce(h.oldvalue, a.price) end) as oldprice,
max(case when h.changefield = 'PRICE' then coalesce(h.newvalue, a.price) end) as newprice,
max(case when h.changefield = 'LOT' then coalesce(h.oldvalue, a.lot) end) as oldlot,
max(case when h.changefield = 'LOT' then coalesce(h.newvalue, a.lot) end) as newlot,
max(case when h.changefield = 'INTEREST' then coalesce(h.oldvalue, a.interest) end) as oldinterest,
max(case when h.changefield = 'INTEREST' then coalesce(h.newvalue, a.interest) end) as newinterest
from table_a a
left join table_b b on b.aid = a.aid
left join history h on h.bid = b.bid
group by a.aid

You can try to use OUTER JOIN with the condition aggregate function.
Query 1:
SELECT A.AID,
MAX(ISNULL(CASE WHEN h.ChangeField = 'PRICE' THEN h.OldValue END,A.PRICE)) OldPRICE,
MAX(ISNULL(CASE WHEN h.ChangeField = 'PRICE' THEN h.NewValue END,A.PRICE)) NewPRICE,
MAX(ISNULL(CASE WHEN h.ChangeField = 'LOT' THEN h.OldValue END,A.LOT)) OldLot,
MAX(ISNULL(CASE WHEN h.ChangeField = 'LOT' THEN h.NewValue END,A.LOT)) NewLot,
MAX(ISNULL(CASE WHEN h.ChangeField = 'INTEREST' THEN h.OldValue END,A.INTEREST)) OldInterest,
MAX(ISNULL(CASE WHEN h.ChangeField = 'INTEREST' THEN h.NewValue END,A.INTEREST)) NewInterest
FROM A
LEFT JOIN B ON A.AID = B.AID
LEFT JOIN History h ON B.BID = h.BID
GROUP BY A.AID
Results:
| AID | OldPRICE | NewPRICE | OldLot | NewLot | OldInterest | NewInterest |
|-----|----------|----------|--------|--------|-------------|-------------|
| 1 | 1700 | 1500 | 15 | 10 | 0.5 | 0.5 |
| 2 | 2500 | 2500 | 20 | 20 | 1.5 | 1.5 |

Related

new vs old customers sql

I am trying to get new customer's vs returning customers and for this I have to create multiple tables. Is there a better way to aggregate the data shown like below:
my SQL code looks like below:
---- ALL INDIVIDUALS WHO PURCHASED IN CURRENT WEEK---------
CREATE TABLE PURCHASES_FEB_WK2 AS (Select DISTINCT INDIVIDUAL_ID
from DM_OWNER.TRANSACTION_DETAIL_MV
WHERE BRAND_ORG_CODE = 'BRAND'
and is_merch = 1
and currency_code = 'USD'
AND LINE_ITEM_AMT_TYPE_CD = 'S'
AND TRUNC(TXN_DATE) BETWEEN '10-FEB-19' AND '16-FEB-19')
----------MINIMUM PURCHASE DATE OF ALL CUSTOMERS------------
Create table feb_wk2_min as
Select distinct Individual_ID, MIN(TRANSACTION_DATE) as FIRST_TRANSACTION
from dm_owner.transaction_mv
WHERE BRAND_ORG_CODE = 'BRAND'
and transaction_type_code in ('PR','EP')
group by individual_ID;
------- NEW CUSTOMERS FOR THE WEEK---------
Select Count(distinct B.INDIVIDUAL_ID)
from PURCHASES_FEB_WK2 A
JOIN FEB_WK2_MIN B ON A.INDIVIDUAL_ID = B.INDIVIDUAL_ID
where FIRST_TRANSACTION between '10-FEB-19' and '16-FEB-19'
---- ALL RETURNING CUSTOMERS
SELECT COUNT (DISTINCT INDIVIDUAL_ID)
FROM PURCHASES_FEB_WK2
WHERE INDIVIDUAL_ID IN (SELECT INDIVIDUAL_ID FROM DM_OWNER.TRANSACTION_DETAIL_MV WHERE TRUNC(TXN_DATE) < '10-FEB-19' AND BRAND_ORG_CODE = 'BRAND' AND IS_MERCH = 1 AND line_item_amt_type_cd = 'S' AND STATUS = 'A')
-------NEW CUSTOMERS DOLLAR_VALUE_US------
SELECT SUM(DOLLAR_VALUE_US) FROM DM_OWNER.TRANSACTION_DETAIL_MV
WHERE INDIVIDUAL_ID IN (Select distinct B.INDIVIDUAL_ID
from PURCHASES_FEB_WK2 A
JOIN FEB_WK2_MIN B ON A.INDIVIDUAL_ID = B.INDIVIDUAL_ID
where FIRST_TRANSACTION between '10-FEB-19' and '16-FEB-19')
AND BRAND_ORG_CODE = 'BRAND'
and is_merch = 1
and currency_code = 'USD'
AND LINE_ITEM_AMT_TYPE_CD = 'S'
AND TRUNC(TXN_DATE) BETWEEN '10-FEB-19' AND '16-FEB-19'
-------RETURNING CUSTOMERS DOLLAR_VALUE_US------
SELECT SUM(DOLLAR_VALUE_US) FROM DM_OWNER.TRANSACTION_DETAIL_MV
WHERE INDIVIDUAL_ID IN (SELECT DISTINCT INDIVIDUAL_ID
FROM PURCHASES_FEB_WK2
WHERE INDIVIDUAL_ID IN (SELECT INDIVIDUAL_ID FROM DM_OWNER.TRANSACTION_DETAIL_MV WHERE TRUNC(TXN_DATE) < '10-FEB-19' AND BRAND_ORG_CODE = 'BRAND' AND IS_MERCH = 1 AND line_item_amt_type_cd = 'S' AND STATUS = 'A'))
AND BRAND_ORG_CODE = 'BRAND'
and is_merch = 1
and currency_code = 'USD'
AND LINE_ITEM_AMT_TYPE_CD = 'S'
AND TRUNC(TXN_DATE) BETWEEN '10-FEB-19' AND '16-FEB-19'
To get the quantity and the count of order, I am replacing the sum (dollar_value_us) with count of distinct orders and sum of quantity. Is there an easy way to pivot and combine this code so that I can just copy paste the data in the format (picture attached) I have provided.
Based on the comments, I understand that you want to split the customers into two groups : customers that had their first transactions during the period should be separated from thoses who had transactions before. For each group, you want to count the number of customers and sum the value of the transactions.
NB : your sql code does not show hot to compute qty and count_of_orders, so I left it apart (but this will likely follow the same logic).
Given this sample data:
INDIVIDUAL_ID | DOLLAR_VALUE_US | TXN_DATE | RAND_ORG_CODE | IS_MERCH | CURRENCY_CODE | LINE_ITEM_AMT_TYPE_CD
------------: | --------------: | :-------- | :------------ | -------: | :------------ | :--------------------
1 | 10 | 01-FEB-19 | BRAND | 1 | USD | S
1 | 10 | 10-FEB-19 | BRAND | 1 | USD | S
1 | 10 | 15-FEB-19 | BRAND | 1 | USD | S
1 | 10 | 28-FEB-19 | BRAND | 1 | USD | S
2 | 11 | 11-FEB-19 | BRAND | 1 | USD | S
2 | 11 | 12-FEB-19 | BRAND | 1 | USD | S
3 | 11 | 12-FEB-19 | BRAND | 1 | USD | S
Considering week range from February 10th to 16th included, customer 1 is a returning customer with 2 transactions in the window, and customers 2 and 3 are new customers with respectively 2 and 1 transactions. You would expect the following output:
TYPE_OF_CUSTOMER | COUNT_OF_CUSTOMERS | SUM_DOLLAR_VALUE_US
:------------------ | -----------------: | ------------------:
New Customers | 2 | 33
Returning Customers | 1 | 20
To solve this, you need to set up several levels of aggregation. First, use window function MIN() OVER() to recover the date of the first transaction of each customer. Then, filter on the anlaysis period, split customers into new/returning groups, and aggregate the money spent. Finally, aggregate all results together.
Query:
SELECT
DECODE(is_new, 1, 'New Customers', 'Returning Customers') type_of_customer,
COUNT(individual_id) count_of_customers,
SUM(dollar_value_us) sum_dollar_value_us
FROM (
SELECT
individual_id,
SUM(dollar_value_us) dollar_value_us,
CASE WHEN MIN(txn_date) = min_txn_date THEN 1 ELSE 0 END is_new
FROM (
SELECT
individual_id,
dollar_value_us,
txn_date,
MIN(txn_date) OVER(PARTITION BY individual_id) min_txn_date
FROM transaction_detail_mv
WHERE
rand_org_code = 'BRAND'
AND is_merch = 1
AND currency_code = 'USD'
AND line_item_amt_type_cd = 'S'
) t
WHERE
txn_date >= TO_DATE('10-02-2019', 'DD-MM-YYYY')
AND txn_date < TO_DATE('17-02-2019', 'DD-MM-YYYY')
GROUP BY
individual_id,
min_txn_date
) x GROUP BY is_new
This demo on DB Fiddle demonstrates each step of the computation.

SQL calculate the sum of a column based on the date in two differents variables

I've a simple table in this forme :
BillItem (id,amount, volume, bill_date,....other fields)
I want to obtain in my query 4 differents sum of fields amount and volume based on the date
for example, in my table i've this data :
Id | amount | volume | bill_date | libelle
1 | 10 | 50 | 02/04/2016| bill1
2 | 20 | 55 | 02/04/2016| bill1
2 | 88 | 66 | 02/05/2016| bill1
3 | 30 | 60 | 03/05/2016| bill2
4 | 40 | 10 | 02/04/2016| bill3
5 | 50 | 20 | 02/05/2016| bill3
and the result must be like this :
bill1, sum_date_1=30, sum_date_2=88, sum_volume_date_1=105, sum_volume_date_2=66
bill2, sum_date_1=0, sum_date_2=30, sum_volume_date_1=0, sum_volume_date_2=60
bill3, sum_date_1=40, sum_date_2=50, sum_volume_date_1=10, sum_volume_date_2=20
i've this query with only two sum variable :
select ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE,
sum(bi.ACTUAL_AMOUNTVAT),sum(bi.ACTUAL_VOLUME), bi.BILL_DATE
from bill_item bi left outer join ANALYTIC_SECTION ans on ans.TREE_PATH=bi.REPORT_SECTION
where bi.account_id=7
and bi.BILL_DATE<='31/05/2016' and bi.BILL_DATE>='01/04/2016'
and ans.REPORT_TYPE='ARPE_REPORT' and ans.ACCOUNT_ID=7
group by ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE, bi.BILL_DATE;
Is it possible to obtain two differents sum for each field (amount and volume) ?
I've resolved the query like this :
select distinct ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE,
sum(Case when bi.BILL_DATE<'01/05/2016' then bi.ACTUAL_AMOUNTVAT ELSE 0 END) as amount_m1,
sum(Case when bi.BILL_DATE>='01/05/2016' then bi.ACTUAL_AMOUNTVAT ELSE 0 END) as amount_m,
sum(Case when bi.BILL_DATE<'01/05/2016' then bi.ACTUAL_VOLUME ELSE 0 END) as volume_m1,
sum(Case when bi.BILL_DATE>='01/05/2016' then bi.ACTUAL_VOLUME ELSE 0 END) as volume_m
--,bi.BILL_DATE
from bill_item bi left outer join ANALYTIC_SECTION ans on ans.TREE_PATH=bi.REPORT_SECTION
where bi.account_id=7
and bi.BILL_DATE<='06/05/2016' and bi.BILL_DATE>='06/04/2016'
and ans.REPORT_TYPE='ARPE_REPORT' and ans.ACCOUNT_ID=7
group by ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE;
Thank's All for your help
it looks to me like you want to summarize by each date and then return the value for the months in ascending fields?
It might work with a subquery and a ranking option, although this may not be the most efficient route.
Select t.Id
, Max(Case When t.BillRank=1 Then t.SumVolume Else Null End) As Volume1
, Max(Case When t.BillRank=2 Then t.SumVolume Else Null End) As Volume2
, Max(Case When t.BillRank=1 Then t.SumVAT Else Null End) As Vat1
, Max(Case When t.BillRank=2 Then t.SumVAT Else Null End) As Vat2
From ( Select ans.SERVICE_TYPE
, ans.SERVICE_SUB_TYPE
, Sum(bi.ACTUAL_AMOUNTVAT) As SumVAT
, Sum(bi.ACTUAL_VOLUME) As SumVolume
, bi.BILL_DATE
, bi.Id
, Rank() Over ( Partition By bi.Id Order By bi.BILL_DATE Asc ) As BillRank
From bill_item As bi
Left Outer Join ANALYTIC_SECTION as ans
On ans.TREE_PATH = bi.REPORT_SECTION
Group By ans.SERVICE_TYPE
, ans.SERVICE_SUB_TYPE
, bi.BILL_DATE
, bi.Id
) t
Group By t.Id;

sql how to transform data vertically

I have a 3 datbles Dealer, payment_type and dealer_payment_type
Dealer : dealer_id , dealer_name, dealer_address
1 | test | 123 test lane
2 | abc | abc lane
3 | def | def lane
Payment_type : paymenttype_id , paytype
1 | CHECK
2 | WIRE
3 | CREDIT
Dealer_Payment_type : DPT_id , dealer_id , payment_type_id
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 2
5 | 2 | 3
6 | 3 | 1
7 | 3 | 2
I have to write a query to get payment type info for each dealer , query needs to return data like this:
dealer_id , dealer_name , paytype
1 | test | check,wire,credit
2 | abc | wire,credit
3 | def | check,wire
OR
dealer_id , dealer_name , check , wire , credit
1 | test | true | true | true
2 | abc | false | true | true
3 | def | true | false | true
You did not specify what version of Oracle you are using.
If you are using Oracle 11g, then you can use the following.
To get the values into a single column, then you can use LISTAGG:
select d.dealer_id,
d.dealer_name,
listagg(p.paytype, ',') within group (order by d.dealer_id) as paytype
from dealer d
left join Dealer_Payment_type dp
on d.dealer_id = dp.dealer_id
left join payment_type p
on dp.payment_type_id = p.paymenttype_id
group by d.dealer_id, d.dealer_name;
See SQL Fiddle with demo
To get the values in separate columns, then you can use PIVOT:
select dealer_id, dealer_name,
coalesce("Check", 'false') "Check",
coalesce("Wire", 'false') "Wire",
coalesce("Credit", 'false') "Credit"
from
(
select d.dealer_id,
d.dealer_name,
p.paytype,
'true' flag
from dealer d
left join Dealer_Payment_type dp
on d.dealer_id = dp.dealer_id
left join payment_type p
on dp.payment_type_id = p.paymenttype_id
)
pivot
(
max(flag)
for paytype in ('CHECK' as "Check", 'WIRE' as "Wire", 'CREDIT' as "Credit")
)
See SQL Fiddle with Demo.
If you are not using Oracle 11g, then you can use wm_concat() to concatenate the values into a single row:
select d.dealer_id,
d.dealer_name,
wm_concat(p.paytype) as paytype
from dealer d
left join Dealer_Payment_type dp
on d.dealer_id = dp.dealer_id
left join payment_type p
on dp.payment_type_id = p.paymenttype_id
group by d.dealer_id, d.dealer_name;
To create the separate columns, then you can use an aggregate function with a CASE:
select dealer_id, dealer_name,
max(case when paytype = 'CHECK' then flag else 'false' end) "Check",
max(case when paytype = 'WIRE' then flag else 'false' end) "Wire",
max(case when paytype = 'CREDIT' then flag else 'false' end) "Credit"
from
(
select d.dealer_id,
d.dealer_name,
p.paytype,
'true' flag
from dealer d
left join Dealer_Payment_type dp
on d.dealer_id = dp.dealer_id
left join payment_type p
on dp.payment_type_id = p.paymenttype_id
)
group by dealer_id, dealer_name;
See SQL Fiddle with Demo

SQL Server SQL JOIN Assistance

I'm doing joins incorrectly, and haven't figured out the proper way to get the data I need given the 3 tables below (Customer, SalesHeader, SalesDetail) I'm trying to get line items with 1 Customer per line and the sum of all Sales Details that are in GL Acct 4000 and all Sales Deails in GL Acct 5000. There are many more columns, and several other GL Accounts I want to add, but I've broken it down to the simplest form to try to get it to work. I've provide mock data below and the lastest version of the SQL Statement I had unsuccessfully worked on. It would be greatly appreciated if someone could help me figure out what I'm doing wrong in this SQL Select Statement.
Customer
CustID | CustCode
------------------
1 | AAA111
2 | AN8348
SalesHeader
SH_ID | SH_CustID | SH_GLACCT
-------------------------------
1 | 1 | 4000
2 | 1 | 5000
3 | 1 | 4000
4 | 2 | 5000
SalesDetail
SD_ID | SD_HID | Price
--------------------------
1 | 1 | 100.00
2 | 1 | 540.00
3 | 2 | 100.00
4 | 3 | 600.00
5 | 4 | 50.00
6 | 4 | 75.00
Desired OUTPUT
Carpet = 4000
Pad = 5000
CustID | CustCode | Carpet (Sum all SH_GLACCT = 4000) | PAD (Sum all SH_GLACCT = 5000)
-------------------------------------------------------------------------------------------
1 | AAA111 | 1240.00 | 100.00
2 | AN8348 | 0.00 | 125.00
Incorrect SQL (Both Amounts are over what they should be)
SELECT C.CustID, C.CustCode, SUM(ADH.Price) AS Carpet, SUM(APD.Price) As Pad
FROM Customer AS C
LEFT OUTER JOIN SalesHeader AS ACH On C.CustID = ACH.SH_CustID AND ACH.SH_GLACCT = '4000'
LEFT OUTER JOIN SalesDetail AS ADH On ACH.SH_ID = ADH.SD_HID
LEFT OUTER JOIN SalesHeader AS APH On C.CustID = APH.SH_CustID AND APH.SH_GLACCT = '5000'
LEFT OUTER JOIN SalesDetail AS APD On APH.SH_ID = APD.SD_HID
GROUP BY C.CustID, C.CustCode
Try This:
Select c.CustId, c.CustCode
Sum(Case When h.SH_GLACCT = 4000 Then Price End) Acct4000Total,
Sum(Case When h.SH_GLACCT = 5000 Then Price End) Acct5000Total
From Customer c
Join Salesheader h On h.SH_CustID = c.CustID
Join SalesDetail d On d.SD_HID = h.SH_ID
Where h.SH_GLACCT In (4000, 5000)
Group By c.CustId
if you want to list the customers with no sales then use outer join:
Select c.CustId, c.CustCode
Sum(Case When h.SH_GLACCT = 4000 Then Price End) Acct4000Total,
Sum(Case When h.SH_GLACCT = 5000 Then Price End) Acct5000Total
From Customer c
Left Join (Salesheader h Join SalesDetail d
On d.SD_HID = h.SH_ID
And h.SH_GLACCT In (4000, 5000))
On h.SH_CustID = c.CustID
Group By c.CustId
Try something like the following:
SELECT c.CustID
, c.CustCode
, Carpet = SUM(CASE WHEN sh.SH_GLACCT = 4000 THEN sd.Price ELSE 0 END)
, Pad = SUM(CASE WHEN sh.SH_GLACCT = 5000 THEN sd.Price ELSE 0 END)
FROM Customer c
LEFT JOIN
SalesHeader sh
ON c.CustID = sh.CustID
LEFT JOIN
SalesDetail sd
ON sh.sh_id = sd.sd_hid
GROUP BY
c.CustID
, c.CustCode
You can use the PIVOT operator for this:
SELECT CustID, CustCode, SUM([4000]) Carpet, SUM([5000]) PAD
FROM Cust c
JOIN SalesHeader sh ON c.CustID = sh.SH_CustID
JOIN SalesDetail sd ON sh.SH_ID = sd.SD_HID
PIVOT (
SUM(sd.Price)
FOR sh.SH_GLACCT IN ([4000],[5000])
) AS pt
GROUP BY CustID, CustCode
SELECT C.CustID, C.CustCode, SH_GLACCT, SUM(Price) AS sum_price
FROM Customer C
INNER JOIN SalesHeader sh On C.CustID = sH.SH_CustID
LEFT OUTER JOIN SalesDetail sd On sh.SH_ID = sd.SD_HID
WHERE
SH_GLACCT in(4000,5000)
GROUP BY CustID, CustCode, SH_GLACCT

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