一句SQL完成动态分级查询
2017-04-24 13:40
295 查看
在最近的活字格项目中使用ActiveReports报表设计器设计一个报表模板时,遇到一个多级分类的难题:需要将某个部门所有销售及下属部门的销售金额汇总,因为下属级别的层次不确定,所以靠拼接子查询的方式显然是不能满足要求,经过一番实验,利用了CTE(Common Table Expression)很轻松解决了这个问题!举例:有如下的部门表
以及员工表
如果想查询所有西北区的员工(包含西北、西安、兰州),如下图所示:
因为是会话内的临时结果集,不需要去显示的声明或销毁
改写后的SQL语句可读性提高(看的明白才能修改)
给数据库引擎优化执行计划的可能性(这个不是肯定的,需要根据具体CTE的实现有关),优化了执行计划,自然地性能就能上升
为了更好的说明CTE的能力,这里附上两个例子(转自SQLite官网文档)曼德勃罗集合(Mandelbrot set)
数独问题(Sudoku)假设有类似下图的问题:
附:SQLite中CTE(WITH关键字)语法图解:WITH
cte-table-name
Select-stmt:
以及员工表
如果想查询所有西北区的员工(包含西北、西安、兰州),如下图所示:
如何用CTE的方式实现呢?
Talk is cheap. Show me the code-- 以下代码使用SQLite 3.18.0 测试通过 WITH [depts]([dept_id]) AS( SELECT [d].[dept_id] FROM [dept] [d] JOIN [employees] [e] ON [d].[dept_id] = [e].[dept_id] WHERE [e].[emp_name] = '西北-经理' UNION ALL SELECT [d].[dept_id] FROM [dept] [d] JOIN [depts] [s] ON [d].[parent_id] = [s].[dept_id] ) SELECT * FROM [employees] WHERE [dept_id] IN (SELECT [dept_id] FROM [depts]);可能有些同学对CTE(Common Table Expression)还不太熟悉,这里简单说一下,有兴趣的同学可以google或者百度,介绍很多(这里以SQLite举例): 我还是更喜欢称CTE(Common Table Expression)为“公用表变量”而不是“公用表达式”,因为从行为和使用场景上讲,CTE更多的时候是产生(分迭代或者不迭代)结果集,供其后的语句使用(查询、插入、删除或更新),如上述的例子就是一个典型的利用迭代遍历树形结构数据。CTE的优点:递归的特点使得原本需要使用临时表、存储过程才能完成的逻辑,通过SQL就可以完成,尤其针对一些树或者是图的数据模型
因为是会话内的临时结果集,不需要去显示的声明或销毁
改写后的SQL语句可读性提高(看的明白才能修改)
给数据库引擎优化执行计划的可能性(这个不是肯定的,需要根据具体CTE的实现有关),优化了执行计划,自然地性能就能上升
为了更好的说明CTE的能力,这里附上两个例子(转自SQLite官网文档)曼德勃罗集合(Mandelbrot set)
-- 以下代码使用SQLite 3.18.0 测试通过 WITH RECURSIVE xaxis(x) AS (VALUES(-[b]2.0[/b]) UNION ALL SELECT x+[b]0.05[/b] FROM xaxis WHERE x<[b]1.2[/b]), yaxis(y) AS (VALUES(-[b]1.0[/b]) UNION ALL SELECT y+[b]0.1[/b] FROM yaxis WHERE y<[b]1.0[/b]), m(iter, cx, cy, x, y) AS ( SELECT [b]0[/b], x, y, [b]0.0[/b], [b]0.0[/b] FROM xaxis, yaxis UNION ALL SELECT iter+[b]1[/b], cx, cy, x*x-y*y + cx, [b]2.0[/b]*x*y + cy FROM m WHERE (x*x + y*y) < [b]4.0[/b] AND iter<[b]28[/b] ), m2(iter, cx, cy) AS ( SELECT max(iter), cx, cy FROM m GROUP BY cx, cy ), a(t) AS ( SELECT group_concat( substr(' .+*#', [b]1[/b]+min(iter/[b]7[/b],[b]4[/b]), [b]1[/b]), '') FROM m2 GROUP BY cy ) SELECT group_concat(rtrim(t),x'0a') FROM a;运行后的结果,如下图:(使用SQLite Expert Personal 4.2 x64)
数独问题(Sudoku)假设有类似下图的问题:
-- 以下代码使用SQLite 3.18.0 测试通过 WITH RECURSIVE input(sud) AS ( VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79') ), digits(z, lp) AS ( VALUES('1', [b]1[/b]) UNION ALL SELECT CAST(lp+[b]1[/b] AS TEXT), lp+[b]1[/b] FROM digits WHERE lp<[b]9[/b] ), x(s, ind) AS ( SELECT sud, instr(sud, '.') FROM input UNION ALL SELECT substr(s, [b]1[/b], ind-[b]1[/b]) || z || substr(s, ind+[b]1[/b]), instr( substr(s, [b]1[/b], ind-[b]1[/b]) || z || substr(s, ind+[b]1[/b]), '.' ) FROM x, digits AS z WHERE ind>[b]0[/b] AND NOT EXISTS ( SELECT [b]1[/b] FROM digits AS lp WHERE z.z = substr(s, ((ind-[b]1[/b])/[b]9[/b])*[b]9[/b] + lp, [b]1[/b]) OR z.z = substr(s, ((ind-[b]1[/b])%[b]9[/b]) + (lp-[b]1[/b])*[b]9[/b] + [b]1[/b], [b]1[/b]) OR z.z = substr(s, (((ind-[b]1[/b])/[b]3[/b]) % [b]3[/b]) * [b]3[/b] + ((ind-[b]1[/b])/[b]27[/b]) * [b]27[/b] + lp + ((lp-[b]1[/b]) / [b]3[/b]) * [b]6[/b], [b]1[/b]) ) ) SELECT s FROM x WHERE ind=[b]0[/b];执行结果(结果中的数字就是对应格子中的答案)
附:SQLite中CTE(WITH关键字)语法图解:WITH
cte-table-name
Select-stmt:
总结
CTE是解决一些特定问题的利器,但了解和正确的使用是前提,在决定将已有的一些SQL重构为CTE之前,确保对已有语句有清晰的理解以及对CTE足够的学习!Good Luck~~~附件:用到的SQL脚本相关文章推荐
- 一句SQL完成动态分级查询 推荐
- 一句SQL完成动态分级查询
- 一句SQL完成动态分级查询
- 一句SQL完成动态分级查询
- 使用MyBatis(12)动态SQL 完成分页查询
- MyBatis中动态SQL语句完成多条件查询
- MyBatis中动态SQL语句完成多条件查询
- SQL动态表查询 推荐
- 动态查询SQL中变量的返回值
- sql 在存储过程中的动态查询--就是当有的查询条件为空时就不加入查询
- T-SQL,动态聚合查询
- db2 在储存过程中如何实现纯动态查询sql?
- SQL语句查询结果集中的动态修改案例(临时表+游标)
- 动态生成SQL查询条件定制表单及语句的脚本
- LINQ体验(17)——LINQ to SQL语句之动态查询
- LINQ体验(17)——LINQ to SQL语句之动态查询
- Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别
- linq to sql 的动态条件查询方法
- linq to sql的多条件动态查询(上)
- Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别