I am trying to list product variations with their quantities ordered, BUT ALSO show the product variations where there is no quantity ordered. So I thought it would be as simple as selecting the products and doing a left join on the orders of each product where the order is a current revision.
So I expected like this order of operations:
SELECT p.product_id, SUM(po.quantity)
FROM `products` p
LEFT JOIN `product_orders` po ON p.product_id=po.product_id
LEFT JOIN `orders` o ON o.order_id=po.order_id AND o.is_current='1'
but that is getting also the quantities where the is_current is not 1
Then I thought, okay, I can just do an inner join after the left join instead like this:
SELECT p.product_id, SUM(po.quantity)
FROM `products` p
LEFT JOIN `product_orders` po ON p.product_id=po.product_id
INNER JOIN `orders` o ON o.order_id=po.order_id AND o.is_current='1'
but then the products which have not been ordered yet are not being listed. I expected them to to show up as SUM(quantity) being NULL.
Can anyone see where my logic has gone wrong?
Thanks!
Scott
While Kaj's answer is correct, it's not necessarily ideal, as MySQL tends to skip the utilization of indexes when using derived tables (sub-selects). At least, this is my understanding. You can continue to use your methodology of using JOINS if you place the joins within parenthesis:
SELECT p.product_id, SUM(po.quantity)
FROM `products` p
LEFT JOIN (`product_orders` po
INNER JOIN `orders` o ON o.order_id=po.order_id AND o.is_current='1')
ON p.product_id=po.product_id;
Just remember that the ON clause for the LEFT JOIN needs to come after the parenthesis. I hope this helps!
If the only product orders that count are those where it is current then you need to find that subset before you do a left join to it. Otherwise if you do a left join you either get all or only those ordered as you've discovered.
So something like the following should work:
select p.productid, sum(po.quantity)
from products p
left outer join (select po.productid, po.quantity
from productorders po
inner join orders o on o.orderid = po.orderid and o.iscurrent = 1) po on po.productid = p.productid
group by p.productid
Try grouping by your product id:
SELECT p.product_id, SUM(po.quantity)
FROM `products` p
LEFT JOIN `product_orders` po ON p.product_id=po.product_id
INNER JOIN `orders` o ON o.order_id=po.order_id AND o.is_current='1'
GROUP BY p.product_id
Related
I am trying to list all customers’ names with each product that customer has ordered and include customers who don't have an order. Also If a customer has ordered the same product multiple times, only list the product once for that customer.
I have the beginning of the query set up so its showing all customers and each product ordered but I can't seem to figure out how to add customers with NULL value ( meaning they haven't ordered an item.) I know left outer join is supposed to be used somehow. This is what I have so far:
select distinct
c.customerName, p.productName
from
products p, customers c, orders o, orderDetails d
left join
When using a left join a syntax like:
select distinct
c.customerName, p.productName
from customers c
left join order o on c.id = o.customer_id
left join orderDetails d on o.id = d.order_id
left join products p on p.id = d.product_id AND
p.productName = 'cow'
I have four tables products, product_histories, vendor_invoices and invoices
This is the query I have developed
SELECT p.product_id, product_name, vendor_name FROM products AS p
INNER JOIN product_histories AS ph ON p.product_id = ph.product_id
CASE
WHEN ph.history_type = "P" THEN
LEFT JOIN vendor_invoices AS vi ON link_id = vi.vi_id
WHEN ph.history_type = "S" THEN
LEFT JOIN invoices AS i ON i.invoice_id = link_id
END
ORDER BY ph_id ASC
What I want that if ph.history_type is P then is should join vendor_invoices and if it is S then it should join invoices. But it says there is a syntax error.
Can anyone help me out with it? Or could show a better way to achieve this problem.
SELECT
CH.ChannelName, COUNT(O.OrderID) AS Orders
FROM
Channels CH
LEFT JOIN Programs P USING (ChannelID)
LEFT JOIN Codes C USING (ProgramID)
LEFT JOIN Order O USING (CodeID)
WHERE
O.OrderDate = '2012-04-11'
GROUP BY
CH.ChannelName
WITH ROLLUP
This query is only returning channels that have orders. How do I display ALL channels, even if there are no orders in the order table for that particular channel? So basically, all channels will be listed, and if there are no orders for that channel, I need to display zero.
I know the solution to this is probably very simple. Thanks for the help.
SELECT
CH.ChannelName, COUNT(O.OrderID) AS Orders
FROM
Channels CH
LEFT JOIN Programs P USING (ChannelID)
LEFT JOIN Codes C USING (ProgramID)
LEFT OUTER JOIN Order O USING (CodeID)
WHERE
O.OrderDate = '2012-04-11'
GROUP BY
CH.ChannelName
WITH ROLLUP
Try this:
SELECT CH.ChannelName, SUM(O.OrderDate = '2012-04-11') AS Orders
FROM Channels CH
LEFT JOIN Programs P USING (ChannelID)
LEFT JOIN Codes C USING (ProgramID)
LEFT JOIN Order O USING (CodeID)
GROUP BY CH.ChannelName
WITH ROLLUP
Your where-clause limits the query to the channels that have orders for that date, but if you move that condition into the join statement it will give you the result you want:
SELECT
CH.ChannelName, COUNT(O.ID) AS Orders
FROM
Channels CH
LEFT JOIN Programs P USING (ChannelID)
LEFT JOIN Codes C USING (ProgramID)
LEFT JOIN Order O ON CH.CodeID = O.CodeID AND O.OrderDate = '2012-04-11'
GROUP BY
CH.ChannelName
WITH ROLLUP
Note, that it should be COUNT(O.ID) to make SQL count only rows with non-null orders. In that case you'll correctly get zero orders count for channels without orders.
SELECT i.id AS id, i.modify_date as modify_date, s.subscription as subscriptionid, p.paid/i.total AS paidratio
FROM invoices i,
(SELECT p.invoice, sum(amount) AS paid FROM payments p GROUP BY p.invoice) p
LEFT JOIN sub_to_inv s
ON i.id=s.invoice
WHERE p.invoice=i.id
AND i.corporation='3'
AND i.payer=1
The error I get is "unknown column on i.id" which is total bogus - invoices (i) has an id row for sure. They all do.
The purpose of the sub=query is to find out how much of the invoice has been paid. For an invoice that has a "total" column of 1000.00, for example, could have 2 or 3 split payments. What I ultimately wnat to do here is list all the unpaid invoices or partially invoices first. But before I even get to the ORDER BY stage, I need to figure out this error.
Use JOIN syntax for all joins. Don't mix JOIN syntax with the comma-style SQL-89 syntax.
SELECT ...
FROM invoices i
INNER JOIN (SELECT...) p
ON p.invoice=i.id
LEFT OUTER JOIN sub_to_inv s
ON i.id=s.invoice
WHERE
AND i.corporation='3'
AND i.payer=1
Explanation: JOIN has higher precedence than comma-joins. So p JOIN s is evaluated before the query evaluates the join to i. Therefore, in the clause ON i.id=s.invoice, the i table is not yet known and is an invalid reference.
See http://dev.mysql.com/doc/refman/5.1/en/join.html, in the doc following "Join Processing Changes in MySQL 5.0.12".
Can you try this ?
SELECT i.id AS id, i.modify_date as modify_date, s.subscription as subscriptionid, p.paid/i.total AS paidratio
FROM invoices i
LEFT JOIN
(SELECT p.invoice, sum(amount) AS paid FROM payments p GROUP BY p.invoice) p
ON p.invoice=i.id
LEFT JOIN sub_to_inv s
ON i.id=s.invoice
WHERE i.corporation='3'
AND i.payer=1
I think you might be running into issues because of the order of your table joins in your SQL. Have you tried using an inner join. Perhaps try:
SELECT
i.id AS id,
i.modify_date as modify_date,
s.subscription as subscriptionid,
p.paid/i.total AS paidratio
FROM
invoices i
INNER JOIN
(SELECT
p.invoice,
sum(amount) AS paid
FROM
payments p
GROUP BY
p.invoice) p
ON
p.invoice=i.id
LEFT JOIN
sub_to_inv s
ON
i.id=s.invoice
WHERE
i.corporation='3' AND i.payer=1
I have 3 tables: events, products and a association table event_products.
events:event_id,
products: product_id, product_name
event_products:event_id, product_id
There can be none or many products associated with an event.
My question is I need a list of events, and only one resulting row for each event. In the list I need only one product for each event, preferably the first one added, or if that can't be done, the one with lowest product_id.
select e.event_id, ep.product_id, p.product_name
FROM `events` e
LEFT OUTER JOIN event_products ep on ep.event_id = e.event_id
LEFT OUTER JOIN products p on p.product_id = ep.product_id;
will, ofcourse, return multiple rows for every event that have multiple products assigned.
thanks
select e.event_id, min(ep.product_id), p.product_name
FROM `events` e
LEFT OUTER JOIN event_products ep on ep.event_id = e.event_id
LEFT OUTER JOIN products p on p.product_id = ep.product_id
group by e.event_id, p.product_name
;
SELECT e.event_id, p.product_id, p.product_name
FROM products p,
(SELECT e.event_id as event_id, min(p.product_id) as product_id
FROM events e, event_products ep, products p
where e.event_id = ep.event_id, ep.product_id = p.product_id
group_by e.event_id) e
Give this a try.
SELECT `ep`.`product_id`, `p`.`product_name`
FROM `event_products` AS `ep`
JOIN `products` AS `p` USING (`product_id`)
GROUP BY `ep`.`event_id` HAVING `ep`.`product_id`=MIN(`ep`.`product_id`)
Make sure to use proper indexes for this query (test with EXPLAIN)
By your comment I guess that you'll need additional INDEX(product_id) on table event_products.