Перенумеровать строки таблицы с помощью рекурсивного оператора - PullRequest
0 голосов
/ 11 ноября 2019

Чтобы понять поведение рекурсии (в 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;

1 Ответ

0 голосов
/ 12 ноября 2019

Почему рекурсия не останавливается, когда она достигает конца таблицы tb?

Потому что так оно и задумано, и это чрезвычайно полезно. Он мало отличается от большинства языков, в которых есть некоторая форма рекурсии, и зачастую является эффективным и действенным способом решения некоторых проблем программирования, таких как обход дерева каталогов.

  • БольшинствоЯзыки программирования поддерживают рекурсию, позволяя функции вызывать себя из своего собственного кода. Некоторые функциональные языки программирования не определяют циклические конструкции, а полагаются исключительно на рекурсию для многократного вызова кода. Теория вычислимости доказывает, что эти рекурсивные языки являются полными по Тьюрингу;они обладают такими же вычислительными возможностями, что и полные императивные языки Тьюринга, что означает, что они могут решать те же проблемы, что и императивные языки, даже без итеративных структур управления, таких как while и for. Рекурсия (информатика)

Если вы используете LIMIT (SELECT count () FROM tb) вместо LIMIT 6, то рекурсия будет остановлена ​​на основе количества строк в таблице.

Однако, если вы хотите изменить нумерацию (добавив 1 к rowid), вы бы посмотрели что-то более похожее на: -

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 и что строки dw были удалены) приводят к: -

enter image description here

Рассуждения SQliteis: -

Рекурсивные общие табличные выражения предоставляют возможность выполнять иерархические или рекурсивные запросы деревьев и графов, что недоступно в языке SQL. SQL в понимании SQLite - предложение WITH

Можно ли это предотвратить?

Да, вы не можете использовать рекурсию, поскольку возможны альтернативы, но как и в случае с рекурсиейна других языках, если вы используете рекурсию, вам нужно будет определить, когда рекурсия должна закончиться. Этому способствует использование предложения WHERE или LIMIT.

...