базовый рекурсивный запрос на sqlite3? - PullRequest
29 голосов
/ 17 сентября 2011

У меня есть простая таблица sqlite3, которая выглядит следующим образом:

Table: Part
Part    SuperPart
wk0Z    wk00
wk06    wk02
wk07    wk02
eZ01    eZ00
eZ02    eZ00
eZ03    eZ01
eZ04    eZ01

Мне нужно выполнить рекурсивный запрос, чтобы найти все пары данного SuperPart со всеми его частями.Итак, допустим, что у меня есть eZ00.eZ00 - это супер часть eZ01, а eZ01 - это супер часть eZ03.Результат должен включать не только пары (eZ00, eZ01) и (eZ01 и eZ03), но также должен включать пару (eZ00, eZ03).

Я знаю, что существуют другие способы определения таблицы, но яУ меня нет выбора здесь.Я знаю, что могу использовать несколько союзов, если знаю глубину своего дерева, но я не всегда буду знать, какую глубину я хочу достичь.Было бы полезно иметь что-то вроде WITH RECURSIVE или даже просто WITH (,,) AS x, но то, что я искал, невозможно в sqlite, верно?

Есть ли способ сделать это рекурсивнымзапрос в sqlite3?

ОБНОВЛЕНИЕ:

Когда этот вопрос был задан, SQLite не поддерживал рекурсивные запросы, но , как указано @ lunicon SQLite теперь поддерживает рекурсивный CTE, поскольку 3.8.3 sqlite.org / lang_with.html

Ответы [ 5 ]

30 голосов
/ 17 сентября 2011

Если вам повезло использовать SQLite 3.8.3 или выше , то у вас есть доступ к рекурсивным и нерекурсивным CTE с использованием WITH :

enter image description here

Спасибо lunicon за то, что сообщили нам об этом обновлении SQLite.


В версиях до 3.8.3 SQLite не поддерживал рекурсивные CTE (или вообще CTE), поэтому в SQLite не было WITH. Поскольку вы не знаете, насколько глубоко это происходит, вы не можете использовать стандартный трюк JOIN, чтобы подделать рекурсивный CTE. Вы должны сделать это трудным путем и реализовать рекурсию в своем клиентском коде:

  • Захватить начальную строку и идентификаторы подраздела.
  • Захватить строки и идентификаторы частей для частей.
  • Повторяйте, пока ничего не вернется.
6 голосов
/ 06 марта 2014

В этом SQLite Release 3.8.3 2014-02-03 была добавлена ​​поддержка CTE.Вот документация предложение WITH Пример:

WITH RECURSIVE
cnt(x) AS (
 SELECT 1
 UNION ALL
 SELECT x+1 FROM cnt
  LIMIT 1000000
)
SELECT x FROM cnt;
4 голосов
/ 14 июля 2013

есть взлом http://dje.me/2011/03/26/sqlite-data-trees.html

-- A method for storing and retrieving hierarchical data in sqlite3
-- by using a trigger and a temporary table.
-- I needed this but had trouble finding information on it.

-- This is for sqlite3, it mostly won't work on anything else, however 
-- most databases have better ways to do this anyway.

PRAGMA recursive_triggers = TRUE; -- This is not possible before 3.6.18

-- When creating the Node table either use a primary key or some other 
-- identifier which the child node can reference.

CREATE TABLE Node (id INTEGER PRIMARY KEY, parent INTEGER, 
    label VARCHAR(16));

INSERT INTO Node (parent, label) VALUES(NULL, "root");
INSERT INTO Node (parent, label) VALUES(1, "a");
INSERT INTO Node (parent, label) VALUES(2, "b");
INSERT INTO Node (parent, label) VALUES(3, "c1");
INSERT INTO Node (parent, label) VALUES(3, "c2");

-- Create the temp table, note that node is not a primary key
-- which insures the order of the results when Node records are
-- inserted out of order

CREATE TEMP TABLE Path (node INTEGER, parent INTEGER, 
    label VARCHAR(16));

CREATE TRIGGER find_path AFTER INSERT ON Path BEGIN
    INSERT INTO Path SELECT Node.* FROM Node WHERE 
        Node.id = new.parent;
END;


-- The flaw here is that label must be unique, so when creating
-- the table there must be a unique reference for selection
-- This insert sets off the trigger find_path

INSERT INTO Path SELECT * FROM Node WHERE label = "c2";

-- Return the hierarchy in order from "root" to "c2"
SELECT * FROM Path ORDER BY node ASC;

DROP TABLE Path; -- Important if you are staying connected


-- To test this run:
-- sqlite3 -init tree.sql tree.db
3 голосов
/ 08 февраля 2018

На основании образцов, найденных в sqlite с документацией , запрос

DROP TABLE IF EXISTS parts;
CREATE TABLE parts (part, superpart);
INSERT INTO parts VALUES("wk0Z", "wk00");
INSERT INTO parts VALUES("wk06", "wk02");
INSERT INTO parts VALUES("wk07", "wk02");
INSERT INTO parts VALUES("eZ01", "eZ00");
INSERT INTO parts VALUES("eZ02", "eZ00");
INSERT INTO parts VALUES("eZ03", "eZ01");
INSERT INTO parts VALUES("eZ04", "eZ01");

WITH RECURSIVE
  under_part(parent,part,level) AS (
     VALUES('?', 'eZ00', 0)
     UNION ALL
     SELECT parts.superpart, parts.part, under_part.level+1 
        FROM parts, under_part
     WHERE parts.superpart=under_part.part
  )
  SELECT SUBSTR('..........',1,level*3) || "(" || parent || ", " || part || ")" FROM under_part
  ;

будет выводить

  (?, eZ00)
  ...(eZ00, eZ01)
  ...(eZ00, eZ02)
  ......(eZ01, eZ03)
  ......(eZ01, eZ04)

как ожидалось

исходная запись рекурсивной таблицы может быть заменена на

VALUES ((SELECT superpart FROM parts WHERE part='eZ00'), 'eZ00', 0)

для того, чтобы получить также родителя начальной суперпартии, хотя в этом случае вообще нет родителя.

2 голосов
/ 12 мая 2018

Это самый простой запрос, о котором я мог подумать, он генерирует серию, в которой мы начинаем с 1,2 и продолжаем добавлять 1, пока не достигнем 20. Не очень полезный, но немного поработав с этим, вы сможете построить большесложные рекурсивные

Самый простой ряд

WITH b(x,y) AS 
(
    SELECT 1,2 
    UNION ALL 
    SELECT x+ 1, y + 1 
    FROM b 
    WHERE x < 20
) SELECT * FROM b;

Печать

1|2
2|3
3|4
4|5
5|6
6|7
7|8
8|9
9|10
10|11
11|12
12|13
13|14
14|15
15|16
16|17
17|18
18|19
19|20
20|21

Вот еще один простой пример, который генерирует числа Фибоначчи, с которых мы начинаемa = 0, b = 1, а затем перейдите к a = b, b = a + b, как если бы вы делали это на любом языке программирования

ряд Фибоначчи

WITH b(x,y) AS 
(
    SELECT 0,1 
    UNION ALL 
    SELECT y, x + y 
    FROM b 
    WHERE x < 10000
) select * FROM b;

Печать

0|1
1|1
1|2
2|3
3|5
5|8
8|13
13|21
21|34
34|55
55|89
89|144
144|233
233|377
377|610
610|987
987|1597
1597|2584
2584|4181
4181|6765
6765|10946
10946|17711
...