平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“SQL偏移类窗口函数 LAG、LEAD的用法小结”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
从实现思路看,在 SQL 里,偏移类窗口函数 LAG() 和 LEAD() 用来访问当前行的前几行或后几行的值。

LAG() 函数得到当前行的前几行的数据。
LAG(Expression, OffSetValue, DefaultVar) OVER (
PARTITION BY [Expression]
ORDER BY Expression [ASC|DESC]
);
NULL,如果没有设置 default_value,且当前行是窗口的第一行或没有前几行数据时,得到 NULL。GROUP BY。如果没有此项,整个数据集视为一个窗口。表格数据
sales 表,表结构和数据如下所示:
| id | month | revenue |
|---|---|---|
| 1 | Jan | 100 |
| 2 | Feb | 150 |
| 3 | Mar | 200 |
采用 LAG() 函数来拿到按月排序后的“revenue”列的前一行的值。
SELECT id,
month,
revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_revenue
FROM sales;
| id | month | revenue | prev_revenue |
|---|---|---|---|
| 1 | Jan | 100 | NULL |
| 2 | Feb | 150 | 100 |
| 3 | Mar | 200 | 150 |
Tips:
prev_revenue 为 NULL。prev_revenue 为第一行的 revenue 值(100)。prev_revenue 为第二行的 revenue 值(150)。采用 LAG() 函数,同时指定偏移量为 2,拿到两行之前的“revenue”值。
SELECT id,
month,
revenue,
LAG(revenue, 2) OVER (ORDER BY month) AS prev_revenue
FROM sales;
| id | month | revenue | prev_revenue |
|---|---|---|---|
| 1 | Jan | 100 | NULL |
| 2 | Feb | 150 | NULL |
| 3 | Mar | 200 | 100 |
Tips:
prev_revenue 为 NULL。prev_revenue 为第一行的 revenue 值(100)。采用 LAG() 函数,同时指定默认值为 0,当无法拿到前一行的值时得到默认值。
SELECT id,
month,
revenue,
LAG(revenue, 1, 0) OVER (ORDER BY month) AS prev_revenue
FROM sales;
| id | month | revenue | prev_revenue |
|---|---|---|---|
| 1 | Jan | 100 | 0 |
| 2 | Feb | 150 | 100 |
| 3 | Mar | 200 | 150 |
Tips:
SELECT
sale_date,
amount,
LAG(amount, 1, 0) OVER (ORDER BY sale_date) AS previous_day_amount,
amount - LAG(amount, 1, 0) OVER (ORDER BY sale_date) AS difference
FROM sales;
LAG(amount, 1, 0):这行的 LAG 函数表示拿到前一天(前一行)的 amount 列的值,如果前一天没有数据(比如第一行),则得到 0。ORDER BY sale_date,确保按日期顺序排列数据。| sale_date | amount | previous_day_amount | difference |
|---|---|---|---|
| 2025-01-01 | 100 | 0 | 100 |
| 2025-01-02 | 150 | 100 | 50 |
| 2025-01-03 | 200 | 150 | 50 |
| 2025-01-04 | 180 | 200 | -20 |

LEAD() 函数与 LAG() 类似,但它得到的是当前行的后几行的数据。
LEAD(Expression, OffSetValue, DefaultVar) OVER (
PARTITION BY [Expression]
ORDER BY Expression [ASC|DESC]
);
NULL,如果没有设置 default_value,且当前行是窗口的第一行或没有前几行数据时,得到 NULL。GROUP BY。如果没有此项,整个数据集视为一个窗口。采用 LEAD() 函数来拿到按月排序后的“revenue”列的后一行的值。
SELECT id,
month,
revenue,
LEAD(revenue) OVER (ORDER BY month) AS next_revenue
FROM sales;
| id | month | revenue | next_revenue |
|---|---|---|---|
| 1 | Jan | 100 | 150 |
| 2 | Feb | 150 | 200 |
| 3 | Mar | 200 | NULL |
Tips:
next_revenue 为第二行的 revenue 值(150)。next_revenue 为第三行的 revenue 值(200)。采用 LEAD() 函数,同时指定偏移量为 2,拿到两行之后的“revenue”值。
SELECT id,
month,
revenue,
LEAD(revenue, 2) OVER (ORDER BY month) AS next_revenue
FROM sales;
| id | month | revenue | next_revenue |
|---|---|---|---|
| 1 | Jan | 100 | 200 |
| 2 | Feb | 150 | NULL |
| 3 | Mar | 200 | NULL |
Tips:
采用 LEAD() 函数,并指定默认值为 0,当无法拿到后一行的值时得到默认值。
SELECT id, month, revenue, LEAD(revenue, 1, 0) OVER (ORDER BY month) AS next_revenue
FROM sales;
| id | month | revenue | next_revenue |
|---|---|---|---|
| 1 | Jan | 100 | 150 |
| 2 | Feb | 150 | 200 |
| 3 | Mar | 200 | 0 |
Tips:
SELECT
sale_date,
amount,
LEAD(amount, 1, 0) OVER (ORDER BY sale_date) AS next_day_amount,
LEAD(amount, 1, 0) OVER (ORDER BY sale_date) - amount AS difference
FROM sales;
LEAD(amount, 1, 0):这行的 LEAD 函数表示拿到下一天(下一行)的 amount 列的值。如果下一天没有数据(比如最后一行),则得到 0。ORDER BY sale_date,确保按日期顺序排列数据。| sale_date | amount | next_day_amount | difference |
|---|---|---|---|
| 2025-01-01 | 100 | 150 | 50 |
| 2025-01-02 | 150 | 200 | 50 |
| 2025-01-03 | 200 | 180 | -20 |
| 2025-01-04 | 180 | 0 | -180 |
最后再来一个小练习(lc会员题):查找电影院所有连续可用的座位。


WITH t1 AS (
SELECT
seat_id, -- 选择座位ID
free, -- 选择当前座位的空闲状态
lag(free, 1, 999) OVER() AS pre, -- 获取当前座位前一个座位的空闲状态,默认值为 999
lead(free, 1, 999) OVER() AS next -- 获取当前座位后一个座位的空闲状态,默认值为 999
FROM Cinema -- 从 Cinema 表中选择数据
)
SELECT
seat_id -- 返回座位ID
FROM t1 -- 从 t1 子查询中选择数据
WHERE
free = 1 -- 当前座位为空闲
AND (pre = 1 OR next = 1) -- 前一个座位或后一个座位为空闲
ORDER BY seat_id; -- 按座位ID升序排序
思路:
lag(free, 1, 999) 和 lead(free, 1, 999):
lag(free, 1, 999) 用来拿到当前座位前一个座位的 free 值(默认为 999,表示没有前一个座位)。lead(free, 1, 999) 用来拿到当前座位后一个座位的 free 值(默认为 999,表示没有后一个座位)。free = 1 和 (pre = 1 OR next = 1):
free = 1)。pre = 1 OR next = 1),表示这些座位是连续空闲的。ORDER BY seat_id:
| seat_id | free |
|---|---|
| 1 | 1 |
| 2 | 0 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
借助执行查询,得到的 t1 子查询结果:
| seat_id | free | pre | next |
|---|---|---|---|
| 1 | 1 | 999 | 0 |
| 2 | 0 | 1 | 1 |
| 3 | 1 | 0 | 1 |
| 4 | 1 | 1 | 1 |
| 5 | 1 | 1 | 999 |
落到代码里,从 t1 中筛选出满足 free = 1 且 (pre = 1 OR next = 1) 的行,得到的结果:
| seat_id |
|---|
| 3 |
| 4 |
| 5 |
到此这篇关于SQL偏移类窗口函数 LAG、LEAD的用法小结的文章就介绍到这了,更多相关SQL偏移类窗口函数 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多兼容脚本之家!