Necesito calcular la profundidad de un descendiente de su antepasado. Cuando un registro tiene object_id = parent_id = ancestor_id
, se considera un nodo raíz (el antepasado). He estado tratando de WITH RECURSIVE
ejecutar una consulta con PostgreSQL 9.4 .
No controlo los datos o las columnas. El esquema de datos y tablas proviene de una fuente externa. La mesa está creciendo continuamente . En este momento por unos 30k registros por día. Puede faltar cualquier nodo en el árbol y se extraerá de una fuente externa en algún momento. Por lo general, se extraen en created_at DESC
orden, pero los datos se extraen con trabajos en segundo plano asincrónicos.
Inicialmente teníamos una solución de código para este problema, pero ahora con más de 5 millones de filas, tarda casi 30 minutos en completarse.
Ejemplo de definición de tabla y datos de prueba:
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
Tenga en cuenta que object_id
no es único, pero la combinación (customer_id, object_id)
es única.
Ejecutando una consulta como esta:
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;
Me gustaría que la generation
columna se establezca como la profundidad que se calculó. Cuando se agrega un nuevo registro, la columna de generación se establece como -1. Hay algunos casos en los que es parent_id
posible que todavía no se haya retirado. Si parent_id
no existe, debería dejar la columna de generación establecida en -1.
Los datos finales deberían verse así:
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
El resultado de la consulta debe ser actualizar la columna de generación a la profundidad correcta.
Comencé a trabajar a partir de las respuestas a esta pregunta relacionada sobre SO .
ancestor_id
ya está configurado, ¿solo necesita asignar la generación desde la profundidad CTE?
update
la mesa con el resultado de tu CTE recursivo?