如何在SQL中结合窗口函数和CTE公用表表达式优化复杂逻辑?

云浩小哥_7035

云浩小哥_7035

2026-06-20

740人浏览

原创

cte应先过滤再窗口计算,避免全表扫描;postgresql需显式控制物化,mysql 8.0链式cte更安全。

如何在sql中结合窗口函数和cte公用表表达式优化复杂逻辑?

窗口函数和CTE不是“搭配着用就一定更快”,而是要按逻辑阶段拆分+按计算依赖选执行策略——否则容易在 PostgreSQL 里掉进物化陷阱,在 MySQL 8.0 中错过索引下推机会。

CTE 先过滤再窗口:避免全表扫描

很多同学一上来就写 WITH ranked AS (SELECT ..., ROW_NUMBER() OVER (...)),结果发现慢得离谱。问题常出在没把高选择性过滤提前到 CTE 内部。

典型错误:

WITH ranked AS (
  SELECT id, user_id, amount, created_at,
         ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
  FROM orders
)
SELECT * FROM ranked WHERE rn = 1 AND created_at >= '2026-01-01';

这个写法会让 PostgreSQL 先算完整个 orders 表的窗口,再过滤日期——哪怕只有 0.1% 的数据满足时间条件。

正确做法是把强过滤条件塞进 CTE:

  • 把 WHERE created_at >= '2026-01-01' 移入 CTE 内部
  • 确保 (user_id, created_at) 有联合索引(升序/降序需匹配 ORDER BY)
  • 若还需返回其他字段(如 product_name),考虑加 INCLUDE 覆盖索引

PostgreSQL 中 CTE 物化必须主动干预

PostgreSQL 默认把每个 CTE 当作物化临时表处理,即使你只引用一次。这在窗口函数嵌套时特别危险——比如先用 CTE 算聚合,再在主查询里对它开窗口,等于扫两遍磁盘。

现象:EXPLAIN ANALYZE 显示多次 Seq Scan on orders 或明显 I/O 等待。

Komo Search
Komo Search

一款AI工具,主要用于Komo Search 是一个生成式AI驱动的搜索引擎,适合需要提升相关任务效率的用户。

下载

解决方法只有两个:

  • 用 MATERIALIZED 或 NOT MATERIALIZED 显式提示(PG 12+):WITH ranked AS MATERIALIZED (SELECT ...)
  • 更推荐:直接改写成子查询,绕过 CTE —— 尤其当 CTE 只被引用一次且无递归时,优化器更容易内联

注意:NOT MATERIALIZED 不保证不物化,只是告诉优化器“请尽量内联”,最终是否生效仍取决于代价估算。

MySQL 8.0 的 CTE + 窗口函数可安全链式调用

MySQL 8.0 对 CTE 的实现更接近“语法糖”,默认不强制物化(除非递归或显式声明 WITH RECURSIVE),所以链式 CTE 更安全:

WITH filtered AS (
  SELECT user_id, SUM(amount) AS total_spent
  FROM orders 
  WHERE status = 'completed' 
  GROUP BY user_id
),
ranked AS (
  SELECT *, RANK() OVER (ORDER BY total_spent DESC) AS rank_no
  FROM filtered
)
SELECT * FROM ranked WHERE rank_no <p>这种写法在 MySQL 中基本等价于单层子查询展开,不会额外落盘。但要注意:</p>
  • RANK() 和 ROW_NUMBER() 在并列值处理上不同,业务语义要核对清楚
  • 如果 filtered 结果集很大(比如百万行),ranked 的 ORDER BY 仍会触发 filesort,需要确认 total_spent 是否有索引支持
  • MySQL 不支持 ROWS BETWEEN 的逆序帧(如 ROWS BETWEEN CURRENT ROW AND 6 FOLLOWING),写移动平均时得反向排序再取

递归 CTE + 窗口函数慎用:层级深度与内存限制硬碰硬

递归 CTE 本身就会吃内存,再叠一层窗口函数(比如在每层算 SUM() OVER (PARTITION BY level)),很容易触发 ERROR: stack depth limit exceeded 或 OOM kill。

真实踩坑点:

  • PostgreSQL 默认 work_mem 是 4MB,递归深度超 200 层就可能崩;加 SET work_mem = '64MB' 仅临时缓解,不治本
  • 窗口函数的 PARTITION BY 若含递归生成的 level 字段,会导致每个层级单独排序,复杂度从 O(n) 变成 O(n × depth)
  • 替代方案:先用递归 CTE 输出扁平结果,再用外部程序或物化视图预计算层级统计,而非实时窗口

真正要记住的是:CTE 解决的是“怎么写清楚”,窗口函数解决的是“怎么算得对”,而性能瓶颈往往卡在“数据库到底扫了几遍数据”——这个数字,得看 EXPLAIN,不能靠感觉。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

mysql 8.0

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

3943

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

851

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1029

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5781

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2723

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5760

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7621

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1030

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

912

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习