Our db stores customer orders in two tables: customerorders and customerorderlines. Amongst other fields, there is a customerorders.type field which determines (being = 1 or = 2) if that customerorder is an invoice or a creditnote.
We currently have a report that lists amount of units sold, profits etc but with its current query, only displays total units sold over the period, ie, it doesnt subtract the number of credits, if any. Here is its sql and an example of the results it generates:
(apologies for the massive query that follows :))
SELECT l.name AS locationname
, sr.name AS salesrepname
, ct.name AS customertypename
, c.name AS customername
, c.id AS customer_id
, c.code
, s.name AS suppliername
, p.description AS productname
, p.id AS product_id
, p.unitofmeasure
, SUM(col.vatableprice) AS totalsales
, SUM(col.vatprice) AS vat
, SUM(col.quantity) AS totalitems
, SUM(col.quantity * col.costprice) AS totalsalecost
, SUM(col.vatableprice) - SUM(col.quantity * col.costprice) AS totalprofit
,(
SELECT SUM(col2.vatableprice) AS totalsales
FROM customerorders AS co2
LEFT JOIN customerorderlines AS col2 ON col2.customerorder_id = co2.id
LEFT JOIN customers AS c2 ON c2.id = co2.customer_id
LEFT JOIN locations AS l2 ON l2.id = c2.location_id
LEFT JOIN customertypes AS ct2 ON ct2.id = c2.customertype_id
LEFT JOIN salesreps AS sr2 ON sr2.id = c2.salesrep_id
LEFT JOIN products AS p2 ON p2.id = col2.product_id
LEFT JOIN suppliers AS s2 ON s2.id = p2.supplier_id
WHERE c.salesrep_id = c2.salesrep_id
AND co2.type = 2 AND p2.supplier_id = 179
AND co2.orderdate >= '2010-01-01 00:00:00'
AND co2.orderdate <= '2010-02-01 23:59:59'
) AS credits
,(
SELECT SUM(col2.vatprice) AS totalvat FROM customerorders AS co2
LEFT JOIN customerorderlines AS col2 ON col2.customerorder_id = co2.id
LEFT JOIN customers AS c2 ON c2.id = co2.customer_id
LEFT JOIN locations AS l2 ON l2.id = c2.location_id
LEFT JOIN customertypes AS ct2 ON ct2.id = c2.customertype_id
LEFT JOIN salesreps AS sr2 ON sr2.id = c2.salesrep_id
LEFT JOIN products AS p2 ON p2.id = col2.product_id
LEFT JOIN suppliers AS s2 ON s2.id = p2.supplier_id
WHERE c.salesrep_id = c2.salesrep_id
AND co2.type = 2
AND p2.supplier_id = 179
AND co2.orderdate >= '2010-01-01 00:00:00'
AND co2.orderdate <= '2010-02-01 23:59:59'
) AS creditsvat
FROM customerorders AS co
LEFT JOIN customerorderlines AS col ON col.customerorder_id = co.id
LEFT JOIN customers AS c ON c.id = co.customer_id
LEFT JOIN locations AS l ON l.id = c.location_id
LEFT JOIN customertypes AS ct ON ct.id = c.customertype_id
LEFT JOIN salesreps AS sr ON sr.id = c.salesrep_id
LEFT JOIN products AS p ON p.id = col.product_id
LEFT JOIN suppliers AS s ON s.id = p.supplier_id
WHERE co.status_v = 5
AND co.type = 1
AND p.supplier_id = 179
AND co.orderdate >= '2010-01-01 00:00:00' AND co.orderdate <= '2010-02-01 23:59:59'
GROUP BY c.salesrep_id
Which, in this case (grouping etc is determined by application code) generates a 'per sales rep' report:
Rep | TotalItems | SalesValue| CostOfSales | Profit | VAT | Credits | Credits(VAT)
Rep1| 937 | £5796.49 | £3606.49 | £2190.00 | £1013.73 | £220.12 | £38.57
Rep2| 1905 | £11695.09 | £7314.95 | £4380.14 | £2045.32 | £268.85 | £47.00
Rep3| 1074 | £6346.61 | £3950.53 | £2396.08 | £1109.76 | £54.89 | £9.57
Rep4| 2687 | £16129.42 | £10171.65 | £5957.77 | £2820.46 | £839.15 | £146.78
So, the problem lies in that TotalItems is the absolute number of items sold (all customerorders of type = 1). The credits field shows total cost of items in the period of type = 2, ie returned. TotalItems should have the qty of credits deducted from it so at a glance it can be seen what has actually been sold and of course all the other fields need their Credits counterpart deducting from each other too so that they reflect the correct amounts for items sold.
At first I thought this would be as simple modification to the existing query, but then noticed that i could not reference subquery aliases in the select so I rewrote the whole query using JOIN (SELECT ....) AS sales / JOIN (SELECT .....) AS credits so I could then reference sales.qty and credits.qty from the SELECT at the top of the query but then that didnt scale at all unless you were doing little queries.
This is how far I got:
(Yes, Im querying for different things here... this would be essentially the simplest form of the query: sales / credits for a single product)
SELECT sr.name AS salesrepsname
,l.name AS locationname
,sup.name AS suppliername
,p.description AS productname
,sales.qty AS sold
,credits.qty AS credits
,sales.qty - credits.qty AS actualsold
FROM
customerorders co
LEFT JOIN customerorderlines col ON col.customerorder_id = co.id
LEFT JOIN customers c ON c.开发者_如何学Cid = co.customer_id
LEFT JOIN products p ON p.id = col.product_id
LEFT JOIN salesreps sr ON sr.id = c.salesrep_id
LEFT JOIN locations l ON l.id = c.location_id
LEFT JOIN suppliers sup ON sup.id = p.supplier_id
JOIN (SELECT SUM(col.quantity) AS qty,
SUM(col.vatableprice) AS total FROM customerorderlines col
LEFT JOIN customerorders co ON co.id = col.customerorder_id
WHERE col.product_id = 27642 AND co.type = 1)
AS sales
JOIN (SELECT SUM(col2.quantity) AS qty FROM customerorderlines col2
LEFT JOIN customerorders co2 ON co2.id = col2.customerorder_id
WHERE col2.product_id = 27642 AND co2.type = 2) AS credits
WHERE col.product_id = 27642
GROUP BY c.salesrep_id
So I have to admit Im a bit stuck, not being very knowledgable with mysql at all.
Any suggestions are very welcome and please feel free to point me towards any literature on advanced subquerying and joins that I should be reading.
Cheers!
I think the trick you may be looking for is along these lines. In any given query/subquery you want to fix, use some SQL like this, selecting all the rows, regardless of whether it's an invoice or a creditnote, and summing a value that's either positive or negative depending on which way the order/money is going:
SELECT
SUM(CASE WHEN co.type = 1 THEN col.quantity ELSE -col.quantity END)
FROM
...
Get it? You grab all your order lines back, but when you do the sum, you're adding up positive credits and negative debits, to get the real total in one operation.
精彩评论