零基础学SQL 23:和上月比总对不上?LAG/LEAD 一步取出上一次(附 4 个模板)

每个月做报表,老板都要问一句:"这个月比上个月涨了多少?"

我最早的做法是写两个查询,一个算这个月,一个算上个月,再把两个结果拼到一起。后来学聪明点,用自连接把月份错开一位再 JOIN 回来------SQL 能跑,但每次写都要想半天连接条件。

其实 MySQL 里有个函数专门干这件事:LAG,把"上一行"的值直接搬到当前行来。

一行搞定,不用自连接,不用子查询。


一、本篇用到的练习表

和本系列每篇一样,先建库建表。整段可以直接复制运行,重复执行也不会报"表已存在"。

sql 复制代码
-- SQL 系列统一练习表(复制即可运行,可重复执行)
CREATE DATABASE IF NOT EXISTS sql_practice DEFAULT CHARACTER SET utf8mb4;
USE sql_practice;

DROP TABLE IF EXISTS 员工表;
CREATE TABLE 员工表 (
    员工id   INT          PRIMARY KEY,
    姓名     VARCHAR(20),
    部门     VARCHAR(20),
    工资     DECIMAL(10,2),
    邮箱     VARCHAR(50),
    手机     VARCHAR(20),
    入职日期 DATE
);
INSERT INTO 员工表 (员工id, 姓名, 部门, 工资, 邮箱, 手机, 入职日期) VALUES
(1, '张三', '技术部', 9000.00, 'zhangsan@demo.com', '13800000001', '2019-03-01'),
(2, '李四', '技术部', 9000.00, 'lisi@demo.com',     '13800000002', '2020-07-15'),
(3, '王五', '销售部', 9200.00, 'wangwu@demo.com',   '13800000003', '2018-01-10'),
(4, '赵六', '销售部', 9200.00, 'zhaoliu@demo.com',  '13800000004', '2021-05-20');

DROP TABLE IF EXISTS 订单表;
CREATE TABLE 订单表 (
    订单id   INT          PRIMARY KEY,
    员工id   INT,
    订单金额 DECIMAL(10,2),
    下单时间 DATETIME,
    付款时间 DATETIME,
    状态     VARCHAR(20)
);
INSERT INTO 订单表 (订单id, 员工id, 订单金额, 下单时间, 付款时间, 状态) VALUES
(101, 1, 3000.00, '2026-01-10 10:00:00', '2026-01-10 10:05:00', '已付款'),
(102, 1, 2500.00, '2026-01-15 14:00:00', '2026-01-15 14:10:00', '已付款'),
(103, 3, 4000.00, '2026-01-20 09:30:00', '2026-01-20 09:40:00', '已付款'),
(104, 2, 1500.00, '2026-01-25 16:00:00', NULL,                  '待付款'),
(105, 3, 6000.00, '2026-02-01 11:00:00', '2026-02-01 11:20:00', '已付款'),
(106, 1, 1200.00, '2026-02-05 15:00:00', '2026-02-05 15:10:00', '已付款'),
(107, 2, 8000.00, '2026-02-10 10:30:00', '2026-02-10 10:45:00', '已付款'),
(108, 3, 2200.00, '2026-02-15 17:00:00', '2026-02-15 17:30:00', '已付款'),
(109, 1, 5500.00, '2026-02-20 09:00:00', '2026-02-20 09:15:00', '已付款'),
(110, 2, 3300.00, '2026-02-25 14:20:00', '2026-02-25 14:40:00', '已付款');

表里是 2026 年 1 月和 2 月的订单,10 条。后面所有例子都跑在这张表上。

二、LAG 是干嘛的

2.0 之前我是怎么写的:自连接

在没有 LAG 的时候,要跟上个月比,最常见的写法是把月份错开一位,自己连自己:

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
)
SELECT a.月份, a.销售额 本月, b.销售额 上月
FROM 月度 a
LEFT JOIN 月度 b ON b.月份 = DATE_FORMAT(DATE_SUB(
        STR_TO_DATE(CONCAT(a.月份, '-01'), '%Y-%m-%d'), INTERVAL 1 MONTH), '%Y-%m');

能跑,结果也对。但你得先算"上一个月是几月",再拿这个月份去连自己------月份要是断了(比如 2 月没有数据),LEFT JOIN 出来就是 NULL,还得再处理一次。

换成 LAG,同样的结果:

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
)
SELECT 月份, 销售额 本月, LAG(销售额) OVER (ORDER BY 月份) 上月
FROM 月度 ORDER BY 月份;

两种方式对比一下:

自连接 LAG
代码量 要先算上一个月,还要写连接条件 一行
月份断档 断档那个月连出来是 NULL 取的是"上一行",跟月份连不连续无关
按部门各比各的 连接条件里要带上部门 加一个 PARTITION BY
读起来 得反过来想"谁连谁" 直说"取上一次"

"取上一行"是位置关系,不是值的关系。 自连接是在值上做匹配(找上一个月是哪个月),LAG 是在排好队的行上按位置取(往前挪一行)。月份断不断、编号连不连续,跟 LAG 没关系。

2.1 先把月度数据算出来

要比"这个月 vs 上个月",得先有一份按月汇总的数据:

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
)
SELECT * FROM 月度 ORDER BY 月份;
月份 销售额
2026-01 11000.00
2026-02 26200.00

2.2 一行取出上一次

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
)
SELECT 月份, 销售额,
       LAG(销售额) OVER (ORDER BY 月份) 上月销售额
FROM 月度 ORDER BY 月份;
月份 销售额 上月销售额
2026-01 11000.00 NULL
2026-02 26200.00 11000.00

LAG(销售额) 的意思是"把上一行的销售额搬到这一行来"。第一行前面没有行,所以是 NULL。

一个字都没改表结构,没写自连接,一行就比出来了。

2.3 首行那个 NULL 怎么处理

LAG 其实有三个参数,平时只用第一个:

sql 复制代码
-- LAG 三个参数的写法(这不是能执行的语句,是参数说明)
-- LAG(列名, 往前第几行, 取不到时给什么)

所以想把首行的 NULL 变成 0,写第二个和第三个参数就行:

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
)
SELECT 月份, 销售额,
       LAG(销售额, 1, 0) OVER (ORDER BY 月份) 上月销售额
FROM 月度 ORDER BY 月份;
月份 销售额 上月销售额
2026-01 11000.00 0.00
2026-02 26200.00 11000.00

第二个参数 1 是"往前一行",改成 2 就是"往前两行"。第三个参数是取不到时填的默认值,不写就是 NULL。

三个参数放在一起看:

写法 意思 首行结果
LAG(销售额) 往前 1 行,取不到给 NULL NULL
LAG(销售额, 1) 同上,1 是默认值 NULL
LAG(销售额, 1, 0) 往前 1 行,取不到给 0 0
LAG(销售额, 2) 往前 2 行 前两行都是 NULL

第二个参数不写默认是 1,第三个参数不写默认是 NULL。要注意的是第三个参数的类型得跟第一列对得上------销售额是小数,填 0 就写 0;如果是日期列,别填 0,要么不填让它 NULL,要么填一个具体日期。


三、LAG 排在第几步,以及三个必踩的坑

3.0 先看它在整个 SQL 里的位置

MySQL 执行一句查询,大致是这个顺序:

text 复制代码
FROM      →  WHERE  →  GROUP BY  →  聚合函数
          →  HAVING  →  窗口函数  →  ORDER BY  →  LIMIT

窗口函数排在聚合之后、排序之前。这个位置决定了三件事:

想在哪儿用 LAG 行不行 原因
SELECT 里 行 它就在这步算
WHERE 里 不行 WHERE 比它早,那时候还没算出来
HAVING 里 不行 HAVING 也比它早
ORDER BY 里 行 排序在它之后,能拿到结果
再套一层的外层 WHERE 行 内层已经算完了

所以 3.2、3.3 里那两个报错,不是 LAG 的毛病,是你把它放到了它还没出生的地方。

3.1 ORDER BY 决定"上一个"是谁(不报错,结果全变)

这是 LAG 最阴的地方:括号里的 ORDER BY 定义了什么叫"上一行"。写错了不报错,只是结果完全不对。

同一份数据,按时间排:

sql 复制代码
SELECT 订单id, 订单金额,
       LAG(订单金额) OVER (ORDER BY 下单时间) 上一笔
FROM 订单表 ORDER BY 下单时间;
订单id 订单金额 上一笔
101 3000.00 NULL
102 2500.00 3000.00
103 4000.00 2500.00
104 1500.00 4000.00
105 6000.00 1500.00

只把 ORDER BY 换成订单金额,其它一个字不动:

sql 复制代码
SELECT 订单id, 订单金额,
       LAG(订单金额) OVER (ORDER BY 订单金额) 按金额的上一个
FROM 订单表 ORDER BY 订单id;
订单id 订单金额 按金额的上一个
101 3000.00 2500.00
102 2500.00 2200.00
103 4000.00 3300.00
104 1500.00 1200.00
105 6000.00 5500.00

101 号订单的"上一个",从 NULL 变成了 2500。没有任何报错提示。

要的是"上一笔订单",ORDER BY 就得写时间;要的是"上一档金额",才写金额。括号里的排序 = 你对"上一次"的定义。

3.1.5 忘写 PARTITION BY:数据会串到别的组

先说结论:不写 PARTITION BY,LAG 会把整张表当成一队。

还是刚才那份部门月度数据,把 PARTITION BY 去掉:

sql 复制代码
WITH 部门月度 AS (
    SELECT e.部门, DATE_FORMAT(o.下单时间, '%Y-%m') 月份, SUM(o.订单金额) 销售额
    FROM 员工表 e JOIN 订单表 o ON e.员工id = o.员工id
    GROUP BY e.部门, DATE_FORMAT(o.下单时间, '%Y-%m')
)
SELECT 部门, 月份, 销售额,
       LAG(销售额) OVER (ORDER BY 月份, 部门) 不分组的上一个
FROM 部门月度 ORDER BY 月份, 部门;
部门 月份 销售额 不分组的上一个
技术部 2026-01 7000.00 NULL
销售部 2026-01 4000.00 7000.00
技术部 2026-02 18000.00 4000.00
销售部 2026-02 8200.00 18000.00

销售部 1 月的"上一个"变成了技术部 1 月的 7000------跨部门串了。加了 PARTITION BY 部门,每组才各自从 NULL 开始。

判断方法很简单:跑完看每组第一个的"上一次"是不是 NULL。不是 NULL,就是分组漏了。

3.2 WHERE 里用不了 LAG

算出上月之后,有人会想"只保留比上月高的行",于是写成:

sql 复制代码
SELECT 订单id, 订单金额
FROM 订单表
WHERE LAG(订单金额) OVER (ORDER BY 下单时间) > 2000;

MySQL 直接报错:

复制代码
ERROR 3593 (HY000): You cannot use the window function 'lag' in this context.

原因是执行顺序:WHERE 比窗口函数先跑,WHERE 执行的时候 LAG 还没算出来。

要过滤窗口函数的结果,就得套一层子查询(或者用 CTE),在外层过滤。

3.3 聚合函数里也套不了

sql 复制代码
SELECT SUM(LAG(订单金额) OVER (ORDER BY 下单时间)) FROM 订单表;

同样报:

复制代码
ERROR 3593 (HY000): You cannot use the window function 'lag' in this context.

窗口函数不能当聚合函数的参数。窗口函数是在聚合之后才跑的 ,想对窗口结果再做聚合,一样得套一层。

四、PARTITION BY:各部门自己跟自己比

上面的例子是整张表按一个顺序排。如果数据里有多个部门、多个产品,每个都要跟自己的上一次比,就得加 PARTITION BY:

sql 复制代码
WITH 部门月度 AS (
    SELECT e.部门, DATE_FORMAT(o.下单时间, '%Y-%m') 月份, SUM(o.订单金额) 销售额
    FROM 员工表 e JOIN 订单表 o ON e.员工id = o.员工id
    GROUP BY e.部门, DATE_FORMAT(o.下单时间, '%Y-%m')
)
SELECT 部门, 月份, 销售额,
       LAG(销售额) OVER (PARTITION BY 部门 ORDER BY 月份) 本部门上月
FROM 部门月度 ORDER BY 部门, 月份;
部门 月份 销售额 本部门上月
技术部 2026-01 7000.00 NULL
技术部 2026-02 18000.00 7000.00
销售部 2026-01 4000.00 NULL
销售部 2026-02 8200.00 4000.00

PARTITION BY 的作用是"分组后各自排队":技术部在自己的队列里比,销售部也在自己的队列里比,每组的第一个月都是 NULL,不会串到别的部门去。

不加 PARTITION BY,销售部的 1 月就会拿技术部的 2 月当"上一次",结果完全错。

还有个固定的报错要记住:PARTITION BY 必须写在 ORDER BY 前面,写反了直接语法错误:

sql 复制代码
SELECT 月份, LAG(销售额) OVER (ORDER BY 月份 PARTITION BY 月份) 上月 FROM 月度;
复制代码
ERROR 1064 (42000): You have an error in your SQL syntax; ...

五、LEAD:取下一次

LEAD 和 LAG 长得一模一样,只是方向相反------取"下一行":

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
)
SELECT 月份, 销售额,
       LEAD(销售额) OVER (ORDER BY 月份) 下月销售额
FROM 月度 ORDER BY 月份;
月份 销售额 下月销售额
2026-01 11000.00 26200.00
2026-02 26200.00 NULL

这回 NULL 出现在最后一行 。LEAD 同样支持三个参数,LEAD(列名, 2, 0) 就是"往后两行,取不到填 0"。


六、4 个能直接抄的模板

模板 1:算环比百分比

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
)
SELECT 月份, 销售额,
       LAG(销售额) OVER (ORDER BY 月份) 上月,
       ROUND((销售额 - LAG(销售额) OVER (ORDER BY 月份))
             / LAG(销售额) OVER (ORDER BY 月份) * 100, 1) 环比百分比
FROM 月度 ORDER BY 月份;
月份 销售额 上月 环比百分比
2026-01 11000.00 NULL NULL
2026-02 26200.00 11000.00 138.2

首行是 NULL 不会报错------任何值跟 NULL 做四则运算,结果都是 NULL,这反而是好事,不会算出个 100% 出来。

模板 2:算增长额(首月当 0)

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
)
SELECT 月份, 销售额,
       销售额 - LAG(销售额, 1, 0) OVER (ORDER BY 月份) 比上月多
FROM 月度 ORDER BY 月份;
月份 销售额 比上月多
2026-01 11000.00 11000.00
2026-02 26200.00 15200.00

模板 3:分组各自比

sql 复制代码
WITH 部门月度 AS (
    SELECT e.部门, DATE_FORMAT(o.下单时间, '%Y-%m') 月份, SUM(o.订单金额) 销售额
    FROM 员工表 e JOIN 订单表 o ON e.员工id = o.员工id
    GROUP BY e.部门, DATE_FORMAT(o.下单时间, '%Y-%m')
)
SELECT 部门, 月份, 销售额,
       LAG(销售额) OVER (PARTITION BY 部门 ORDER BY 月份) 本部门上月
FROM 部门月度 ORDER BY 部门, 月份;

模板 4:取前 N 次

sql 复制代码
SELECT 订单id, 订单金额,
       LAG(订单金额, 2) OVER (ORDER BY 下单时间) 前两笔
FROM 订单表 ORDER BY 下单时间;
订单id 订单金额 前两笔
101 3000.00 NULL
102 2500.00 NULL
103 4000.00 3000.00
104 1500.00 2500.00

前两行都是 NULL,从第三行开始才有值。

模板 5:判断是不是连续增长

想知道"这个月比上个月高还是低",把两次的值直接比一下就行:

sql 复制代码
WITH 月度 AS (
    SELECT DATE_FORMAT(下单时间, '%Y-%m') 月份, SUM(订单金额) 销售额
    FROM 订单表
    GROUP BY DATE_FORMAT(下单时间, '%Y-%m')
),
带上月 AS (
    SELECT 月份, 销售额,
           LAG(销售额) OVER (ORDER BY 月份) 上月
    FROM 月度
)
SELECT 月份, 销售额, 上月,
       CASE WHEN 上月 IS NULL THEN '首月无对比'
            WHEN 销售额 > 上月 THEN '涨'
            WHEN 销售额 < 上月 THEN '跌'
            ELSE '持平' END 走势
FROM 带上月 ORDER BY 月份;
月份 销售额 上月 走势
2026-01 11000.00 NULL 首月无对比
2026-02 26200.00 11000.00 涨

这里用了两个 CTE:第一个段算月度,第二个段算 LAG。因为窗口函数不能跟 CASE 的判断写在同一层(它比 CASE 晚跑),所以拆成两段最省事------这也是上一篇 WITH 多段串联最典型的用法。

注意 上月 IS NULL 这个判断要写在最前面:先排除首月,再做大小比较,不然 NULL 参与比较会得到"未知",三个分支一个都不进,走势会显示成"持平"。


七、和 WITH 配合:一份完整的环比报表

把前一篇的多段串联和这一篇的 LAG 放在一起,就是一份能直接交上去的报表:

sql 复制代码
WITH 部门月度 AS (
    SELECT e.部门, DATE_FORMAT(o.下单时间, '%Y-%m') 月份, SUM(o.订单金额) 销售额
    FROM 员工表 e JOIN 订单表 o ON e.员工id = o.员工id
    GROUP BY e.部门, DATE_FORMAT(o.下单时间, '%Y-%m')
)
SELECT 部门, 月份, 销售额,
       LAG(销售额) OVER (PARTITION BY 部门 ORDER BY 月份) 上月,
       ROUND((销售额 - LAG(销售额) OVER (PARTITION BY 部门 ORDER BY 月份))
             / LAG(销售额) OVER (PARTITION BY 部门 ORDER BY 月份) * 100, 1) 环比百分比
FROM 部门月度 ORDER BY 部门, 月份;
部门 月份 销售额 上月 环比百分比
技术部 2026-01 7000.00 NULL NULL
技术部 2026-02 18000.00 7000.00 157.1
销售部 2026-01 4000.00 NULL NULL
销售部 2026-02 8200.00 4000.00 105.0

四个部门的环比一次出来,各部门互不干扰。这就是 WITH + 窗口函数的组合价值:WITH 负责把复杂查询拆成能读懂的几段,窗口函数负责在同一段里做"跨行"的计算。


八、练习题

用本篇的练习表做下面三题,答案附在后面。

第 1 题:查出每笔订单,并带上这个人"上一笔订单"的金额。

第 2 题:在第 1 题的基础上,只要"有上一笔"的记录(把每人第一笔过滤掉)。

第 3 题:用 LEAD 查出每笔订单的"下一笔"金额,看看最后一笔是什么值。

答案

第 1 题:

sql 复制代码
SELECT o.员工id, e.姓名, o.订单id, o.订单金额, o.下单时间,
       LAG(o.订单金额) OVER (PARTITION BY o.员工id ORDER BY o.下单时间) 上一次金额
FROM 订单表 o JOIN 员工表 e ON e.员工id = o.员工id
ORDER BY o.员工id, o.下单时间;
员工id 姓名 订单id 订单金额 下单时间 上一次金额
1 张三 101 3000.00 2026-01-10 10:00:00 NULL
1 张三 102 2500.00 2026-01-15 14:00:00 3000.00
1 张三 106 1200.00 2026-02-05 15:00:00 2500.00
1 张三 109 5500.00 2026-02-20 09:00:00 1200.00
2 李四 104 1500.00 2026-01-25 16:00:00 NULL
2 李四 107 8000.00 2026-02-10 10:30:00 1500.00
2 李四 110 3300.00 2026-02-25 14:20:00 8000.00
3 王五 103 4000.00 2026-01-20 09:30:00 NULL
3 王五 105 6000.00 2026-02-01 11:00:00 4000.00
3 王五 108 2200.00 2026-02-15 17:00:00 6000.00

赵六(员工id 4)没有订单,用 JOIN 就查不出来。要让他也出现在结果里(上一次金额显示 NULL),把 JOIN 换成 LEFT JOIN:

sql 复制代码
SELECT e.姓名, o.订单id, o.订单金额,
       LAG(o.订单金额) OVER (PARTITION BY e.员工id ORDER BY o.下单时间) 上一次金额
FROM 员工表 e LEFT JOIN 订单表 o ON e.员工id = o.员工id
ORDER BY e.员工id, o.下单时间;

赵六那一行订单信息全是 NULL,上一次金额也是 NULL。这里有个细节:PARTITION BY e.员工id 要写员工表的 id,不能写 o.员工id------LEFT JOIN 补出来的那一行,订单表侧的员工id 是 NULL,用它分组会把赵六单独分成一组,算出来的"上一次"就不对了。

第 2 题:窗口函数不能写在 WHERE 里,所以要套一层:

sql 复制代码
WITH t AS (
    SELECT 员工id, 订单id, 订单金额,
           LAG(订单金额) OVER (PARTITION BY 员工id ORDER BY 下单时间) 上一次金额
    FROM 订单表
)
SELECT * FROM t WHERE 上一次金额 IS NOT NULL ORDER BY 员工id;

一共 7 行(10 笔订单减去 3 个人的第一笔)。

第 3 题:

sql 复制代码
SELECT 订单id, 员工id, 订单金额,
       LEAD(订单金额) OVER (PARTITION BY 员工id ORDER BY 下单时间) 下一次金额
FROM 订单表 ORDER BY 员工id, 下单时间;

每人的最后一笔,下一次金额都是 NULL。


九、LAG / LEAD 速查表

想干什么 写法
取上一次 LAG(列) OVER (ORDER BY 排序列)
取下一次 LEAD(列) OVER (ORDER BY 排序列)
首行不显示 NULL LAG(列, 1, 0)
取前两行 LAG(列, 2)
分组各自比 加 PARTITION BY 分组列(必须写在 ORDER BY 前)
算出结果后要过滤 套一层 CTE 或子查询,WHERE 里写不了

记一句话就够:括号里的 ORDER BY,定义的就是"上一次"是谁。


十、几个常被问到的问题

Q:LAG 和 GROUP BY 有什么区别?

GROUP BY 是把多行压成一行(汇总),LAG 是在保留原有行数的前提下多带一列出来。行数不一样:GROUP BY 之后行数变少,LAG 之后行数不变。

Q:LAG 能配合 WHERE 条件只算部分数据吗?

能,但要分清楚 WHERE 过滤的是"参与排队的行"。写在最里层的 WHERE 先筛数据,筛完再排队取上一次;而"取出上一次之后再做筛选"必须套一层(见 3.2)。

Q:首行那个 NULL 一定要处理吗?

看用途。算增长额时可以留着(NULL 就是"没有上月"),但要参与加减乘除时结果也会变 NULL,这时候用 LAG(列, 1, 0) 补 0 最省事。做展示时一般用 CASE 换成"首月无对比"更好读。

Q:自连接和 LAG 哪个更快?

数据量小的时候感觉不出来。数据量大时 LAG 通常更好,因为它只排一次队、扫一遍;自连接要把同一份数据连一遍,等于多扫一次。真正在意的场景可以两边都跑一下,用 EXPLAIN 看扫描行数再决定。

Q:MySQL 5.7 能用吗?

不能。窗口函数是 MySQL 8.0 才有的,5.7 只能回到自连接或者变量的老办法。这也是很多老项目升级到 8.0 的主要动力之一。


前一篇讲的是 WITH 多段串联(把复杂查询拆成几段),这一篇讲 LAG/LEAD(跟上一次比)。两篇连起来用,先算月度、再比环比,一整份报表就出来了。

你在做报表时,"跟上个月比"是怎么写的?自连接、子查询,还是别的办法?

相关推荐
码士集团小青1 小时前
Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?
数据库·redis·缓存
yuzhiboyouye1 小时前
com.fasterxml.jackson.annotation 里最常用高频的注解,可以按“控制字段、控制格式、控制结构、控制流程”来记
java·开发语言
太理摆烂哥1 小时前
MySQL的查询
数据库·mysql
码士集团小青1 小时前
硬核 | Redis 布隆(Bloom Filter)过滤器原理与实战
数据库·redis·缓存
苜蓿muxu1 小时前
简述 ConcurrentHashMap 和 CopyOnWriteArrayList 的区别以及各自适用场景
java·编程语言
9527!到!1 小时前
Springboot @value读取配置参数踩坑&推荐解决方案
java·spring boot·spring
十年Java程序媛2 小时前
Java 面试常问 this、super:构造方法调用顺序避坑
java·开发语言·spring boot·面试·个人开发
钱栈up2 小时前
配料秤料位查出来是负数——IoTDB存的是周期增量,我却在拿首尾相减
前端·数据库
人丰2 小时前
一次生产卡顿复盘:BI 数据抽取引发 PostgreSQL 长事务事故
数据库·c#