----VQ 查询用户刷题日期和下一次刷题日期
现有牛客刷题记录表questions_pass_record ,请查询用户user_id,刷题日期date (每组按照date降序排列)和该用户的下一次刷题日期nextdate(若是没有则为None),组之间按照user_id升序排序,每组内按照date升序排列,查询返回结果名称和顺序为:
user_id|date|nextdate
表的创建及数据的添加:
sql
drop table if exists questions_pass_record;
CREATE TABLE questions_pass_record
(
user_id int NOT NULL,
question_type varchar(32) NOT NULL,
device varchar(14) NOT NULL,
question_id int NOT NULL,
date date NOT NULL
);
INSERT INTO questions_pass_record
VALUES (101, 'java', 'app', 2, '2020-03-01');
INSERT INTO questions_pass_record
VALUES (102, 'sql', 'pc', 15, '2021-07-07');
INSERT INTO questions_pass_record
VALUES (102, 'python', 'pc', 9, '2021-04-09');
INSERT INTO questions_pass_record
VALUES (102, 'python', 'app', 3, '2022-03-17');
INSERT INTO questions_pass_record
VALUES (103, 'sql', 'pc', 60, '2018-08-15');
INSERT INTO questions_pass_record
VALUES (104, 'sql', 'pc', 20, '2019-05-15');
INSERT INTO questions_pass_record
VALUES (105, 'sql', 'pc', 550, '2022-07-25');
INSERT INTO questions_pass_record
VALUES (105, 'sql', 'pc', 299, '2020-05-16');
INSERT INTO questions_pass_record
VALUES (106, 'java', 'pc', 17, '2021-04-15');
INSERT INTO questions_pass_record
VALUES (106, 'java', 'pc', 20, '2021-04-15');
查询如下:
sql
select q1.user_id, q1.date, q2.date as nextdate
from (
select user_id, date,
row_number() over (partition by user_id order by date) as nextNum
from questions_pass_record) q1
left join
(
select user_id, date,
row_number() over (partition by user_id order by date) as nextNum
from questions_pass_record) q2
on q1.user_id = q2.user_id and q1.nextNum + 1 = q2.nextNum
或者直接使用函数:
sql
select user_id,date,
lead(date, 1) over (partition by user_id order by date) as nextdate
from questions_pass_record
两个查询的执行结果都如下:
sql
user_id date nextdate
101 2020-03-01 NULL
102 2021-04-09 2021-07-07
102 2021-07-07 2022-03-17
102 2022-03-17 NULL
103 2018-08-15 NULL
104 2019-05-15 NULL
105 2020-05-16 2022-07-25
105 2022-07-25 NULL
106 2021-04-15 2021-04-15
106 2021-04-15 NULL
在上述查询中:
① ROW_NUMBER() 函数
在 SQL Server 中,ROW_NUMBER( )
函数结合 OVER
子句用于为结果集中的行分配一个唯一的整数序号。
ROW_NUMBER( )
函数的常见用法是将数据分组后,根据指定的排序方式为每个分组中的行分配一个序号。
语法:
sql
ROW_NUMBER() OVER (PARTITION BY column1 ORDER BY column2)
其中,PARTITION BY
指定了分组的列,而 ORDER BY
指定了排序的列。
sql
row_number() over (partition by user_id order by date) as nextNum
上面的示例中,我们将结果集按 user_id 列进行分组,并按 date列进行排序。然后,为每个分组中的行分配一个唯一的整数序号,命名为 nextNum。
需要注意 :
<1> ROW_NUMBER( )
函数是在 SELECT 查询的结果集上计算的,并不会对数据库中的实际数据进行更改。如果需要使用序号进行更新、删除等操作,可以将查询的结果作为子查询,并在外部查询中进行相关操作。
<2> ROW_NUMBER( )
函数返回的序号是根据 ORDER BY
子句的排序顺序而确定的。如果 ORDER BY
子句中出现相同的值,结果可能会根据数据库引擎的具体实现而产生不确定的结果。如果需要确保排序的唯一性,请考虑在 ORDER BY
子句中使用更多的列来进行排序。
② LEAD( ) 函数
在 SQL Server 中,LEAD( )
函数用于在每一行中获取指定列的后一行的值。LEAD( )
函数结合 OVER 子句可以对每个分组进行操作。
语法:
sql
LEAD(column, offset, default value) OVER (PARTITION BY partition_column ORDER BY order_column)
column:指定要获取后一行值的列。
offset:可选参数,指定要获取的后一行的偏移量,默认为 1,即获取紧接着当前行的下一行的值。
default value:可选参数,在最后一行时用于提供默认值。
sql
lead(date, 1) over (partition by user_id order by date) as nextdate
在查询中我们将结果集按 user_id 列进行分组,并按 date 列进行排序。然后使用 LEAD( )
函数获取每个分组中当前行的下一个行的 date 列的值,将其命名为 nextdate。
需要注意 :
<1> LEAD( )
函数的结果是基于指定的分组和排序顺序计算的。如果没有下一行或者超出了最后一行,LEAD( )
函数根据提供的默认值返回 NULL。
<2>通过使用 LEAD( )
函数,可以方便地获取指定列的后一行的值,这一点在很多情况都非常好用。