astak16/blog-mysql

63 指定日期的产品价格

Opened this issue · 0 comments

题目

题目链接:指定日期的产品价格

编写一个解决方案,找出在 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_id3 的数据,因为这个产品在 2019-08-16 之前没有修改过价格

方法一

思路:

查询出在 2019-08-16 之前的最新修改过价格的 product_id

如果修改过价,那么 new_price 是有值的

没有修改过价格,那么 new_pricenull,给它赋值为 10

+------------+-------------+
| product_id | new_price   |
+------------+-------------+
| 1          | 35          |
| 2          | 50          |
| 3          | NULL        |
+------------+-------------+

步骤:

  1. 按照 product_id,查询出 2019-08-16 日期之前的最新修改过价格的产品,注意:这里查询不出价格
  2. 使用子查询,根据 product_idchange_date 查询出 product_idnew_price,作为临时表 tmp2
  3. 查询出所有产品的 product_id,作为临时表 tmp1
    • SELECT product_id FROM Products GROUP BY product_id
    • SELECT DISTINCT product_id FROM Products
  4. 左连查询,将 tmp1tmp2 左连,连接条件是 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_id3 的产品就没有价格

+------------+-------------+
| product_id | change_date |
+------------+-------------+
| 1          | 2019-08-16  |
| 2          | 2019-08-14  |
| 3          | NULL        |
+------------+-------------+

步骤:

  1. 按照 product_id 分组,查询出 2019-08-16 之前,最后修改价格的日期,作为临时表 tmp
  2. tmpProducts 左连,连接条件是 product_idchange_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)

方法三

思路:

使用窗口函数,将每个产品按照修改日期进行排序

步骤:

  1. 查询出在 2019-08-16 日期前修改过价格的记录,将大于 2019-08-16 的日期设置为 null,作为临时表 tmp
  2. 使用窗口函数,按照 product_id 分组,按照 change_date 降序排序,作为临时表 tmp2
  3. 查询出 tmp2,筛选出 rk = 1product_idnew_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

相关联的题目

按分类统计薪水