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

Múltiples llamadas a array_agg() en una sola consulta

DISTINCT a menudo se aplica para reparar consultas que están podridas desde el interior, y eso suele ser lento y/o incorrecto. No multiplique las filas para empezar, así no tendrá que clasificar los duplicados no deseados al final.

Unirse a varias tablas n ("tiene muchas") a la vez multiplica las filas en el conjunto de resultados. Eso es como un CROSS JOIN o producto cartesiano por proxy :

  • Dos SQL LEFT JOINS producen un resultado incorrecto

Hay varias formas de evitar este error.

Agregue primero, únase después

Técnicamente, la consulta funciona siempre que se una a uno tabla con varias filas a la vez antes de agregar:

SELECT e.id, e.name, e.age, e.streets, arrag_agg(wd.day) AS days
FROM  (
   SELECT e.id, e.name, e.age, array_agg(ad.street) AS streets
   FROM   employees e 
   JOIN   address  ad ON ad.employeeid = e.id
   GROUP  BY e.id    -- id enough if it is defined PK
   ) e
JOIN   workingdays wd ON wd.employeeid = e.id
GROUP  BY e.id, e.name, e.age;

También es mejor incluir la clave principal id y GROUP BY porque name y age no son necesariamente únicos. Podría fusionar dos empleados por error.

Pero puede agregar en una subconsulta before te unes, eso es superior a menos que tengas un WHERE selectivo condiciones de employees :

SELECT e.id, e.name, e.age, ad.streets, arrag_agg(wd.day) AS days
FROM   employees e 
JOIN  (
   SELECT employeeid, array_agg(ad.street) AS streets
   FROM   address
   GROUP  BY 1
   ) ad ON ad.employeeid = e.id
JOIN   workingdays wd ON e.id = wd.employeeid
GROUP  BY e.id, e.name, e.age, ad.streets;

O agrega ambos:

SELECT name, age, ad.streets, wd.days
FROM   employees e 
JOIN  (
   SELECT employeeid, array_agg(ad.street) AS streets
   FROM   address
   GROUP  BY 1
   ) ad ON ad.employeeid = e.id
JOIN  (
   SELECT employeeid, arrag_agg(wd.day) AS days
   FROM   workingdays
   GROUP  BY 1
   ) wd ON wd.employeeid = e.id;

El último suele ser más rápido si recupera todos o la mayoría de las filas en las tablas base.

Tenga en cuenta que usar JOIN y no LEFT JOIN elimina del resultado a los empleados que no tienen dirección o sin días laborables. Eso puede o no ser intencionado. Cambiar a LEFT JOIN retener todas empleados en el resultado.

Subconsultas correlacionadas / unión LATERAL

Para una pequeña selección , consideraría subconsultas correlacionadas en su lugar:

SELECT name, age
    , (SELECT array_agg(street) FROM address WHERE employeeid = e.id) AS streets
    , (SELECT arrag_agg(day) FROM workingdays WHERE employeeid = e.id) AS days
FROM   employees e
WHERE  e.namer = 'peter';  -- very selective

O, con Postgres 9.3 o posterior, puede usar LATERAL se une para eso:

SELECT e.name, e.age, a.streets, w.days
FROM   employees e
LEFT   JOIN LATERAL (
   SELECT array_agg(street) AS streets
   FROM   address
   WHERE  employeeid = e.id
   GROUP  BY 1
   ) a ON true
LEFT   JOIN LATERAL (
   SELECT array_agg(day) AS days
   FROM   workingdays
   WHERE  employeeid = e.id
   GROUP  BY 1
   ) w ON true
WHERE  e.name = 'peter';  -- very selective
  • ¿Cuál es la diferencia entre LATERAL y una subconsulta en PostgreSQL?

Cualquiera de las consultas retiene todas empleados en el resultado.