题目
题目链接:指定日期的产品价格
编写一个解决方案,找出在 2019-08-16 时全部产品的价格,假设所有产品在修改前的价格都是 10 。
以 任意顺序 返回结果表。
结果格式如下例所示。
输入:
Products 表:
+------------+-----------+-------------+
| product_id | new_price | change_date |
+------------+-----------+-------------+
| 1 | 20 | 2019-08-14 |
| 2 | 50 | 2019-08-14 |
| 1 | 30 | 2019-08-15 |
| 1 | 35 | 2019-08-16 |
| 2 | 65 | 2019-08-17 |
| 3 | 20 | 2019-08-18 |
+------------+-----------+-------------+
(product_id, change_date) 是此表的主键(具有唯一值的列组合)。
这张表的每一行分别记录了 某产品 在某个日期 更改后 的新价格。
输出:
+------------+-------+
| product_id | price |
+------------+-------+
| 2 | 50 |
| 1 | 35 |
| 3 | 10 |
+------------+-------+
Create table If Not Exists Products (product_id int, new_price int, change_date date);
Truncate table Products;
insert into Products (product_id, new_price, change_date) values ('1', '20', '2019-08-14');
insert into Products (product_id, new_price, change_date) values ('2', '50', '2019-08-14');
insert into Products (product_id, new_price, change_date) values ('1', '30', '2019-08-15');
insert into Products (product_id, new_price, change_date) values ('1', '35', '2019-08-16');
insert into Products (product_id, new_price, change_date) values ('2', '65', '2019-08-17');
insert into Products (product_id, new_price, change_date) values ('3', '20', '2019-08-18');
解析
这题考察的点是如何查出 product_id
为 3
的数据,因为这个产品在 2019-08-16
之前没有修改过价格
方法一
思路:
查询出在 2019-08-16
之前的最新修改过价格的 product_id
,
如果修改过价,那么 new_price
是有值的
没有修改过价格,那么 new_price
是 null
,给它赋值为 10
+------------+-------------+
| product_id | new_price |
+------------+-------------+
| 1 | 35 |
| 2 | 50 |
| 3 | NULL |
+------------+-------------+
步骤:
- 按照
product_id
,查询出2019-08-16
日期之前的最新修改过价格的产品,注意:这里查询不出价格 - 使用子查询,根据
product_id
和change_date
查询出product_id
和new_price
,作为临时表tmp2
查询出所有产品的
product_id
,作为临时表tmp1
SELECT product_id FROM Products GROUP BY product_id
SELECT DISTINCT product_id FROM Products
左连查询,将
tmp1
和tmp2
左连,连接条件是product_id
- 使用
if
或者ifnull
判断new_price
是否为空,如果为空,则使用10
作为价格
- 使用
SELECT
product_id, IF(new_price, new_price, 10) price
FROM (
SELECT product_id FROM Products GROUP BY product_id
) tmp1
LEFT JOIN (
SELECT product_id, new_price FROM Products WHERE ( product_id, change_date )
IN ( SELECT product_id, max(change_date) FROM Products WHERE DATE ( change_date ) <= DATE ( "2019-08-16" ) GROUP BY product_id )
) tmp2 USING ( product_id )
方法二
方法二的思路正好和方法一是相反的
思路:
查询出每个产品在 2019-08-16
之前,最后修改价格的日期,在左连 Products
表,那么 product_id
为 3
的产品就没有价格
+------------+-------------+
| product_id | change_date |
+------------+-------------+
| 1 | 2019-08-16 |
| 2 | 2019-08-14 |
| 3 | NULL |
+------------+-------------+
步骤:
- 按照
product_id
分组,查询出2019-08-16
之前,最后修改价格的日期,作为临时表tmp
将
tmp
和Products
左连,连接条件是product_id
和change_date
- 使用
if
或者ifnull
判断new_price
是否为空,如果为空,则使用10
作为价格
- 使用
WITH tmp AS (
SELECT
product_id, MAX(IF(DATE ( change_date ) <= DATE ( "2019-08-16" ), change_date, NULL)) change_date
FROM Products GROUP BY product_id
)
SELECT
product_id, IF(new_price, new_price, 10) price
FROM tmp LEFT JOIN Products USING(product_id, change_date)
方法三
思路:
使用窗口函数,将每个产品按照修改日期进行排序
步骤:
- 查询出在
2019-08-16
日期前修改过价格的记录,将大于2019-08-16
的日期设置为null
,作为临时表tmp
- 使用窗口函数,按照
product_id
分组,按照change_date
降序排序,作为临时表tmp2
查询出
tmp2
,筛选出rk = 1
的product_id
和new_price
- 使用
if
或者ifnull
判断new_price
是否为空,如果为空,则使用10
作为价格 - 使用
distinct
去重,可能会有某个产品某天修改多次的记录
- 使用
SELECT
DISTINCT product_id, IF(change_date, new_price, 10) price
FROM (
SELECT
*, RANK() OVER(PARTITION BY product_id ORDER BY change_date DESC) AS rk
FROM (
SELECT product_id, new_price, IF(DATE( change_date ) > DATE( "2019-08-16" ), NULL, change_date) change_date FROM Products
) tmp
) tmp2 WHERE rk = 1