2012-11-02 25 views
8

Questa è una versione semplificata di un problema che sto riscontrando in PostgreSQL.somma gerarchica in PostgreSQL

Ho la seguente tabella A:

[IDINTERO| VALORENUMERIC (10,2)| GENITOREINTERO]

Dove 'genitori' è un autoreferenziale FK a ID di colonna.

La definizione della tabella è:

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

Questa semplice tabella permette di definire strutture dati albero di profondità arbitraria. Ora ho bisogno di scrivere un SQL (preferisco non usare il PL-SQL lato server) che riporta per ogni nodo, il valore totale del sottoalbero "sospeso" sotto di esso. Per esempio, con la seguente tabella:

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

dovrei ottenere il set di risultati seguente:

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

Per simplicitly, si può supporre che solo i nodi foglia hanno valori, i nodi non foglia avere sempre un valore di NULL nella colonna VALUE. C'è un modo per farlo in SQL, anche utilizzando estensioni specifiche di PostgreSQL?

risposta

5

In PostgreSQL è possibile utilizzare ricorsiva CTE (espressione di tabella comune) per camminare alberi nelle query.

Qui ci sono due link pertinenti in docs:

EDIT

Poiché non esiste un sub-SELECT richiesto potrebbe funzionare un po 'meglio su un set di dati più ampio rispetto alla query di Arion.

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

Forse qualcosa di simile:

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; 

Verrà avviata con i bambini che non ha figli. Poi vai su dall'albero ai genitori. Il risultato sarà simile a questo:

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