Reputation: 374
I have three relevant tables in a MySQL database:
Inventory_transactions have a quantity_offset that stores how many of a given inventory_item were added or removed from a given location.
I have the following query, which will get the global_quantity (sum of all quantity_offsets for a given inventory_item_id) as well as location_quantity which does the same for a specific location so long as I know the location ID.
SELECT sku, name, SUM(quantity_offset) AS global_quantity,
(
SELECT SUM(quantity_offset)
FROM inventory_transactions
WHERE inventory_items.inventory_item_id = inventory_transactions.inventory_item_id AND inventory_transactions.location_id = 1
) AS location_quantity
FROM inventory_items
JOIN inventory_transactions ON inventory_items.inventory_item_id = inventory_transactions.inventory_item_id
GROUP BY sku
What I'd like to end up with is a column being added for each location, so the result may have values as such:
sku, name, global_quantity, denver_quantity, dallas_quantity, ft_wayne_quantity
My fallback is to do this on an individual inventory_item page (when I'll know the inventory item and can group by location) but being able to do it in a single query and put it all into one table would be pretty great.
Initially, I had thought I could iterate through the subqueries but was unable to set the column name (in AS) programmatically.
Thank you in advance for any assistance you can provide.
Upvotes: 0
Views: 138
Reputation: 2097
You should be using conditional sums - this is the most efficient way (fastest query) I know of:
SELECT sku, name, SUM(quantity_offset) AS global_quantity,
sum(case when inventory_transactions.location_id = 1 then quantity_offset else 0 end) as loc1_quantitity,
sum(case when inventory_transactions.location_id = 2 then quantity_offset else 0 end) as loc2_quantitity,
sum(case when inventory_transactions.location_id = 3 then quantity_offset else 0 end) as loc3_quantitity
FROM inventory_items JOIN inventory_transactions ON inventory_items.inventory_item_id = inventory_transactions.inventory_item_id
GROUP BY sku
Upvotes: 1
Reputation: 108420
As an aside, rather than using a subquery in the SELECT list, you could get the same result with a SUM aggregate on an expression that conditionally returns the quantity_offset if the row is from a given location. I would also use a LEFT outer join to the inventory_transactions table, so that I would be sure to get a row back for each row in inventory_items:
SELECT i.sku
, i.name
, SUM(t.quantity_offset) AS global_quantity
, SUM(IF(t.location_id = 1,t.quantity_offset,NULL)) AS location_1_quantity
FROM inventory_items i
LEFT
JOIN inventory_transactions t ON i.inventory_item_id = t.inventory_item_id
GROUP BY i.sku
(That doesn't answer your question, but I wanted to make you aware of that.)
To answer your question, firstly, it's not possible for a SELECT statement to dynamically change the number or types of the columns it returns in a resultset. You need to define an expression for every column you want returned. (The generation of the SQL SELECT statment text could be done dynamically, to produce a particular SELECT statement, but the columns returned by that SELECT statement are fixed by the SELECT statement.)
If I had a relatively fixed set of location_id values, and I needed the result set you specify, I would repeat the SUM of the conditional expression multiple times, once for each location or whatever set of locations I wanted:
SELECT i.sku
, i.name
, SUM(t.quantity_offset) AS global_quantity
, SUM(IF(t.location_id = 1,t.quantity_offset,NULL)) AS location_1_quantity
, SUM(IF(t.location_id = 2,t.quantity_offset,NULL)) AS location_2_quantity
, SUM(IF(t.location_id = 3,t.quantity_offset,NULL)) AS location_3_quantity
, SUM(IF(t.location_id IN (1,2,3),t.quantity_offset,NULL)) AS location_123_quantity
, SUM(IF(t.location_id = 4,t.quantity_offset,NULL)) AS location_4_quantity
FROM inventory_items i
LEFT
JOIN inventory_transactions t ON i.inventory_item_id = t.inventory_item_id
GROUP BY i.sku
If the set of columns to be returned needs to be truly dynamic, then I wouldn't return them as columns at all. Instead, I would return the SUM(quantity_offset) for each location as a separate row, and then handle the conversion to a column-wise representation on the client side.
Upvotes: 1
Reputation: 37233
try this
SELECT sku, name, SUM(quantity_offset) AS global_quantity,
(
SELECT SUM(quantity_offset)
FROM inventory_transactions
WHERE inventory_items.inventory_item_id = inventory_transactions.inventory_item_id AND inventory_transactions.location_id = 1
AND location = denver) AS denver_quantity ,
(
SELECT SUM(quantity_offset)
FROM inventory_transactions
WHERE inventory_items.inventory_item_id = inventory_transactions.inventory_item_id AND inventory_transactions.location_id = 1
AND location = dallas) AS dallas_quantity,
(
SELECT SUM(quantity_offset)
FROM inventory_transactions
WHERE inventory_items.inventory_item_id = inventory_transactions.inventory_item_id AND inventory_transactions.location_id = 1
AND location = ft_wayne) AS ft_wayne_quantity
FROM inventory_items
JOIN inventory_transactions ON inventory_items.inventory_item_id = inventory_transactions.inventory_item_id
GROUP BY sku
note that i used location
as column name i dont know what column name of location you have and just replace it
Upvotes: 0