2013-06-21 3 views
1

Этот вопрос основан на следующий вопрос, но с дополнительным требованием: PostgreSQL: How to find the last descendant in a linear "ancestor-descendant" relationshipКак найти последнего потомка (что соответствует другим критериям) в линейном «предок-потомок» отношения

В принципе, то, что мне нужно это оператор Postgre-SQL, который находит последнего потомка в линейном соотношении «предок-потомок», который соответствует дополнительным критериям.

Пример:

Здесь содержание таблицы «RELATIONSHIP_TABLE»:

id | id_ancestor | id_entry | bool_flag 
--------------------------------------- 
1 | null  | a  | false 
2 | 1   | a  | false 
3 | 2   | a  | true 
4 | 3   | a  | false 
5 | null  | b  | true 
6 | null  | c  | false 
7 | 6   | c  | false 

Каждая запись в пределах определенной иерархии имеет тот же «id_entry» Есть 3 различных «предок-потомок» отношения в этот пример:

1. 1 <- 2 <- 3 <- 4 
2. 5 
3. 6 <- 7 

Вопрос PostgreSQL: How to find the last descendant in a linear "ancestor-descendant" relationship показывает, как найти последнюю запись каждого отношения. В приведенном выше примере:

1. 4 
2. 5 
3. 7 

Итак, что мне нужно на этот раз последний потомок от «id_entry», чьи «bool_flag» установлена ​​истина. В приведенном выше примере:

1. 3 
2. 5 
3. <empty result> 

Кто-нибудь знает решение?

Заранее спасибо :)

QStormDS

+1

Я, ваш пример, отношения между оркестрами сортируются (например, у предков всегда есть меньшие идентификаторы, а затем деканданты). Это всегда в вашем случае? –

+0

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

+1

Должна ли запись с {id = 5, id_entry = b, bool_flag = True} быть также в ожидаемом выходе? – wildplasser

ответ

3

Графы, деревья, цепочки и т.д. представлены в виде края списков, как правило, хорошо использует для рекурсивных общих табличных выражений - т.е. WITH RECURSIVE запросов.

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

WITH RECURSIVE walk(id, id_ancestor, id_entry, bool_flag, id_root, generation) AS (
    SELECT id, id_ancestor, id_entry, bool_flag, id, 0 
    FROM RELATIONSHIP_TABLE 
    WHERE id_ancestor IS NULL 
    UNION ALL 
    SELECT x.id, x.id_ancestor, x.id_entry, x.bool_flag, walk.id_root, walk.generation + 1 
    FROM RELATIONSHIP_TABLE x INNER JOIN walk ON x.id_ancestor = walk.id 
) 
SELECT 
    id_entry, id_root, id 
FROM (
    SELECT 
    id, id_entry, bool_flag, id_root, generation, 
    max(CASE WHEN bool_flag THEN generation END) OVER w as max_enabled_generation 
    FROM walk 
    WINDOW w AS (PARTITION BY id_root ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) 
) x 
WHERE generation = max_enabled_generation; 

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

Если id_entry является общим для всех членов дерева, вы можете избежать необходимости отслеживать id_root. Вы должны создать UNIQUE ограничения на (id_entry, id) и ограничение внешнего ключа на FOREIGN KEY (id_entry, id_ancestor) REFERENCES (id_entry, id), чтобы убедиться, что упорядочение соответствует, то используйте:

WITH RECURSIVE walk(id, id_ancestor, id_entry, bool_flag, generation) AS (
    SELECT id, id_ancestor, id_entry, bool_flag, 0 
    FROM RELATIONSHIP_TABLE 
    WHERE id_ancestor IS NULL 
    UNION ALL 
    SELECT x.id, x.id_ancestor, x.id_entry, x.bool_flag, walk.generation + 1 
    FROM RELATIONSHIP_TABLE x INNER JOIN walk ON x.id_ancestor = walk.id 
) 
SELECT 
    id_entry, id 
FROM (
    SELECT 
    id, id_entry, bool_flag, generation, 
    max(CASE WHEN bool_flag THEN generation END) OVER w as max_enabled_generation 
    FROM walk 
    WINDOW w AS (PARTITION BY id_entry ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) 
) x 
WHERE generation = max_enabled_generation; 

Поскольку это дает таблицу конечных потомков сопоставляются с корневыми родителями, вы может просто фильтровать с помощью обычного предложения WHERE, просто добавьте AND bool_flag. Если вы хотите исключить цепочки, которые имеют bool_flag, установите значение false на любым пунктами в пути, вы можете добавить WHERE bool_value в соединение запроса RECURSIVE.

SQLFiddle пример: http://sqlfiddle.com/#!12/92a64/3

+0

Привет, когда я выполняю запрос (BTW: Я использую postgre 8.4.1): ОШИБКА: начало кадра в CURRENT ROW не реализовано LINE 16: WINDOW w AS (PARTITION by id_root ROWS BENWEEN CURRENT ROW ... – QStormDS

+0

@QStormDS Ну, это то, что вы получаете за то, что не указали свою версию в вопросах, которые я боюсь: если вы не скажете иначе, я предположим, что вы находитесь в текущей версии и протестируете соответственно. 'ROWS МЕЖДУ ТЕКУЩЕЙ РЯДОМ И НЕОБЫЧНЫМИ FOLLOWING' на самом деле не требуется в окончательной версии запроса (я использовал 'row_number' так, как это было раньше) ... так что вам может быть все в порядке, просто удалив его. –

1
WITH RECURSIVE tail AS (
    SELECT id AS opa 
      , id, bool_flag FROM boolshit 
    WHERE bool_flag = True 
    UNION ALL 
    SELECT t.opa AS opa 
    , b.id, b.bool_flag FROM boolshit b 
    JOIN tail t ON b.id_ancestor = t.id 
    ) 
SELECT * 
FROM boolshit bs 
WHERE bs.bool_flag = True 
AND NOT EXISTS (
    SELECT * FROM tail t 
    WHERE t.opa = bs.id 
    AND t.id <> bs.id 
    AND t.bool_flag = True 
    ); 

Объяснение: выбрать все записи, которые имеют множество bool_flag, за исключением тех, которые имеют потомство (прямое или косвенное), которые имеют множество bool_flag тоже. Это эффективно выбирает последнюю запись цепочки с установленным флагом.

+0

Это хорошо работает. ! :) – QStormDS

+0

@QStormDS Если вы используете решение, вы, как правило, принимаете его как самый правильный ответ с зеленым тиком. http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work –