When I used ActiveReports report designer to design a report template in a recent movable type project , I encountered a multi-level classification problem: I need to summarize all sales of a department and the sales amount of subordinate departments, because the level of subordinate levels is uncertain, Therefore, the method of splicing sub-queries obviously cannot meet the requirements. After some experiments, CTE (Common Table Expression) is used to solve this problem easily!
Example: There is the following department table
and employee table
If you want to query all employees in the Northwest District (including Northwest, Xi'an, Lanzhou), as shown below:
How to use CTE to achieve it?
Talk is cheap. Show me the code
-- The following code tests using SQLite 3.18.0 to pass 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]);
Some students may not be familiar with CTE (Common Table Expression), here is a brief introduction, interested students can google or Baidu, introduce a lot (here takes SQLite as an example):
I still prefer to call CTE (Common Table Expression) a "common table variable" rather than a "common expression", because in terms of behavior and usage scenarios, CTEs are more often used to generate (iterative or non-iterative) result sets , for subsequent statements (query, insert, delete or update), such as the above example is a typical iterative traversal of tree-structured data.
Advantages of CTE:
- The recursive feature makes the logic that originally needed to be completed by using temporary tables and stored procedures can be completed through SQL, especially for some tree or graph data models
- Because it is a temporary result set within the session, there is no need to explicitly declare or destroy
- The readability of the rewritten SQL statement is improved (you can only modify it if you understand it)
- The possibility of optimizing the execution plan for the database engine (this is not certain, it needs to be related to the implementation of the specific CTE), optimize the execution plan, and the performance will naturally increase
In order to better illustrate the capabilities of CTE, here are two examples (transferred from the SQLite official website documentation)
Mandelbrot set
--The following code is tested with SQLite 3.18.0 WITH RECURSIVE xaxis(x) AS (VALUES(-2.0) UNION ALL SELECT x+0.05 FROM xaxis WHERE x<1.2), yaxis(y) AS (VALUES(-1.0) UNION ALL SELECT y+0.1 FROM yaxis WHERE y<1.0), m(iter, cx, cy, x, y) AS ( SELECT 0, x, y, 0.0, 0.0 FROM xaxis, yaxis UNION ALL SELECT iter+1, cx, cy, x*x-y*y + cx, 2.0*x*y + cy FROM m WHERE (x*x + y*y) < 4.0 AND iter<28 ), m2(iter, cx, cy) AS ( SELECT max(iter), cx, cy FROM m GROUP BY cx, cy ), a(t) AS ( SELECT group_concat( substr(' .+*#', 1+min(iter/7,4), 1), '') 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', 1) UNION ALL SELECT CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9 ), x(s, ind) AS ( SELECT sud, instr(sud, '.') FROM input UNION ALL SELECT substr(s, 1, ind-1) || z || substr(s, ind+1), instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' ) FROM x, digits AS z WHERE ind>0 AND NOT EXISTS ( SELECT 1 FROM digits AS lp WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1) OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1) OR z.z = substr(s, (((ind-1)/3) % 3) * 3 + ((ind-1)/27) * 27 + lp + ((lp-1) / 3) * 6, 1) ) ) SELECT s FROM x WHERE ind=0;
执行结果(结果中的数字就是对应格子中的答案)
附:SQLite中CTE(WITH关键字)语法图解:
WITH
cte-table-name
Select-stmt:
总结
CTE是解决一些特定问题的利器,但了解和正确的使用是前提,在决定将已有的一些SQL重构为CTE之前,确保对已有语句有清晰的理解以及对CTE足够的学习!Good Luck~~~
附件:用到的SQL脚本