子查询实现同比需用where锁定去年同期数据并做减法和除法,环比则用日期函数动态计算上期时间;须防多行、null及性能问题,窗口函数lag更优。

子查询里怎么写同比:用上一年同期数据做减法和除法
同比是今年某期和上年同期的比值,核心是“跨年拉数据”。子查询最直接的方式是用 WHERE 锁定去年同一时间范围(比如去年本月),再关联或嵌套计算。注意日期字段必须可比较,DATE 或 DATETIME 类型优先;如果只有年月字符串(如 '2023-06'),得先转成日期或用字符串拼接构造去年值。
- 直接在
SELECT里写标量子查询:适合单行结果,比如查 2024-06 的销售额同比,子查询返回 2023-06 的销售额 - 用
LEFT JOIN关联子查询结果:更适合多行报表,避免重复执行子查询,性能更稳 - 别忘了处理除零:分母可能为 0,加
CASE WHEN ... THEN ... ELSE 0 END包一层 - 示例(MySQL):
SELECT curr.month, curr.sales AS curr_sales, prev.sales AS prev_year_sales, ROUND((curr.sales - prev.sales) / NULLIF(prev.sales, 0), 4) AS yoy_rate FROM sales curr LEFT JOIN ( SELECT month, sales FROM sales WHERE month = DATE_SUB('2024-06', INTERVAL 1 YEAR) ) prev ON curr.month = DATE_SUB(prev.month, INTERVAL 1 YEAR);
环比怎么用子查询实现:和上一周期比,关键是时间偏移逻辑
环比是相邻周期对比(如本月 vs 上月),子查询重点在“动态算出上期时间”。不能硬写 '2024-05',得用函数推导——不同数据库语法差异大:DATE_SUB()(MySQL)、ADD_MONTHS()(Oracle)、INTERVAL '1 month'(PostgreSQL)。子查询返回上期值后,主查询做差值或比率。
- 时间字段类型决定写法:如果是
DATE,用日期函数最安全;如果是INT年月(如202406),可用数学运算:(year_month / 100 * 100) + (year_month % 100) - 1,但要处理跨年(如 202401 → 202312) - 子查询必须带
WHERE精确匹配上期,否则可能返回多行导致错误:Subquery returns more than 1 row - 示例(PostgreSQL):
SELECT t1.month, t1.sales, (t1.sales - COALESCE(t2.sales, 0)) / NULLIF(t2.sales, 0) AS mom_rate FROM sales t1 LEFT JOIN sales t2 ON t2.month = t1.month - INTERVAL '1 month';
为什么子查询容易报错:NULL、多行、性能三座大山
子查询用于同比/环比时,最常见的崩溃点不是逻辑,而是运行时异常。
- 标量子查询返回多行 → 报错
Subquery returns more than 1 row:确保子查询有唯一约束(如加LIMIT 1或精确WHERE条件) - 没数据时子查询返回
NULL→ 主查询计算变成NULL,除法直接失效:一律用COALESCE()或NULLIF()拦截 - 多层嵌套子查询拖慢性能:尤其在大表上,
EXPLAIN会看到全表扫描。改用JOIN或窗口函数(LAG())通常更快 - 日期函数不兼容:比如在 SQL Server 里用
DATEADD(MONTH, -1, ...),别抄 MySQL 的DATE_SUB写法
替代方案比子查询更稳:窗口函数才是同比环比的常规解法
子查询能跑通,不代表该这么写。真正生产环境里,LAG() 和 LEAD() 是更简洁、更高效的选择,尤其当数据按时间有序且需整列计算时。
-
LAG(sales, 1) OVER (ORDER BY month)直接拿到上月值,不用子查询关联 - 同比需要跨年,用
LAG(sales, 12) OVER (ORDER BY year_month)(假设按月连续) - 注意排序必须严格:如果月份有缺失(比如 202312 后直接 202402),
LAG会取前一行而非逻辑上“上月”,这时还是得回退到子查询或 CTE 补齐时间维度 - 不是所有旧版数据库支持窗口函数(如 MySQL INDEX,不然每次子查询都扫全表
实际写的时候,先看数据库版本和数据连续性。能用 LAG 就别硬套子查询;非要子查询,就盯紧 NULL、多行、日期函数这三处,错一个就全崩。











