SQL выбирает потомков строки - PullRequest
4 голосов
/ 30 апреля 2010

Предположим, что древовидная структура реализована в SQL следующим образом:

CREATE TABLE nodes (
    id INTEGER PRIMARY KEY,
    parent INTEGER -- references nodes(id)
);

Хотя в этом представлении могут быть созданы циклы, давайте предположим, что мы никогда этого не допустим. В таблице будет храниться только коллекция корней (записей, где parent равен нулю) и их потомков.

Цель состоит в том, чтобы, учитывая идентификатор узла в таблице, найти все узлы, которые являются его потомками.

A является потомком B , если родительский номер A равен B или A ' s родитель является потомком B . Обратите внимание на рекурсивное определение.

Вот некоторые примеры данных:

INSERT INTO nodes VALUES (1, NULL);
INSERT INTO nodes VALUES (2, 1);
INSERT INTO nodes VALUES (3, 2);
INSERT INTO nodes VALUES (4, 3);
INSERT INTO nodes VALUES (5, 3);
INSERT INTO nodes VALUES (6, 2);

, что представляет:

1
`-- 2
    |-- 3
    |   |-- 4
    |   `-- 5
    |
    `-- 6

Мы можем выбрать (непосредственных) потомков 1, выполнив это:

SELECT a.* FROM nodes AS a WHERE parent=1;

Мы можем выбрать детей и внуков 1, выполнив следующие действия:

SELECT a.* FROM nodes AS a WHERE parent=1
UNION ALL
SELECT b.* FROM nodes AS a, nodes AS b WHERE a.parent=1 AND b.parent=a.id;

Мы можем выбрать детей, внуков и правнуков 1, выполнив следующие действия:

SELECT a.* FROM nodes AS a WHERE parent=1
UNION ALL
SELECT b.* FROM nodes AS a, nodes AS b WHERE a.parent=1 AND b.parent=a.id
UNION ALL
SELECT c.* FROM nodes AS a, nodes AS b, nodes AS c WHERE a.parent=1 AND b.parent=a.id AND c.parent=b.id;

Как построить запрос, который получает всех потомков узла 1, а не тех, которые находятся на фиксированной глубине? Кажется, мне нужно создать рекурсивный запрос или что-то в этом роде.

Я хотел бы знать, возможен ли такой запрос с использованием SQLite. Однако, если для этого типа запросов требуются функции, недоступные в SQLite, мне интересно знать, можно ли это сделать в других базах данных SQL.

Ответы [ 3 ]

8 голосов
/ 30 апреля 2010

Некоторые базы данных допускают использование рекурсивных общих табличных выражений, но не SQLLite.

Вы можете изменить определение таблицы. С такой таблицей легко запросить всех потомков 1:

id (varchar)
--------------
001
001002
001002003
001002003004
001002003005
001002006

Это позволяет запрашивать всех потомков 1, например:

select * from YourTable where id like '001%'

Звучит немного странно, но на практике работает очень хорошо.

6 голосов
/ 30 апреля 2010

То, как вы настроили свою схему, на самом деле не очень хорошо подходит для реляционной модели (как вы обнаружили). Но есть другой способ, который поначалу может быть не так очевиден, но гораздо более гибок. Она называется «моделью вложенного множества» и просто структурирует вещи немного по-другому.

Управление иерархическими данными в MySQL (см. Раздел «Модель вложенного набора») показывает, как это сделать в MySQL, но его следует довольно легко перевести на SQLite. Я не буду вдаваться в подробности, потому что эта статья на самом деле довольно длинная, но она должна дать вам все, что нужно для начала работы.

1 голос
/ 30 апреля 2010

Oracle имеет синтаксис CONNECT BY, который можно использовать для этого, но, конечно, он специфичен для Oracle.

...