Reputation: 90
I am trying to make a printable page, where there is all the sales of a specified manufacturer, listing all the products, between specified dates. If there has not been any sales, it should display 0.
The tables
// Manufacturer table
// mid, manufacturer
// Products table
// pid, product, ref_manufacturer_id
// Orders table
// oid, orderPrice, orderDateTime, ref_product_id
And the query that works (without date limitation)
SELECT prod.product, COALESCE(COUNT(pord.oid),0) AS orderCount,
COALESCE(SUM(pord.orderPrice),0) AS orderSum
FROM product_manufacturer AS manu
JOIN product_list AS prod ON prod.ref_manufacturer_id = manu.mid
LEFT JOIN product_orders AS pord ON pord.ref_product_id = prod.pid
WHERE manu.mid = :manu_id
GROUP BY prod.product;
But as soon as I add into the WHERE-syntax this
WHERE manu.mid = :manu_id AND DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end
I am using PHP PDO on connecting and verifying that the manu_id is int and the orders_start/end is converted to MySQL date format.
But the question I am trying to fidn out is, what is causing the problem, that when I add the date restriction, every product that was not ordered, is not displayed on the output?
SQL on creating the tables
CREATE TABLE product_list (
pid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
product varchar(255) NOT NULL,
ref_manufacturer_id bigint(20) unsigned NOT NULL,
PRIMARY KEY (pid),
KEY ref_manufacturer_id (ref_manufacturer_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE product_manufacturer (
mid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
manufacturer varchar(255) NOT NULL,
PRIMARY KEY (mid),
UNIQUE KEY manufacturer (manufacturer)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE product_orders (
oid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
orderPrice float(10,2) NOT NULL,
orderDatetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
red_product_id bigint(20) unsigned NOT NULL,
PRIMARY KEY (oid),
KEY red_product_id (red_product_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Upvotes: 1
Views: 346
Reputation: 23183
Try:
SELECT p.product,
COALESCE(o.orderCount, 0) as orderCount,
COALESCE(o.orderSum,0) AS orderSum
FROM product_manufacturer AS m
JOIN product_list AS p ON p.ref_manufacturer_id = m.mid
LEFT JOIN (
SELECT ref_product_id as pid, COUNT(oid) AS orderCount, SUM(orderPrice) AS orderSum
FROM product_orders
WHERE DATE(orderDateTime) BETWEEN :orders_start AND :orders_end
GROUP BY ref_product_id
) AS o ON p.pid = o.pid
WHERE m.mid = :manu_id
Edit: Corrected after ypercube comment.
Upvotes: 2
Reputation: 79929
What you need is to move the orderDateTime
criteria to the join clause instead of where clause like:
SELECT prod.product, COALESCE(COUNT(pord.oid),0) AS orderCount,
COALESCE(SUM(pord.orderPrice),0) AS orderSum
FROM product_manufacturer AS manu
JOIN product_list AS prod ON prod.ref_manufacturer_id = manu.mid
LEFT JOIN product_orders AS pord
ON pord.ref_product_id = prod.pid
AND DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end
WHERE manu.mid = :manu_id
GROUP BY prod.product;
The reason it does not work within the WHERE
clause is because of the NULL
values returned from the outer join. When you do not have a row in product_orders
fot a product, the outer join returns a NULL for the date field orderDateTime
and that row will be filtered out because a NULL
is not equal to anything.
Upvotes: 2
Reputation: 6632
I don't know how your specific system works, but it may be orderDateTime
is not set (ie, NULL
or something else) until that product gets ordered. You may want to try:
WHERE manu.mid = :manu_id AND ((DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end) OR pord.orderDateTime=NULL)
If this is not the case, could you give an example of the orderDateTime
value for something that is not showing up when you want it to?
Upvotes: 0
Reputation: 1520
try this on the where clause.
WHERE manu.mid = :manu_id AND (DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end)
It might be reading the second AND function as another where clause that the statement should return true. Just a hunch on that. Let me know if this does the trick.
Upvotes: 0