小众书籍:过去一年卖出不到 10 本
LeetCode 1098 · LEFT JOIN 带时间条件、ON 与 WHERE 的区别
面试怎么回答
书籍 LEFT JOIN 订单,订单的时间范围放在 ON 里,保证零销量的书不丢;上架满一个月的条件放 WHERE。按书分组 SUM(IFNULL(quantity, 0)) < 10。这题的核心是 LEFT JOIN 时条件放在 ON 和 WHERE 的区别。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
书籍表有上架日期,订单表有发货日期和数量。假设今天是 2019-06-23,找出过去一年里卖出不到 10 本的书;上架不满一个月的新书不参与统计。
表结构
- Books:book_id(主键)、name、available_from
- Orders:order_id(主键)、book_id、quantity、dispatch_date
思路
以书籍表为主表 LEFT JOIN 订单表,时间范围(过去一年)要写在 ON 里:这样一年内没卖出的书也会保留(订单列为空);如果写在 WHERE 里,这些书会被过滤掉。上架时间的条件是对书本身的过滤,写在 WHERE 里。最后按书分组,对数量求和(空值当 0)小于 10。
参考解法
SELECT a.book_id, a.name
FROM books a LEFT JOIN orders b ON a.book_id=b.book_id
AND dispatch_date BETWEEN DATE_ADD('2019-06-23', INTERVAL -1 YEAR) AND '2019-06-23'
WHERE a.available_from <= DATE_ADD('2019-06-23',INTERVAL -1 MONTH)
GROUP BY a.book_id, a.name
HAVING SUM(IFNULL(b.quantity,0)) < 10
ORDER BY a.book_id;
易错点
- 把订单的时间条件写在 WHERE 里,一年内零销量的书会消失。
- 要统计的是卖出的「本数」(quantity 之和),不是订单数。
- MySQL 和 Hive 的日期加减函数写法不同,迁移时要注意。