Чтобы понять поведение рекурсии (в 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')}
Thisэквивалентно операции zip
на языке, подобном Python.
Согласно подсказке @CPerkins, эту цель можно достичь с помощью оконных функций (дляSQLite> = 3.25) очень элегантно;например,
SELECT (row_number() OVER (ORDER BY x)) + 2 AS newId, x FROM tb;