要了解递归的行为(在SQLite中),我尝试使用以下语句用递归语句对表的行重新编号:
我们创建一个示例表,
CREATE TABLE tb
(x TEXT(1) PRIMARY KEY);
INSERT INTO tb
VALUES ('a'), ('b'), ('c');
并重新编号从2开始的行,
SELECT tb.x as x, tb.rowid + 1 as idx from tb;
/* yields expected:
a|2
b|3
c|4
*/
尝试对递归WITH
(忽略ROWID
)执行相同操作会导致分歧-在这里,我添加了LIMIT 6
以防止分歧:
WITH RECURSIVE
newtb AS (
SELECT tb.x, 2 AS idx FROM tb
UNION ALL
SELECT tb.x, newtb.idx + 1
FROM tb, newtb
LIMIT 6 -- only to prevent divergence!
)
SELECT * FROM newtb;
/* yields indefinitely:
a|2
b|2
c|2
a|3
b|3
c|3
...
*/
为什么递归到达表tb
的末尾时并没有停止?可以预防吗?
请注意,可以将问题重新表述为如何在SQLite中产生以下过程伪代码的结果(事不宜迟):
tb := {'a', 'b', 'c'};
num := {1, 2, 3};
result := {}; # initialize an empty table
for i in {1, ..., length(tb)} # assume index starts from 1
append tuple(num[i], tb[i]) to result;
end for
# result will be {(1, 'a'), (2, 'b'), (3, 'c')}
这等效于Python之类的zip
操作。
根据@CPerkins的提示,可以非常优雅地通过window functions(对于SQLite> = 3.25)实现此目标;例如,
SELECT (row_number() OVER (ORDER BY x)) + 2 AS newId, x FROM tb;
答案 0 :(得分:0)
为什么递归到达表tb的末尾时不停止?
因为这就是它的设计方式,所以它非常有用。它与大多数具有某种形式的递归的语言几乎没有什么不同,并且通常是解决某些编程问题(例如遍历目录树)的有效途径。
大多数计算机编程语言都通过允许 函数从自己的代码中调用自身。一些功能 编程语言没有定义任何循环结构,而是依赖 完全基于递归来反复调用代码。可计算性理论 证明这些仅递归语言是图灵完整的;他们 与图灵完全命令一样强大的计算能力 语言,这意味着它们可以解决与 命令式语言,甚至没有迭代控制结构,例如 时间和时间。Recursion (computer science)
如果您使用LIMIT(SELECT tb()from tb)而不是LIMIT 6,则基于表中的行数,递归将停止。
但是,如果您要重新编号(通过在rowid上加1),那么您将看到的像是:-
WITH RECURSIVE
cte(idx,newidx) AS (
SELECT (SELECT max(rowid) FROM tb),(SELECT max(rowid) FROM tb) +1
UNION ALL
SELECT
idx-1, newidx-1 FROM cte
WHERE idx > 0
)
SELECT (SELECT x FROM tb WHERE tb.rowid = cte.idx) AS x, newidx, idx AS original FROM cte WHERE x IS NOT NULL;
这将(假设tb包含具有a,b和c .... X,Y和Z的行,并且删除了d-w行)导致:-
SQlite的推理是:-
递归公用表表达式提供了执行 树和图的分层或递归查询,一种功能 这是SQL语言无法提供的。 SQL As Understood By SQLite - WITH clause
这可以预防吗?
是的,您不能使用递归,因为可能有其他选择,但是与递归其他语言一样,如果您确实使用递归,则必须具有某种方法来检测何时应递归。使用WHERE或LIMIT子句可以简化这一过程。