Forcing all rows from first table of a join

一曲冷凌霜 提交于 2020-01-05 03:05:49

问题


I have three tables, machines holding vending machines, products holding all possible products, and machines_products which is the intersection of the two, giving how many of each product line is stocked in a particular machine. If a product is not stocked in a machine, there is no corresponding row in the third table.

DESCRIBE machines_products;
+------------+------------------+------+-----+---------+-------+
| Field      | Type             | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+-------+
| machine_id | int(10) unsigned | NO   | PRI | 0       |       |
| product_id | int(10) unsigned | NO   | PRI | 0       |       |
| quantity   | int(10) unsigned | NO   |     | 0       |       |
+------------+------------------+------+-----+---------+-------+

Each product has a category (think chocolate bars vs. drinks bottles) and a machine knows what category of products it can vend. I want a result table of all products for the category, with a quantity for a specific machine. I have got as far as this:

SELECT products.*, SUM(quantity) qty
FROM products
LEFT JOIN machines_products USING (product_id)
WHERE machine_id=m AND category_id=c
GROUP BY product_id;

The problem is that this filters out all rows where there is no quantity, whereas what I want is all rows from the left table, and NULL/0 in the qty column if there are no corresponding rows in the right-hand table.

BTW: this is not a homework question! I am 30 and sitting in my office :o)


回答1:


SELECT p.*
     , SUM(mp.quantity) AS qty
FROM products p
  LEFT JOIN machine_products mp
    ON mp.product_id = p.product_id
    AND mp.machine_id = m              --- this condition moved from WHERE to ON
WHERE p.category_id = c
GROUP BY p.product_id



回答2:


Actually I figured out the answer a short while after posting. The trick is to avoid specifying either of the columns from the third table's primary key (i.e. machine_id and product_id) in the WHERE clause. By using an AND in the JOIN's ON condition, and specifying the machine ID there, I get the result I was looking for.

SELECT products.*, quantity
FROM products
LEFT JOIN machines_products
  ON products.product_id=machines_products.product_id
  AND machine_id=m
WHERE category_id=c

The COALESCE() function suggested by Brendan was not necessary in my case, since I check the value with PHP's empty() function, so NULL is fine.

As it turns out, there was never a need for GROUP BY, which I had been playing with when posting the question.




回答3:


SUM returns NULL if a single value in the equation is NULL. COALESCE the value first and then SUM:

SELECT p.*, SUM(COALESCE(mp.quantity, 0)) AS qty
FROM products p
LEFT JOIN machine_products mp ON mp.product_id = p.id
WHERE mp.machine_id = m
AND p.category_id = c
GROUP BY p.id

I assumed you have a column in products called id. Rename if it's something different...




回答4:


SELECT p.id, SUM(mp.quantity) AS qty 
FROM products p 
LEFT JOIN machines_products mp ON p.id=mp.product_id
WHERE mp.machine_id=m 
AND p.category_id=c 
GROUP BY p.id;


来源:https://stackoverflow.com/questions/6609059/forcing-all-rows-from-first-table-of-a-join

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!