2010-04-30 2 views
4

Пусть древовидная структура реализуется в SQL, как это:SQL выберите потомков строки

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

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

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

является потомком B если либо 'родитель ы является В или ' родитель ы является потомком 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.

ответ

8

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

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

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

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

select * from YourTable where id like '001%' 

Это звучит немного эксцентричный, но работает очень хорошо на практике.

+0

Определенно дурацкая, определенно удивительная! Кроме того, как насчет разметки идентификаторов с помощью косой черты? (например, «1/2/3/4») –

+0

Мне это нравится, это намного проще, чем мое предложение, хотя, возможно, и не столь универсальное. Я думаю, это зависит от ваших реальных потребностей, хотя ... –

+0

его в основном URL-адрес :) вы могли бы просто использовать URL-адрес, если хотите, и использовать ту же технику. также может помещать маркер в листовые узлы, если вам захочется запросить их. –

6

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

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

+0

Я считаю, что реляционная модель вполне подходит. Я определил «потомок», используя строго логику первого порядка, если я не ошибаюсь :-) –

+0

Решение в ссылке фиксировано глубиной, в то время как вопрос задает любую глубину. Единственное отличие от запроса в вопросе заключается в использовании 'left join' вместо' union all' – Andomar

+0

@Andomar: я не думаю, что вы читаете всю связанную статью ... начните с раздела под названием «The Nested Set Model « –

1

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

+0

Как вопрос, так и теги указывают SQLite ... – Andomar

+0

@Andomar: Как я уже сказал в исходном сообщении: «Однако, если для этого типа запросов требуются функции, недоступные в SQLite, мне любопытно узнать, можно ли это сделать в других базах данных SQL ». –

Смежные вопросы