I've built a query that uses a typical header/detail set of tables to report on records from the header table but provides aggregated data per header record from various types on the detail table.
To get around the grouping issues I created a view to join against that does all the aggregation based on the invoice key.
CREATE OR REPLACE VIEW TransactDetailSummary_V AS
SELECT
td.invoice
, SUM(CASE WHEN td.TaxCollected1 + td.TaxCollected2 + td.TaxCollected3 <> 0 AND td.Type NOT IN (9,13) THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as taxable_sales
, SUM(CASE WHEN td.TaxCollected1 + td.TaxCollected2 + td.TaxCollected3 = 0 AND td.Type NOT IN (9,13) THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as nontaxable_sales
, SUM(CASE WHEN td.Type IN (8,18) THEN td.Amount ELSE 0 END) as total_taxes
, SUM(CASE WHEN td.Type IN (15,16,26) THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) AS returns
, SUM(CASE WHEN td.Type IN (24) THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) AS trade_ins
, SUM(CASE WHEN td.Type IN (9,13,22) THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) AS payments
, SUM(CASE WHEN td.Type = 2 THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as part_sales
, SUM(CASE WHEN td.Type = 3 THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as serial_sales
, SUM(CASE WHEN td.Type = 15 THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as part_returns
, SUM(CASE WHEN td.Type = 16 THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as serial_returns
, SUM(CASE WHEN td.Type = 10 THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as misc_charges
, SUM(CASE WHEN td.Type = 6 THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as labor_charges
, SUM(CASE WHEN td.Type IN (4,5,11) THEN ROUND(td.Quantity * td.Amount, 2) ELSE 0 END) as rental_sales
FROM transactdetail td
GROUP BY td.Invoice;
Then in the report query I join to the details view using the invoice key.
SELECT
'Customer Invoices COMPLETED' as section_title
, CAST(th.Invoice as UNSIGNED) as Invoice
, th.DateInvoice as invoice_date
, th.DateApply as completed_date
, th.orderstatus as status
, th.customername as customer_name
, tdv.taxable_sales
, tdv.nontaxable_sales
, tdv.total_taxes
, tdv.returns
, tdv.trade_ins
, tdv.payments
, tdv.part_sales
, tdv.serial_sales
, tdv.part_returns
, tdv.serial_returns
, tdv.misc_charges
, tdv.labor_charges
, tdv.rental_sales
FROM transactheader th
JOIN TransactDetailSummary_V tdv ON tdv.Invoice = th.Invoice
WHERE th.DateApply BETWEEN '2013-04-15' AND '2013-04-15'
AND th.OrderStatus IN ('Complete','Backorder')
AND th.Type = 1
AND th.Created = 'invoice'
ORDER BY 1;
In this example I get back 13 header records with the aggregate data, cool! But I started to dive into how much work the engine was doing and I found something startling.
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL 75609
1 PRIMARY th ref Invoice_Key,Type_Key,Status_Key Invoice_Key 20 tdv.invoice,const 10 Using where
2 DERIVED td ALL 350477 Using temporary; Using filesort
The explain shows that every row is being read in the details table to provide aggregates for the 13 headers I care about. I understand that the view is materializing and then filtering based on the join.
The question is, is there a way to do this so that it only reads the details for the 13 invoices I care about? I imaging a joined subquery would work the same way unless I can possibly correlate it?
Thanks in advance for any help.