题目描述

(通过次数317 | 提交次数446,通过率71.08%)

表: CoffeeShop
+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| id          | int     |
| drink       | varchar |
+-------------+---------+
id 是该表的主键。
该表中的每一行都显示了订单 id 和所点饮料的名称。一些饮料行为 null。

编写一个 SQL 查询,将 drink 的 null值替换为前面最近一行不为 null的 drink。保证表第一行的 drink 不为 null。
返回与输入顺序相同的结果表。

查询结果格式示例如下。
示例 1:
输入:
CoffeeShop 表:
+----+------------------+
| id | drink            |
+----+------------------+
| 9  | Mezcal Margarita |
| 6  | null             |
| 7  | null             |
| 3  | Americano        |
| 1  | Daiquiri         |
| 2  | null             |
+----+------------------+
输出: 
+----+------------------+
| id | drink            |
+----+------------------+
| 9  | Mezcal Margarita |
| 6  | Mezcal Margarita |
| 7  | Mezcal Margarita |
| 3  | Americano        |
| 1  | Daiquiri         |
| 2  | Daiquiri         |
+----+------------------+
解释: 
对于 ID 6,之前不为空的值来自 ID 9。我们将 null 替换为 “Mezcal Margarita”。
对于 ID 7,之前不为空的值来自 ID 9。我们将 null 替换为 “Mezcal Margarita”。
对于 ID 2,之前不为空的值来自 ID 1。我们将 null 替换为 “Daiquiri”。
请注意,输出中的行与输入中的行相同。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/change-null-values-in-a-table-to-the-previous-value

//测试数据
Create table If Not Exists CoffeeShop (id int, drink varchar(20));

insert into CoffeeShop (id, drink) values ('9', 'Mezcal Margarita');
insert into CoffeeShop (id, drink) values ('6', 'None');
insert into CoffeeShop (id, drink) values ('7', 'None');
insert into CoffeeShop (id, drink) values ('3', 'Americano');
insert into CoffeeShop (id, drink) values ('1', 'Daiquiri');
insert into CoffeeShop (id, drink) values ('2', 'None');

解题思路

这道题出的不是很严谨。
示例中给出了CoffeeShop表的几条样例数据,然后要求以样例数据的顺序来判断某条数据的上一条数据。
实际上,数据在数据库中存储时是无序的,如果不明确指定排序规则,SQL语句多次执行的顺序可能是不一样的。
如果需要明确的查出某条数据的前一条,必须先以一个明确的排序规则编出序号。显然根据题目描述,本题是没有一个明确的排序规则的。
当然,不是说没有明确的排序规则就无法排序,而是说下面同样的SQL,多次执行,可能排序结果不一样。

select 
    id,
    drink,
    row_number() over() rn
from CoffeeShop;

抛开这个问题不谈,题目本身怎么解决呢?
假如,我们知道了每一行的排序序号,那么根据题目描述,如果当前行的drink为空,则需要返回上一个不为空的值。
也就是说,返回从当前行向前数,第一个drink的值不为空的行的drink值。
这里可以使用自关联来实现。
最后,再开窗排序,获取每行最近一个drink不为空的值即可。

参考SQL

未特别说明的情况下,参考SQL为基于MySQL8.0实现。
with
tmp as (
    select 
        id,
        drink,
        row_number() over() rn
    from CoffeeShop
)
select
    c.id,
    c.drink
from (
    select
        a.id,
        coalesce(a.drink,b.drink) drink,
        a.rn,
        row_number() over(partition by a.id order by b.rn desc) rn2
    from tmp a
    left join tmp b
    on a.rn > b.rn
    and b.drink is not null
)c
where c.rn2 = 1
order by c.rn;
picture loss