SQL with COUNTs and SUMs with cross-UNION GROUP BY
SQL guru needed, this is killing my neurons. I have a db structure (simplified) like this:
DESC documents;
id INT PK
bill_id INT FK
dtype INT -- 1=receipts, 0=invoices
total DECIMAL
DESC bills;
id INT PK
waiter_id INT FK
DESC waiters;
id INT PK
name VARCHAR
surname VARCHAR
Quite self-explanatory, but I need to count and sum the totals for all the receipts (documents.dtype=1) and the invoices (documents.dtype=0), grouped by waiter. I did two SELECTs:
SELECT
B.waiter_id,
WA.name,
WA.surname,
COUNT(D.id) AS Receipts,
SUM(D.total) AS TotReceipts
FROM
documents D
JOIN bills B ON (B.id = D.bill_id)
JOIN waiters WA ON (WA.id = B.waiter_id)
WHERE
D.dtype = 1
GROUP BY
waiter_id;
Fine, I get:
1, 'Mario', 'Rossi', 6, 485.20
2, 'Luigino', 'Bianchi', 1, 456.00
the did the second SELECT, just changing the documents.dtype to 0:
SELECT
B.waiter_id,
WA.name,
WA.surname,
COUNT(D.id) AS Invoices,
SUM(D.total) AS TotInvoices
FROM
documents D
JOIN bills B ON (B.id = D.bill_id)
JOIN waiters WA ON (WA.id = B.waiter_id)
WHERE
D.dtype = 0
GROUP BY
waiter_id;
And now I get:
1, 'Mario', 'Rossi', 1, 38.00
Now I can UNION the two SELECTSs
SELECT
B.waiter_id,
WA.name,
WA.surname,
COUNT(D.id) AS Receipts,
SUM(D.total) AS TotReceipts
FROM
documents D
JOIN bills B ON (B.id = D.bill_id)
JOIN waiters WA ON (WA.id = B.waiter_id)
WHERE
D.dtype = 1
GROUP BY
waiter_id
UNION SELECT
B.waiter_id,
WA.name,
WA.surname,
COUNT(D.id) AS Invoices,
SUM(D.total) AS TotInvoices
FROM
documents D
JOIN bills B ON (B.id = D.bill_id)
JOIN waiters WA ON (WA.id = B.waiter_id)
WHERE
D.dtype = 0
GROUP BY
waiter_id;
And I get:
1, 'Mario', 'Rossi', 6, 485.2开发者_C百科0
2, 'Luigino', 'Bianchi', 1, 456.00
1, 'Mario', 'Rossi', 1, 38.00
mmm, correct but I need the rows grouped by waiter cross-union! That is I want a single line for the waiter Mario:
wid wname wsurname receipts totreceipts invoices totinvoices
1, 'Mario', 'Rossi', 6, 485.20 1 38.0
2, 'Luigino', 'Bianchi', 1, 456.00 0 0.0
That would be just great, but I'd also like two more total columns to sum-up the numbers like:
wid wname wsurname receipts totreceipts invoices totinvoices docs totdocs
1, 'Mario', 'Rossi', 6, 485.20 1 38.0 7 523.20
2, 'Luigino', 'Bianchi', 1, 456.00 0 0.0 1 456.00
And that would be super-ultra-cool.
You could move the conditon from the where
clause to a case
statement, like:
SELECT
B.waiter_id,
WA.name,
WA.surname,
SUM(case when d.dtype = 1 then 1 end) AS Receipts,
SUM(case when d.dtype = 1 then D.total end) AS TotReceipts,
SUM(case when d.dtype = 0 then 1 end) AS Invoices,
SUM(case when d.dtype = 0 then D.total end) AS TotInvoices
FROM
documents D
JOIN bills B ON (B.id = D.bill_id)
JOIN waiters WA ON (WA.id = B.waiter_id)
GROUP BY
waiter_id
This query returns what you asked
SELECT
B.waiter_id,
WA.name,
WA.surname,
(select count(id) from documents where dtype = 0 and bill_id = B.id) AS Invoices,
(select isnull(sum(total), 0) from documents where dtype = 0 and bill_id = B.id) AS TotInvoices,
(select count(id) from documents where dtype = 1 and bill_id = B.id) AS Receipts,
(select isnull(sum(total), 0) from documents where dtype = 1 and bill_id = B.id) AS TotReceipts
FROM bills B
inner join waiters WA on WA.id = B.waiter_id
To summarize all the totals, you can make a inner select in order to sum up the totals as:
select data.*,
data.Invoices + data.Receipts as docs,
data.TotInvoices + data.TotReceipts as totaldocs
from (
select
B.waiter_id,
WA.name,
WA.surname,
(select count(id) from documents where dtype = 0 and bill_id = B.id) AS Invoices,
(select isnull(sum(total), 0) from documents where dtype = 0 and bill_id = B.id) AS TotInvoices,
(select count(id) from documents where dtype = 1 and bill_id = B.id) AS Receipts,
(select isnull(sum(total), 0) from documents where dtype = 1 and bill_id = B.id) AS TotReceipts
from bills B
inner join waiters WA on WA.id = B.waiter_id
) data
精彩评论