¿Cuál es la diferencia entre LATERAL y una subconsulta en PostgreSQL?

146

Desde que Postgres salió con la capacidad de hacer LATERALuniones, lo he estado leyendo, ya que actualmente hago volcados de datos complejos para mi equipo con muchas subconsultas ineficientes que hacen que la consulta general tome cuatro minutos o más.

Entiendo que las LATERALuniones pueden ayudarme, pero incluso después de leer artículos como este de Heap Analytics, todavía no lo sigo.

¿Cuál es el caso de uso de una LATERALunión? ¿Cuál es la diferencia entre una LATERALunión y una subconsulta?

jdotjdot
fuente
2
blog.heapanalytics.com/… y explainextended.com/2009/07/16/inner-join-vs-cross-apply (SQL Server applyes el mismo que el lateraldel estándar SQL)
a_horse_with_no_name

Respuestas:

163

Más como una subconsulta correlacionada

Una LATERALunión (Postgres 9.3 o posterior) se parece más a una subconsulta correlacionada , no a una subconsulta simple. Como señaló Andomar , una función o subconsulta a la derecha de una LATERALunión debe evaluarse una vez por cada fila a la izquierda de la misma, al igual que una subconsulta correlacionada , mientras que una subconsulta simple (expresión de tabla) se evalúa solo una vez . (Sin embargo, el planificador de consultas tiene formas de optimizar el rendimiento para ambos).
Esta respuesta relacionada tiene ejemplos de código para ambos lado a lado, resolviendo el mismo problema:

Para devolver más de una columna , una LATERALunión suele ser más simple, limpia y rápida.
Además, recuerde que el equivalente de una subconsulta correlacionada es LEFT JOIN LATERAL ... ON true:

Lea el manual en LATERAL

Es más autoritario que cualquier cosa que vamos a poner aquí en respuestas:

Cosas que una subconsulta no puede hacer

No son cosas que una LATERALcombinación puede hacer, pero un (correlacionados) subconsulta no puede (fácilmente). Una subconsulta correlacionada solo puede devolver un valor único, no columnas múltiples ni filas múltiples, con la excepción de las llamadas a funciones desnudas (que multiplican las filas de resultados si devuelven varias filas). Pero incluso ciertas funciones de devolución de conjuntos solo están permitidas en la FROMcláusula. Al igual que unnest()con múltiples parámetros en Postgres 9.4 o posterior. El manual:

Esto solo está permitido en la FROMcláusula;

Entonces esto funciona, pero no se puede reemplazar fácilmente con una subconsulta:

CREATE TABLE tbl (a1 int[], a2 int[]);
SELECT * FROM tbl, unnest(a1, a2) u(elem1, elem2);  -- implicit LATERAL

La coma ( ,) en la FROMcláusula es una notación corta para CROSS JOIN.
LATERALse asume automáticamente para funciones de tabla.
Más sobre el caso especial de UNNEST( array_expression [, ... ] ):

Establecer funciones de retorno en la SELECTlista

También puede usar funciones de devolución de conjuntos como unnest()en la SELECTlista directamente. Esto solía exhibir un comportamiento sorprendente con más de una de esas funciones en la misma SELECTlista hasta Postgres 9.6. Pero finalmente se ha desinfectado con Postgres 10 y ahora es una alternativa válida (incluso si no es SQL estándar). Ver:

Sobre la base del ejemplo anterior:

SELECT *, unnest(a1) AS elem1, unnest(a2) AS elem2
FROM   tbl;

Comparación:

dbfiddle para pg 9.6 aquí
dbfiddle para pg 10 aquí

Aclarar información errónea

El manual:

Para los tipos INNERy OUTERjoin, se debe especificar una condición de combinación, es decir, exactamente una de NATURAL, ON join_condition o USING( join_column [, ...]). Vea a continuación el significado.
Porque CROSS JOINninguna de estas cláusulas puede aparecer.

Estas dos consultas son válidas (incluso si no son particularmente útiles):

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t ON TRUE;

SELECT *
FROM   tbl t, LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;

Si bien este no es:

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;

Es por eso que el ejemplo de código de @ Andomar es correcto ( CROSS JOINno requiere una condición de unión) y el de @ Attila no es válido.

Erwin Brandstetter
fuente
Hay algunas cosas que una subconsulta puede hacer una LATERAL JOIN no puede hacer. Como funciones de ventana. Como aquí
Evan Carroll,
@EvanCarroll: No pude encontrar ninguna subconsulta correlacionada en el enlace. Pero agregué otra respuesta para demostrar una función de ventana en una LATERALsubconsulta: gis.stackexchange.com/a/230070/7244
Erwin Brandstetter
1
¿Más limpio y más rápido? Como magnitudes más rápidas en algunos casos. Tuve una consulta que pasó de días a segundos después de cambiar a LATERAL.
rovyko
52

La diferencia entre un no lateraly una lateralunión radica en si puede mirar a la fila de la tabla de la izquierda. Por ejemplo:

select  *
from    table1 t1
cross join lateral
        (
        select  *
        from    t2
        where   t1.col1 = t2.col1 -- Only allowed because of lateral
        ) sub

Esta "mirada hacia afuera" significa que la subconsulta debe evaluarse más de una vez. Después de todo, t1.col1puede asumir muchos valores.

Por el contrario, la subconsulta después de una no lateralunión se puede evaluar una vez:

select  *
from    table1 t1
cross join
        (
        select  *
        from    t2
        where   t2.col1 = 42 -- No reference to outer query
        ) sub

Como se requiere sin lateral, la consulta interna no depende de ninguna manera de la consulta externa. Una lateralconsulta es un ejemplo de correlatedconsulta, debido a su relación con filas fuera de la consulta misma.

Andomar
fuente
55
Esta es la explicación más clara de la unión lateral.
1valdis
Explicación fácil de entender, gracias.
Arilwan
¿Cómo se select * from table1 left join t2 using (col1)compara? No está claro para mí cuando una combinación usando / en condición es insuficiente y tendría más sentido usar lateral.
No_name
9

Primero, Lateral y Cross Apply es lo mismo . Por lo tanto, también puede leer sobre Cross Apply. Dado que se implementó en SQL Server durante siglos, encontrará más información al respecto que Lateral.

En segundo lugar, según tengo entendido , no hay nada que no pueda hacer usando subconsulta en lugar de usar lateral. Pero:

Considere la siguiente consulta.

Select A.*
, (Select B.Column1 from B where B.Fk1 = A.PK and Limit 1)
, (Select B.Column2 from B where B.Fk1 = A.PK and Limit 1)
FROM A 

Puede usar lateral en esta condición.

Select A.*
, x.Column1
, x.Column2
FROM A LEFT JOIN LATERAL (
  Select B.Column1,B.Column2,B.Fk1 from B  Limit 1
) x ON X.Fk1 = A.PK

En esta consulta no puede usar la unión normal, debido a la cláusula de límite. La aplicación lateral o cruzada se puede usar cuando no hay una condición de unión simple .

Hay más usos para la aplicación lateral o cruzada, pero este es el más común que encontré.

Atilla Ozgur
fuente
1
Exactamente, me pregunto por qué PostgreSQL usa en laterallugar de apply. ¿Quizás Microsoft patentó la sintaxis?
Andomar
9
@Andomar AFAIK lateralestá en el estándar SQL pero applyno lo está.
mu es demasiado corto
El LEFT JOINrequiere una condición de unión. Hazlo a ON TRUEmenos que quieras restringirlo de alguna manera.
Erwin Brandstetter
Erwin tiene razón, obtendrá un error a menos que use una cross joino una oncondición
Andomar
1
@Andomar: Impulsado por esta información errónea, agregué otra respuesta para aclarar.
Erwin Brandstetter
4

Una cosa que nadie ha señalado es que puede usar LATERALconsultas para aplicar una función definida por el usuario en cada fila seleccionada.

Por ejemplo:

CREATE OR REPLACE FUNCTION delete_company(companyId varchar(255))
RETURNS void AS $$
    BEGIN
        DELETE FROM company_settings WHERE "company_id"=company_id;
        DELETE FROM users WHERE "company_id"=companyId;
        DELETE FROM companies WHERE id=companyId;
    END; 
$$ LANGUAGE plpgsql;

SELECT * FROM (
    SELECT id, name, created_at FROM companies WHERE created_at < '2018-01-01'
) c, LATERAL delete_company(c.id);

Esa es la única forma en que sé cómo hacer este tipo de cosas en PostgreSQL.

Theodore R. Smith
fuente