Photo by Lorem Ipsum
622 words
3 minutes
SQL 系列(五):复杂查询
SQL 教程
Posts in this series (10)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 rnFROM employee;方言说明
- MySQL:8.0+版本才支持窗口函数,5.x版本无此能力;
- 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 ;-- SQLServerCREATE PROC query_emp @dept_id INTASBEGIN SELECT * FROM employee WHERE dept_id = @dept_id;END-- Oracle(PL/SQL语法,完全独立)CREATE PROCEDURE query_emp(p_dept_id IN NUMBER)ISBEGIN SELECT * FROM employee WHERE dept_id=p_dept_id;END;重点:三者存储过程语法互不兼容,属于数据库私有扩展,跨库迁移成本极高。
四、触发器
监听表增删改,自动执行预设SQL,用于数据同步、审计日志。
-- MySQL触发器示例CREATE TRIGGER tri_emp_insertAFTER INSERT ON employee FOR EACH ROWBEGIN INSERT INTO emp_log(name) VALUES(NEW.name);END;方言差异
- MySQL:
NEW代表新增行,OLD代表修改/删除旧行; - Oracle:使用
:NEW、:OLD;支持行级、语句级触发器; - SQLServer:无NEW/OLD关键字,使用
INSERTED、DELETED虚拟表。
开发实践总结
- 窗口函数:新项目尽量避免兼容MySQL5.x;分组排名优先窗口函数,减少自连接;
- 递归CTE:树形数据首选方案,替代递归代码查询;
- 存储过程、触发器尽量少用:数据库绑定严重、难以维护,不利于跨数据库迁移;业务逻辑推荐放在应用代码层实现。