Devo calcolare la profondità di un discendente dal suo antenato. Quando un record ha object_id = parent_id = ancestor_id
, è considerato un nodo radice (l'antenato). Ho cercato di far funzionare una WITH RECURSIVE
query con PostgreSQL 9.4 .
Non controllo i dati o le colonne. Lo schema di dati e tabelle proviene da un'origine esterna. La tabella è in continua crescita . In questo momento da circa 30k record al giorno. Qualunque nodo nella struttura può mancare e verranno estratti da una fonte esterna ad un certo punto. Di solito vengono estratti in created_at DESC
ordine, ma i dati vengono estratti con processi in background asincroni.
Inizialmente avevamo una soluzione di codice per questo problema, ma ora con più di 5 milioni di righe, il completamento richiede quasi 30 minuti.
Definizione della tabella di esempio e dati di test:
CREATE TABLE objects (
id serial NOT NULL PRIMARY KEY,
customer_id integer NOT NULL,
object_id integer NOT NULL,
parent_id integer,
ancestor_id integer,
generation integer NOT NULL DEFAULT 0
);
INSERT INTO objects(id, customer_id , object_id, parent_id, ancestor_id, generation)
VALUES (2, 1, 2, 1, 1, -1), --no parent yet
(3, 2, 3, 3, 3, -1), --root node
(4, 2, 4, 3, 3, -1), --depth 1
(5, 2, 5, 4, 3, -1), --depth 2
(6, 2, 6, 5, 3, -1), --depth 3
(7, 1, 7, 7, 7, -1), --root node
(8, 1, 8, 7, 7, -1), --depth 1
(9, 1, 9, 8, 7, -1); --depth 2
Nota che object_id
non è unico, ma la combinazione (customer_id, object_id)
è unica.
Esecuzione di una query come questa:
WITH RECURSIVE descendants(id, customer_id, object_id, parent_id, ancestor_id, depth) AS (
SELECT id, customer_id, object_id, parent_id, ancestor_id, 0
FROM objects
WHERE object_id = parent_id
UNION
SELECT o.id, o.customer_id, o.object_id, o.parent_id, o.ancestor_id, d.depth + 1
FROM objects o
INNER JOIN descendants d ON d.parent_id = o.object_id
WHERE
d.id <> o.id
AND
d.customer_id = o.customer_id
) SELECT * FROM descendants d;
Vorrei che la generation
colonna fosse impostata come profondità calcolata. Quando viene aggiunto un nuovo record, la colonna di generazione viene impostata come -1. Ci sono alcuni casi in cui a parent_id
potrebbe non essere stato ancora estratto. Se il parent_id
non esiste, dovrebbe lasciare la colonna di generazione impostata su -1.
I dati finali dovrebbero apparire come:
id | customer_id | object_id | parent_id | ancestor_id | generation
2 1 2 1 1 -1
3 2 3 3 3 0
4 2 4 3 3 1
5 2 5 4 3 2
6 2 6 5 3 3
7 1 7 7 7 0
8 1 8 7 7 1
9 1 9 8 7 2
Il risultato della query dovrebbe essere quello di aggiornare la colonna di generazione alla profondità corretta.
Ho iniziato a lavorare dalle risposte a questa domanda correlata su SO .
update
al tavolo con il risultato del tuo CTE ricorsivo?