622 words
3 minutes
SQL 系列(五):复杂查询

AI 智能总结

数据库 5 复杂查询#

基础SQL满足简单业务,窗口函数、递归CTE、存储过程、触发器是处理复杂统计、树形数据、数据库内逻辑的核心能力,不同数据库支持版本、语法存在明显区别。

一、窗口函数 row_number()#

窗口函数属于SQL标准语法,用于分组内排序、排名,不压缩行数(区别于GROUP BY聚合)
常用:ROW_NUMBER()、RANK()、DENSE_RANK()

-- 通用语法:按部门分组,薪资降序排名
SELECT
name,dept,salary,
ROW_NUMBER() OVER(PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employee;

方言说明#

  1. MySQL:8.0+版本才支持窗口函数,5.x版本无此能力;
  2. Oracle、SQLServer:长久原生支持,兼容性更好。

二、CTE 公共表达式 & 递归CTE#

CTE简化多层子查询,递归CTE主要用于树形结构(组织架构、层级菜单)

-- 普通CTE(三库通用基础写法)
WITH emp_cte AS (
SELECT name,salary FROM employee WHERE salary>5000
)
SELECT * FROM emp_cte;
-- 递归CTE简易模板(树形查询)
WITH RECURSIVE tree AS (
SELECT id,name,parent_id FROM dept WHERE parent_id=0
UNION ALL
SELECT d.id,d.name,d.parent_id FROM dept d INNER JOIN tree t ON d.parent_id=t.id
)
SELECT * FROM tree;

方言差异#

  • MySQL:递归CTE必须加关键字 RECURSIVE;
  • Oracle、SQLServer:递归CTE不需要RECURSIVE;
  • Oracle 11g早期版本递归CTE支持有限,建议12c以上使用。

三、存储过程#

将SQL逻辑封装在数据库端,重复调用。

-- MySQL示例
DELIMITER //
CREATE PROCEDURE query_emp(IN dept_id INT)
BEGIN
SELECT * FROM employee WHERE dept_id = dept_id;
END //
DELIMITER ;
-- SQLServer
CREATE PROC query_emp @dept_id INT
AS
BEGIN
SELECT * FROM employee WHERE dept_id = @dept_id;
END
-- Oracle(PL/SQL语法,完全独立)
CREATE PROCEDURE query_emp(p_dept_id IN NUMBER)
IS
BEGIN
SELECT * FROM employee WHERE dept_id=p_dept_id;
END;

重点:三者存储过程语法互不兼容,属于数据库私有扩展,跨库迁移成本极高。

四、触发器#

监听表增删改,自动执行预设SQL,用于数据同步、审计日志。

-- MySQL触发器示例
CREATE TRIGGER tri_emp_insert
AFTER INSERT ON employee FOR EACH ROW
BEGIN
INSERT INTO emp_log(name) VALUES(NEW.name);
END;

方言差异#

  1. MySQL:NEW代表新增行,OLD代表修改/删除旧行;
  2. Oracle:使用:NEW、:OLD;支持行级、语句级触发器;
  3. SQLServer:无NEW/OLD关键字,使用INSERTED、DELETED虚拟表。

开发实践总结#

  1. 窗口函数:新项目尽量避免兼容MySQL5.x;分组排名优先窗口函数,减少自连接;
  2. 递归CTE:树形数据首选方案,替代递归代码查询;
  3. 存储过程、触发器尽量少用:数据库绑定严重、难以维护,不利于跨数据库迁移;业务逻辑推荐放在应用代码层实现。
SQL 系列(五):复杂查询
https://fuwari.vercel.app/posts/sql-series/sql-05-complex-queries/
Author
Zero02
Published at
2025-04-29
License
CC BY-NC-SA 4.0