2012-11-02 15 views
8

To jest uproszczona wersja problemu, który napotykam w PostgreSQL.Hierarchiczna suma w PostgreSQL

mam następującą tabelę :

[IDINTEGER| VALUENUMERIC (10,2)| RODZICINTEGER]

Gdzie rodzica jest samodzielnym przedstawieniu FK do kolumny ID.

Definicja tabela:

CREATE TABLE A(ID INTEGER IDENTITY, VALUE NUMERIC(10,2), PARENT INTEGER)          
ALTER TABLE A ADD CONSTRAINT FK FOREIGN KEY (PARENT) REFERENCES A(ID) 

Ta prosta tabela pozwala na zdefiniowanie struktury danych drzewa arbitralnej głębokości. Teraz muszę napisać SQL (wolę nie używać po stronie PL-SQL po stronie serwera), który zgłasza dla każdego węzła, całkowitą wartość pod-drzewa "wiszącego" pod nim. Na przykład, w poniższej tabeli:

| ID | VALUE | PARENT | 
------------------------- 
| 1 | NULL | NULL | 
| 2 | 3.50 | 1 | 
| 3 | NULL | NULL | 
| 4 | NULL | 3 | 
| 5 | 1.50 | 4 | 
| 6 | 2.20 | 4 | 

Powinienem się następujący zestaw wyników:

| ID | Total-Value-of-Subtree | 
| 1 |     3.50 | 
| 2 |     3.50 | 
| 3 |     3.70 | 
| 4 |     3.70 | 
| 5 |     1.50 | 
| 6 |     2.20 | 

Dla simplicitly, można założyć, że tylko węzły liściowe mają wartości, węzły non-leaf zawsze mieć wartość NULL w kolumnie VALUE. Czy istnieje sposób, aby to zrobić w SQL, nawet korzystając z rozszerzeń specyficznych dla PostgreSQL?

Odpowiedz

5

W PostgreSQL można użyć rekurencyjnej CTE (Common Expression tabeli) chodzić drzew w zapytaniach.

Oto dwa odpowiednie linki do dokumentów:

EDIT

Ponieważ nie ma SUBSELECT wymagane może działać trochę lepiej większy zbiór danych niż zapytanie Ariona.

WITH RECURSIVE children AS (
    -- select leaf nodes 
    SELECT id, value, parent 
     FROM t 
     WHERE value IS NOT NULL 
    UNION ALL 
    -- propagate values of leaf nodes up, adding rows 
    SELECT t.id, children.value, t.parent 
     FROM children JOIN t ON children.parent = t.id 
) 
SELECT id, sum(value) 
    FROM children 
    GROUP BY id -- sum up appropriate rows 
    ORDER BY id; 
5

Może coś takiego:

WITH RECURSIVE CTE 
AS 
(
    SELECT 
     t.ID, 
     t.VALUE, 
     t.PARENT 
    FROM 
     t 
    WHERE NOT EXISTS 
     (
      SELECT NULL FROM t AS t2 WHERE t2.PARENT=t.ID 
     ) 
    UNION ALL 
    SELECT 
     t.ID, 
     COALESCE(t.VALUE,CTE.VALUE), 
     t.PARENT 
    FROM 
     t 
     JOIN CTE 
      ON CTE.PARENT=t.ID 
) 
SELECT 
    CTE.ID, 
    SUM(CTE.VALUE) 
FROM 
    CTE 
GROUP BY 
    CTE.ID 
ORDER BY 
    ID; 

ten rozpocznie się z dziećmi, które nie ma dzieci. Potem idź pod drzewo do rodziców. Rezultatem będzie tak:

1 3.50 
2 3.50 
3 3.70 
4 3.70 
5 1.50 
6 2.20