WITHRECURSIVE org_hierarchy AS ( -- 初始查询:选择顶层员工 SELECT employee_id, manager_id, employee_name FROM employees WHERE manager_id ISNULL
UNIONALL
-- 递归查询:选择每个员工的下属 SELECT e.employee_id, e.manager_id, e.employee_name FROM employees e INNERJOIN org_hierarchy oh ON e.manager_id = oh.employee_id ) SELECT*FROM org_hierarchy;
另一方面,递归 CTE 使 SQL 成为了一门图灵完备的语言,因而也可以实现几乎所有算法。
模拟 for 循环
递归 CTE 最简单的算法应用就是生成一系列数字,用于模拟 for 循环:
1 2 3 4 5 6 7 8 9
WITHRECURSIVEcount(n) AS ( SELECT0 UNIONALL SELECT n +1 FROM count WHERE n <20 ) SELECT n FROM count;
阶乘计算
在公共表中的每一行维护多列数据,就可以在循环的基础上进行更多的操作,例如计算阶乘。
1 2 3 4 5 6 7 8 9
WITHRECURSIVE factorial(n, fact) AS ( SELECT1, 1 UNIONALL SELECT n +1, (n +1) * fact FROM factorial WHERE n <10 ) SELECT n, fact FROM factorial;
斐波那契数列
生成斐波那契数列时需要维护 a,b 两个变量,再加上表示循环变量的 n,每行需要三列数据。
1 2 3 4 5 6 7 8 9
WITHRECURSIVE fibonacci(n, a, b) AS ( SELECT1, 0, 1 UNIONALL SELECT n +1, b, a + b FROM fibonacci WHERE n <20 ) SELECT n, a AS fibonacci_number FROM fibonacci;
WITHRECURSIVE dfs AS ( SELECT source, destination, concat('A', '->', cast(destination as text)) AS path, 1AS level FROM edges WHERE source ='A' UNIONALL SELECT d.source, e.destination, concat(d.path, '->', e.destination) AS path, d.level +1 FROM edges e JOIN dfs d ON e.source = d.destination ) SELECT* FROM dfs;
动态规划
对于经典的硬币组合数问题,也可以用递归 CTE 来给出动态规划的算法实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
CREATE TABLE coins (valueint);
INSERT INTO coins (value) VALUES (1), (2), (5);
WITHRECURSIVE coin_combinations(amount, coin, ways) AS ( SELECT0AS amount, 0AS coin, 1AS ways UNIONALL SELECT cc.amount + c.value, c.value, cc.ways FROM coin_combinations cc JOIN coins c ON cc.amount + c.value <=10AND c.value >= cc.coin ) SELECT amount, sum(ways) AS total_ways FROM coin_combinations WHERE amount =10 GROUPBY amount;
结论
递归 CTE 极大的提高了 SQL 的表达能力,可以用于实现很多有趣的算法,不过由于递归 CTE 的执行会生成并处理大量的中间结果集,对于内存和 CPU 的要求都很高,在生产环境中还是要谨慎使用。