Как работает рекурсия? Иерархические / рекурсивные SQL запросы в базах данных SQLite: WITH RECURSIVE
Помощь проекту:
WMR: R288272666982
WMZ: Z293550531456
Яндекс.Деньги: 410011531129223
Рубрика SQLite на сайте: http://zametkinapolyah.ru/zametki-o-mysql/sqlite/
Паблик блога в ВК: https://vk.com/zametkinapolyah
Тематическая группа в ВК: https://vk.com/zametki_bd
-- Рекурсивные/иерархические SQL запросы в базах данных SQLite:
-- WITH RECURSIVE clause.
/*
Не все любят рекурсивные или как еще говорят иерархические
SQL запросы. Потому что не всем и не всегда просто понять
рекурсию, поэтому, обычно, рекурсивные запросы применяют
либо те, кто рекурсию понимают, либо те, кто не ленится
эксперементировать и пробовать и в конце концов тоже начинает
понимать рекурсию.
Здесь мы попробуем разобраться и с рекурсией и с
иерархическими SQL-запросами. SQLite, как и многие другие
СУБД - реляционная: база данных состоит из таблиц, таблицы
в свою очередь состоят из строк и столбцов, другими словами:
данные представлены двумерно.
В нашей жизни, так уж получается, что не все объекты удобно
описывать двумерной моделью: крупная международная компания
с холдингами и филиалами в различных странах, файловая система
компьютера, карьерная лестница в компании - эти данные намного
удобнее описывать иерархической моделью.
Конечно, например, структуру крупной компании можно описать
несколькими таблицами, например, таблицы стран: Китай, Россия,
США, Англия, Япония. Далее спуститься на уровень ниже
и создать таблицы регионов для страны: Москва, Иваново,
Новосибирск, Омск. Но такой подход не всегда удобен, поскольку
не всегда известна глубина вложенности.
В 1999 году международное сообщество решило эту проблему
путем добавления в стандарт (SQL-1999) рекурсивных SQL-запросов,
хотя компания Oracle это сделала немного раньше (и по Ораклу
рекурсивные запросы называются иерархическими).
*/
http://sqlite.org/lang_with.html
/*
Общий синтаксис рекурсивных запросов в SQLite выглядит следующим
образом:
WITH [RECURSIVE] cte-table-name AS (select-stmt);
Но это не всё, рекурсивный SQL запрос состоит из двух запросов SELECT,
если эта фраза в данном случае уместна:
WITH RECURSIVE
cte-table-name AS (initial-select UNION [ALL] recursive-select)
cte-select;
2 1 cte-table-name 1 2
initial-select (не должен в себя включать ORDER BY, LIMIT, OFFSET) -
точка, откуда начинается рекурсия.
recursive-select - рекурсивный запрос, который будет вполняться
до тех пор, пока условие не будет выполнено, два верхних запроса
позволяют создать наполнить нашу импровизированную таблицу данными
cte-select - позволяет работать с данными таблицы
1-1000
WITH RECURSIVE
table1(col1) AS (
VALUES(1)
UNION ALL
SELECT col1+1 FROM table1
WHERE col1(знак меньше)1000)
SELECT col1 FROM table1;
WITH RECURSIVE
table1(col1) AS (
SELECT 1
UNION ALL
SELECT col1+1 FROM table1
LIMIT 1000
)
SELECT col1 FROM table1;
table1
col1
2
3 2 1 table1 1 2 3
-- Создадим таблицу
CREATE TABLE org(
name TEXT PRIMARY KEY,
boss TEXT REFERENCES org
);
-- наполним ее данными
INSERT INTO org VALUES('Alice',NULL);
INSERT INTO org VALUES('Bob','Alice');
INSERT INTO org VALUES('Cindy','Alice');
INSERT INTO org VALUES('Dave','Bob');
INSERT INTO org VALUES('Emma','Bob');
INSERT INTO org VALUES('Fred','Cindy');
INSERT INTO org VALUES('Gail','Cindy');
SELECT * FROM org;
-- Для лучшего понимания работы рекурсии и поиска значений
-- рекомендую вам погуглить:
-- FIFO, поиск в глубину и поиск в ширину
0|Alice
1|Bob
1|Cindy
2|Dave
2|Emma
2|Fred
2|Gail
-- поиск в глубину
WITH RECURSIVE
under_alice(name,level) AS (
VALUES('Alice',0)
UNION ALL
SELECT org.name, under_alice.level+1
FROM org JOIN under_alice ON org.boss=under_alice.name
ORDER BY 2 DESC
)
SELECT level, name FROM under_alice;
Alice
...Bob
...Cindy
......Dave
......Emma
......Fred
......Gail
WITH RECURSIVE
under_alice(name,level) AS (
VALUES('Alice',0)
UNION ALL
SELECT org.name, under_alice.level+1
FROM org JOIN under_alice ON org.boss=under_alice.name
ORDER BY 2 DESC
)
SELECT substr('..........',1,level*3) || name FROM under_alice;