Ich muss die Tiefe eines Nachkommens von seinem Vorfahren berechnen. Wenn ein Datensatz vorhanden ist object_id = parent_id = ancestor_id
, wird er als Stammknoten (der Vorfahr) betrachtet. Ich habe versucht, eine WITH RECURSIVE
Abfrage mit PostgreSQL 9.4 zum Laufen zu bringen .
Ich kontrolliere weder die Daten noch die Spalten. Das Daten- und Tabellenschema stammt aus einer externen Quelle. Der Tisch wächst stetig . Momentan um ca. 30.000 Datensätze pro Tag. Jeder Knoten in der Baumstruktur kann fehlen und wird irgendwann von einer externen Quelle abgerufen. Sie werden normalerweise der created_at DESC
Reihe nach abgerufen , aber die Daten werden mit asynchronen Hintergrundjobs abgerufen.
Wir hatten ursprünglich eine Codelösung für dieses Problem, aber jetzt, da wir über 5 Millionen Zeilen verfügen, dauert die Ausführung fast 30 Minuten.
Beispiel Tabellendefinition und Testdaten:
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
Beachten Sie, dass dies object_id
nicht eindeutig ist, die Kombination (customer_id, object_id)
jedoch eindeutig ist.
Ausführen einer Abfrage wie folgt:
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;
Ich möchte, dass die generation
Spalte als berechnete Tiefe festgelegt wird. Wenn ein neuer Datensatz hinzugefügt wird, wird die Generierungsspalte auf -1 gesetzt. Es gibt einige Fälle, in denen ein parent_id
möglicherweise noch nicht gezogen wurde. Wenn das parent_id
nicht vorhanden ist, sollte die Generierungsspalte auf -1 gesetzt bleiben.
Die endgültigen Daten sollten wie folgt aussehen:
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
Das Ergebnis der Abfrage sollte sein, die Generierungsspalte auf die richtige Tiefe zu aktualisieren.
Ich begann mit den Antworten auf diese verwandte Frage zu SO .
ancestor_id
Ist das also schon eingestellt, braucht man nur die Generation von der CTE.depth zuzuordnen?
update
mit dem Ergebnis Ihres rekursiven CTE zur Tabelle?