使用递归语句重新编号表行

问题描述 投票:0回答:1

为了了解递归的行为(在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 infinite-loop recursive-query
1个回答
0
投票
为什么递归到达表tb的末尾时不停止?

因为这就是它的设计方式,因此非常有用。它与大多数具有某种形式的递归的语言没有什么不同,并且通常是解决某些编程问题(例如遍历目录树)的有效途径。

  • [大多数计算机编程语言通过允许函数从自己的代码中调用自身。一些功能编程语言没有定义任何循环结构,而是依赖完全基于递归来反复调用代码。可计算性理论证明这些仅递归语言是图灵完整的;他们与图灵完全命令一样强大的计算能力语言,这意味着它们可以解决与命令式语言,甚至没有迭代控制结构,例如一会儿。Recursion (computer science)

如果您使用LIMIT(SELECT tb()from tb)而不是LIMIT 6,则递归将基于表中的行数而停止。

但是,如果您要重新编号(通过在行ID上加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行,结果为:-

enter image description here

SQlite的推理是:-

递归公用表表达式提供了执行树和图的分层或递归查询,一种功能这是SQL语言无法提供的。SQL As Understood By SQLite - WITH clause

可以防止吗?

是的,您不能使用递归,因为可能有其他选择,但是与递归其他语言一样,如果您确实使用递归,则必须具有某种方法来检测何时应完成递归。使用WHERE或LIMIT子句可以简化此过程。

© www.soinside.com 2019 - 2024. All rights reserved.