sql >> Base de Datos >  >> RDS >> PostgreSQL

Auto-unión recursiva de Postgresql

Este es un uso clásico de una expresión de tabla común recursiva simple (WITH RECURSIVE ), disponible en PostgreSQL 8.4 y versiones posteriores.

Demostrado aquí:http://sqlfiddle.com/#!12/78e15/9

Dados los datos de muestra como SQL:

CREATE TABLE Table1
    ("ID1" text, "ID2" text)
;

INSERT INTO Table1
    ("ID1", "ID2")
VALUES
    ('vc1', 'vc2'),
    ('vc2', 'vc3'),
    ('vc3', 'vc4'),
    ('vc4', 'rc7')
;

Podrías escribir:

WITH RECURSIVE chain(from_id, to_id) AS (
  SELECT NULL, 'vc2'
  UNION
  SELECT c.to_id, t."ID2"
  FROM chain c
  LEFT OUTER JOIN Table1 t ON (t."ID1" = to_id)
  WHERE c.to_id IS NOT NULL
)
SELECT from_id FROM chain WHERE to_id IS NULL;

Lo que esto hace es recorrer iterativamente la cadena, agregando cada fila a la chain tabla como punteros de origen y destino. Cuando encuentra una fila para la que no existe la referencia 'to', agregará una referencia nula 'to' para esa fila. La próxima iteración notará que la referencia 'a' es nula y producirá cero filas, lo que hace que la iteración finalice.

Luego, la consulta externa selecciona las filas que se ha determinado que son el final de la cadena al tener un to_id inexistente.

Se necesita un poco de esfuerzo para comprender los CTE recursivos. Las cosas clave para entender son:

  • Comienzan con la salida de una consulta inicial, que unen repetidamente con la salida de la "parte recursiva" (la consulta después de UNION o UNION ALL ) hasta que la parte recursiva no agregue filas. Eso detiene la iteración.

  • No son realmente recursivos, más iterativos, aunque son buenos para el tipo de cosas para las que podría usar la recursividad.

Así que básicamente estás construyendo una tabla en un bucle. No puede eliminar filas ni cambiarlas, solo agregar nuevas, por lo que generalmente necesita una consulta externa que filtre los resultados para obtener las filas de resultados que desea. A menudo agregará columnas adicionales que contienen datos intermedios que utiliza para rastrear el estado de la iteración, controlar las condiciones de parada, etc.

Puede ayudar mirar el resultado sin filtrar. Si reemplazo la consulta de resumen final con una simple cadena SELECT * FROM chain Puedo ver la tabla que se ha generado:

 from_id | to_id 
---------+-------
         | vc2
 vc2     | vc3
 vc3     | vc4
 vc4     | rc7
 rc7     | 
(5 rows)

La primera fila es la fila del punto de inicio agregada manualmente, donde especifica lo que desea buscar; en este caso, fue vc2 . Cada fila subsiguiente fue agregada por UNION ed término recursivo, que hace un LEFT OUTER JOIN en el resultado anterior y devuelve un nuevo conjunto de filas que emparejan el anterior to_id (ahora en el from_id columna) al siguiente to_id . Si la LEFT OUTER JOIN no coincide con el to_id será nulo, lo que hará que la siguiente invocación devuelva filas y finalice la iteración.

Debido a que esta consulta no intenta agregar solo el último fila cada vez, en realidad está repitiendo bastante trabajo en cada iteración. Para evitar eso, necesitaría usar un enfoque más parecido al de Gordon, pero además filtrar en el campo de profundidad anterior cuando escaneó la tabla de entrada, por lo que se unió solo a la fila más reciente. En la práctica, esto generalmente no es necesario, pero puede ser una preocupación para conjuntos de datos muy grandes o cuando no puede crear índices apropiados.

Se puede aprender más en la documentación de PostgreSQL sobre CTE.