SQL 高级功能与实战

9次阅读

窗口函数无结果需先检查 partition by 与 order by 组合逻辑:partition by 决定分组,缺失则全表为一组;order by 决定窗口内顺序,缺失时 postgresql 允许但结果不可靠、mysql 8.0+ 直接报错;稳定排名应加二级排序如 order by created_at, id。

SQL 高级功能与实战

窗口函数写完没结果?先检查 PARTITION BYORDER BY 的组合逻辑

窗口函数不是“加个 OVER() 就能跑”,最常见的情况是返回全 NULL 或结果和预期严重不符。根本原因往往出在分区与排序的配合上:比如用 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at) 统计用户行为序号,但 created_at 有大量重复值且未加二级排序,会导致序号分配不稳定甚至跳变。

  • PARTITION BY 决定“分几组”,不写就相当于全表一个窗口,容易误算累计值
  • ORDER BY 在窗口内决定计算顺序,缺失时多数数据库(如 PostgreSQL)允许但结果不可靠;MySQL 8.0+ 则直接报错 Window 'w' lacks an ORDER BY clause
  • 需要稳定排名时,建议补全二级排序,例如 ORDER BY created_at, id,避免因主键缺失导致相同时间戳下行为不可预测

CTE 递归查树形结构卡死?注意循环引用和层级限制

WITH RECURSIVE 查组织架构或评论回复链时,看似语法对了,一执行就超时或返回极少量数据,大概率是递归没终止。SQL 标准不自动检测环路,PostgreSQL 和 SQL Server 需手动设 MAXRECURSION(后者用 OPTION (MAXRECURSION n)),而 MySQL 8.0 默认只跑 1000 层,超出就中断并报错 Recursive query aborted after 1000 iterations

  • 必须在递归 CTE 的 WHERE 条件中显式排除自引用,比如 parent_id != id,否则一条坏数据就能拖垮整个查询
  • depth 计数列,并在递归分支里限制 depth,比依赖全局配置更可控
  • 如果表里存在双向父子关系(A 是 B 父节点,B 又是 A 父节点),索引也救不了——得先清洗数据,再查

JSON_EXTRACT 返回 NULL?别急着改数据,先看路径语法和字符集

MySQL 的 JSON_EXTRACT 或 PostgreSQL 的 ->> 拿不到值,90% 不是 JSON 本身有问题,而是路径写法或字段类型踩了坑。比如字段存的是 '{"name": " 张三 "}'(带单引号字符串),但类型是 VARCHAR 而非 JSON,MySQL 就会静默失败,返回 NULL 而不报错。

  • MySQL 中用 JSON_VALID(col) 先确认字段内容是否真为合法 JSON,别信业务代码写的“已转 JSON”
  • 路径里含中文或特殊字符时,MySQL 要求用双美元符写法:JSON_EXTRACT(data, '$." 用户信息 ". 姓名'),单引号或漏掉点号都会失效
  • PostgreSQL 对大小写敏感,data->>'Name'data->>'name' 是两个世界,别靠猜

UPDATE 关联子查询慢到无法接受?优先考虑 JOIN 写法而非嵌套 (SELECT ……)

UPDATE t1 SET x = (SELECT y FROM t2 WHERE t2.id = t1.ref_id) 在数据量过万后明显变慢,不是因为子查询逻辑错,而是多数数据库(尤其 MySQL 5.7 及以前)会对 t1 每行都执行一次子查询,O(n²) 复杂度直接拉垮。

  • MySQL 改成 UPDATE t1 JOIN t2 ON t1.ref_id = t2.id SET t1.x = t2.y,执行计划立刻从“dependent subquery”变成“ref”
  • PostgreSQL 不支持 UPDATE + JOIN 语法,得用 UPDATE t1 SET x = t2.y FROM t2 WHERE t1.ref_id = t2.id,FROM 子句位置不能错
  • 无论哪种写法,确保 t2.idt1.ref_id 都有索引,否则 JOIN 也白搭

复杂点在于:窗口函数的排序稳定性、递归 CTE 的环路防御、JSON 路径的引号嵌套规则、UPDATE 关联的引擎差异——这些地方不写错,功能才真正可用。

text=ZqhQzanResources