Reputation: 1346
I am struggling writing a (to me!) complex query which will calculate the total number of units sold of items, pulling data from two separate tables. I am using MySQL.
The first table is a simple 'order_contents' table (sale_ord_contents
in my example) with item_id's and the quantity sold. Some items may be an Assembly, in which case their contents are stored in another table, sale_ord_assembly_contents
, along with the quantity of each subcomponent. In my example here, the only 'Assembly' is the Fruit Basket. I have made a note that one of the Fruit Basket entries has a quantity sold of (2), meaning all of its contents should have their quantity multiplied by two.
My goal is to find the 20 most common items by their accumulative quantity sold between the two tables. The part I am struggling with the most is how to handle multiplying the values of subcomponents by their quantity of the parent in the sale_ord_contents
table.
I have written this query which handles the sale_ord_contents
calculation, but I am having trouble joining in the data from the sale_ord_assembly_contents
table...
SELECT
item_id, name, sku,
SUM(quantity) AS purchases
FROM
sale_ord_contents
GROUP BY
item_id
ORDER BY purchases DESC
LIMIT 20
Am I going to be better off doing this in PHP, or is this feasible in MySQL? If so, could a SQL guru assist me in writing a query that provides the desired results? Thank you!
Upvotes: 0
Views: 1222
Reputation: 795
The key to solving your problem is a left join. Essentially what this does is it matches up values between the table on the left and the table on the right side of the left join
. If the table on the right doesn't have any values that match up correctly, null
is inserted instead. Doing a left join between sale_ord_contents and sale_ord_assembly_contents will yield results like this:
Sale_ord_contents_name | quantity | sale_ord_assembly_contents_name | quantity apple | 5 | null | null fruitbasket 1 | 1 | apple | 3 fruitbasket 1 | 1 | orange | 1
These numbers don't match up with the example you provided, but you get the point. This is exactly what you will need to find the correct numbers.
The next keyword that is important is coalesce
. What this does is it goes through the list of items that you have provided and returns the first value that is not null. So if I use coalesce(sale_ord_assembly_contents_name, sale_ord_contents_name), for the above example the null in the assembly table will be ignored and apple will be returned. Then, since the assembly name exists for the next two rows, apple and orange will be the value returned by the coalesce.
I believe the following query is what you will need:
SELECT
coalesce(soa.item_id, so.item_id) as item_id,
coalesce(soa.name, so.name) as name,
so.sku,
SUM(so.quantity * coalesce(soa.quantity, 1)) AS purchases
FROM
sale_ord_contents so
left join sale_ord_assembly_contents soa
on so.id = soa.order_item_id
GROUP BY
item_id, name, so.sku
ORDER BY purchases DESC
LIMIT 20
Upvotes: 0
Reputation: 261
Updated my answer, please try again the following:
SELECT
IF (soac.item_id IS NULL, soc.item_id, soac.item_id) as item_id,
IF (soac.name IS NULL, soc.name, soac.name) as name,
SUM(soc.quantity * COALESCE(soac.quantity, 1)) AS total_sold
FROM
sale_ord_contents soc
LEFT OUTER JOIN sale_ord_assembly_contents soac
ON soac.order_item_id = soc.id
GROUP BY
IF (soac.quantity IS NULL, soc.item_id, soac.item_id),
IF (soac.name IS NULL, soc.name, soac.name)
ORDER BY total_sold DESC LIMIT 20
Upvotes: 1
Reputation: 44
Yes its feasible and the query execution time is also less as you have limit the number of rows returned. Please check in query optimizer in mysql workbench to get the query response time :). Writing too many for loops in php will degrade the page response time and exection time will be more. So its better to write high level application queries in your php code to get the better response time for your web pages :)
Upvotes: 0