2008-09-09 5 views
62

У меня есть таблица, похожее на это:Можно ли сделать рекурсивный SQL-запрос?

CREATE TABLE example (
    id integer primary key, 
    name char(200), 
    parentid integer, 
    value integer); 

можно использовать поле ParentId организовать данные в виде древовидной структуры.

Теперь вот бит, который я не могу решить. Учитывая родительский вопрос, можно ли написать инструкцию SQL, чтобы скомпилировать все поля значений под этим родителем и перезаписать ветвь дерева?

UPDATE: Я использую posgreSQL, поэтому модные функции MS-SQL недоступны для меня. В любом случае, я бы хотел, чтобы это рассматривалось как общий вопрос SQL.

BTW, я очень впечатлен тем, что у меня есть 6 ответов в течение 15 минут с вопросом! Переполнение стека!

+3

Это данные по иерархии. Я нашел обсуждения Энтони Моллинаро о гериархических данных в SQL Cookbook (O'Reilly), чтобы быть действительно удобными; он охватывает практически все популярные СУБД, включая PostrgreSQL. – 2009-03-20 13:37:06

+0

Я использую posgreSQL, поэтому модные функции MS-SQL недоступны мне. В любом случае, я бы хотел, чтобы это рассматривалось как общий вопрос SQL. Кстати, я очень впечатлен тем, что у меня есть 6 ответов в течение 15 минут с вопросом! Переполнение стека! – 2008-09-09 23:28:43

+1

Если вы придете сюда из проверки google check @Chris KL, так как регрессивные запросы PostgreSQl доступны на postgreSQL. – regilero 2011-06-20 12:24:51

ответ

11

Есть несколько способов сделать то, что вам нужно в PostgreSQL.

  • Если вы можете установить модули, посмотрите на tablefunc contrib. Он имеет функцию connectby(), которая обрабатывает перемещение деревьев. http://www.postgresql.org/docs/8.3/interactive/tablefunc.html

  • Также проверьте ltree вно, который вы могли бы адаптировать ваш стол для использования: http://www.postgresql.org/docs/8.3/interactive/ltree.html

  • Или вы можете перемещаться по дереву себя функции/PGSQL PL.

Что-то вроде этого:

create or replace function example_subtree (integer) 
returns setof example as 
'declare results record; 
     child record; 
begin 
    select into results * from example where parent_id = $1; 
    if found then 
    return next results; 
    for child in select id from example 
        where parent_id = $1 
     loop 
     for temp in select * from example_subtree(child.id) 
     loop 
      return next temp; 
     end loop; 
     end loop; 
    end if; 
    return null; 
end;' language 'plpgsql'; 

select sum(value) as value_sum 
    from example_subtree(1234); 
0

это SQL Server? Не могли бы вы написать хранимую процедуру TSQL, которая проходит и объединяет результаты вместе?

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

5

использование common table expression.

Может потребоваться указать только SQL Server 2005 или выше. Dale Ragan

here's an article рекурсии по SqlTeam без общих табличных выражений.

+0

Может потребоваться указать только SQL Server 2005 или выше. – 2008-09-09 23:19:21

+0

Не хотелось бы смотреть сейчас, но я знаю, что у оракула 10g есть предложения. Интересно, возможно ли это там. – 2008-09-09 23:54:58

+1

Oracle 11gR2 представил поддержку рекурсивных предложений WITH; до этого предложение WITH не могло ссылаться на себя. Для предыдущих версий Oracle имеет свой собственный иерархический синтаксис запроса (START WITH + CONNECT BY), поскольку по крайней мере версии 7 или 8, возможно, раньше. – 2010-01-27 05:46:15

0

Я думаю, что проще в SQL 2008 с HierarchyID

+0

согласился. базы данных связаны меню будет soooo гораздо приятнее. – 2008-09-09 23:25:28

5

Если с помощью SQL Server 2005, есть действительно хороший способ сделать это с помощью общих табличных выражений.

Из-за создания временной таблицы требуется все из-за работы, и в основном позволяет вам делать все с помощью только СОН и СОЮЗА.

Вот хороший учебник:

http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html

15

Если вы хотите портативную решение, которое будет работайте над любой СУБД ANSI SQL-92, вам нужно будет добавить новый столбец в вашу таблицу.

Joe Celko является оригинальным автором подхода к хранению иерархий в SQL. Nested Sets. Вы можете использовать Google "nested sets" hierarchy, чтобы больше узнать о фоном.

Или вы можете просто переименовать ParentId в leftid и добавить rightid.

Вот моя попытка суммировать вложенные наборы, которые будут падать ужасно коротко, потому что я не Joe Celko: SQL - это язык, основанный на наборе, а модель смежности (сохранение родительского идентификатора) НЕ представляет собой наборное представление иерархии. Поэтому для запроса схемы смежности нет чистого метода на основе набора.

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

-1

Если вам нужно хранить произвольные графы, а не только иерархия, вы можете нажать Postgres в сторону и попробовать базу данных графа таких как AllegroGraph:

Все в базе данных графа хранится в виде тройным (исходного узла, edge, target node), и он дает вам поддержку первого класса для управления структурой графа и запроса его с использованием языка SQL.

Он не интегрируется хорошо с чем-то вроде Hibernate или Django ORM, но если вы серьезно относитесь к структурам графов (это дает вам не просто иерархии, такие как модель вложенного набора).

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

2

Следующий код компилирует и это тестирование OK.

 
create or replace function subtree (bigint) 
returns setof example as $$ 
declare 
    results record; 
    entry record; 
    recs record; 
begin 
    select into results * from example where parent = $1; 
    if found then 
     for entry in select child from example where parent = $1 and child parent loop 
      for recs in select * from subtree(entry.child) loop 
       return next recs; 
      end loop; 
     end loop; 
    end if; 
    return next results; 
end; 
$$ language 'plpgsql'; 

условие «ребенок <> родитель» необходимо в моем случае, так как узлы указывают на себя.

Удачи :)

34

Начиная с версии 8.4, PostgreSQL имеет recursive query support для общих табличных выражений, используя стандартный SQL WITH синтаксис.

1

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

общий SQL вопрос

то реализация SQL является довольно прямолинейно, поскольку SQL'99 допускает линейную рекурсию в спецификации (хотя я считаю, что RDBMSs не реализует стандарт полностью) через оператор WITH RECURSIVE. Поэтому с теоретической точки зрения мы можем сделать это прямо сейчас.

1

Ни один из примеров не работал нормально для меня, так что я установил его так:

 
declare 
    results record; 
    entry record; 
    recs record; 
begin 
    for results in select * from project where pid = $1 loop 
     return next results; 
     for recs in select * from project_subtree(results.id) loop 
      return next recs; 
     end loop; 
    end loop; 
    return; 
end; 
10

Стандартный способ сделать рекурсивный запрос в SQL рекурсивны CTE. PostgreSQL поддерживает их с 8.4.

В более ранних версиях, вы можете написать рекурсивную возвращающих набор функций:

CREATE FUNCTION fn_hierarchy (parent INT) 
RETURNS SETOF example 
AS 
$$ 
     SELECT example 
     FROM example 
     WHERE id = $1 
     UNION ALL 
     SELECT fn_hierarchy(id) 
     FROM example 
     WHERE parentid = $1 
$$ 
LANGUAGE 'sql'; 

SELECT * 
FROM fn_hierarchy(1) 

Смотрите эту статью:

41

Ниже приведен пример сценария, используя общий стол выражение:

with recursive sumthis(id, val) as (
    select id, value 
    from example 
    where id = :selectedid 
    union all 
    select C.id, C.value 
    from sumthis P 
    inner join example C on P.id = C.parentid 
) 
select sum(val) from sumthis 

В приведенном выше скрипте создается виртуальная таблица с именем sumthis, которая имеет столбцы id и val. Он определяется как результат двух выбранных объединений с union all.

Первый select получает корень (where id = :selectedid).

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

Конечный результат может быть обработан как обычная таблица. В этом случае столбцы val суммируются.