Qual é a diferença entre LATERAL e uma subconsulta no PostgreSQL?


146

Desde que o Postgres surgiu com a capacidade de fazer LATERALjunções, eu tenho lido sobre isso, pois atualmente faço dumps de dados complexos para minha equipe com muitas subconsultas ineficientes que fazem com que a consulta geral leve quatro minutos ou mais.

Entendo que as LATERALassociações podem me ajudar, mas mesmo depois de ler artigos como este no Heap Analytics, ainda não os entendo.

Qual é o caso de uso para uma LATERALassociação? Qual é a diferença entre uma LATERALjunção e uma subconsulta?


Respostas:


163

Mais como uma subconsulta correlacionada

Uma LATERALjunção (Postgres 9.3 ou posterior) é mais como uma subconsulta correlacionada , não como uma subconsulta simples. Como Andomar apontou , uma função ou subconsulta à direita de uma LATERALjunção deve ser avaliada uma vez para cada linha restante dela - exatamente como uma subconsulta correlacionada - enquanto uma subconsulta simples (expressão de tabela) é avaliada apenas uma vez . (No entanto, o planejador de consultas tem maneiras de otimizar o desempenho.)
Essa resposta relacionada possui exemplos de código para os dois lados, resolvendo o mesmo problema:

Para retornar mais de uma coluna , uma LATERALjunção geralmente é mais simples, mais limpa e mais rápida.
Além disso, lembre-se de que o equivalente a uma subconsulta correlacionada é LEFT JOIN LATERAL ... ON true:

Leia o manual em LATERAL

É mais autoritário do que qualquer coisa que colocaremos em respostas aqui:

Coisas que uma subconsulta não pode fazer

Não são coisas que uma LATERALjunção pode fazer, mas um (correlacionado) subconsulta não pode (facilmente). Uma subconsulta correlacionada pode retornar apenas um único valor, não várias colunas e nem várias linhas - com exceção das chamadas de função simples (que multiplicam as linhas de resultado se retornarem várias linhas). Mas até certas funções de retorno de conjunto são permitidas apenas na FROMcláusula. Como unnest()com vários parâmetros no Postgres 9.4 ou posterior. O manual:

Isso é permitido apenas na FROMcláusula;

Portanto, isso funciona, mas não pode ser facilmente substituído por uma subconsulta:

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

A vírgula ( ,) na FROMcláusula é uma notação curta para CROSS JOIN.
LATERALé assumido automaticamente para funções da tabela.
Mais sobre o caso especial de UNNEST( array_expression [, ... ] ):

Funções de retorno de conjunto na SELECTlista

Você também pode usar funções de retorno de conjunto como unnest()na SELECTlista diretamente. Isso costumava exibir um comportamento surpreendente com mais de uma função na mesma SELECTlista até o Postgres 9.6. Mas finalmente foi higienizado com o Postgres 10 e é uma alternativa válida agora (mesmo que não seja o SQL padrão). Vejo:

Com base no exemplo acima:

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

Comparação:

dbfiddle para a página 9.6 aqui
dbfiddle para a página 10 aqui

Esclarecer informações erradas

O manual:

Para os tipos INNERe OUTERjoin, uma condição de join deve ser especificada, a saber, exatamente um de NATURAL, ON join_condition ou USING( join_column [, ...]). Veja abaixo o significado.
Pois CROSS JOIN, nenhuma dessas cláusulas pode aparecer.

Portanto, essas duas consultas são válidas (mesmo que não sejam particularmente úteis):

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;

Enquanto este não é:

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

É por isso que o exemplo de código do @ Andomar está correto (o CROSS JOINnão requer uma condição de junção) e o @ do Attila é inválido.


Existem algumas coisas que uma subconsulta pode fazer uma LATERAL JOIN não pode fazer. Como funções da janela. Como aqui
Evan Carroll

@EvanCarroll: Não encontrei nenhuma subconsulta correlacionada no link. Mas acrescentei outra resposta para demonstrar uma função de janela em uma LATERALsubconsulta: gis.stackexchange.com/a/230070/7244
Erwin Brandstetter

1
Mais limpo e mais rápido? Como magnitudes mais rápidas em alguns casos. Eu tive uma consulta que passou de dias para segundos depois de mudar para LATERAL.
Rovyko 18/10/19

51

A diferença entre uma junção não laterale uma lateraljunção está em saber se você pode olhar para a linha da tabela à esquerda. Por exemplo:

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

Essa "visão externa" significa que a subconsulta deve ser avaliada mais de uma vez. Afinal, t1.col1pode assumir muitos valores.

Por outro lado, a subconsulta após uma não lateralassociação pode ser avaliada uma vez:

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

Como é necessário sem lateral, a consulta interna não depende de forma alguma da consulta externa. Uma lateralconsulta é um exemplo de correlatedconsulta, devido à sua relação com linhas fora da própria consulta.


5
Esta é a explicação mais clara da junção lateral.
Valdis

explicação fácil de entender, obrigado.
arilwan 16/06

como se select * from table1 left join t2 using (col1)compara? Não está claro para mim quando uma junção usando / na condição é insuficiente e faria mais sentido usar lateral.
No_name

9

Primeiro, a aplicação lateral e cruzada é a mesma coisa . Portanto, você também pode ler sobre o Cross Apply. Desde que foi implementado no SQL Server há muito tempo, você encontrará mais informações sobre ele do que o Lateral.

Segundo, de acordo com meu entendimento , não há nada que você não possa fazer usando subconsulta em vez de usar lateral. Mas:

Considere a seguinte 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 

Você pode usar lateral nessa condição.

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

Nesta consulta, você não pode usar a junção normal, devido à cláusula limit. A aplicação lateral ou cruzada pode ser usada quando não há uma condição de junção simples .

Existem mais usos para aplicação lateral ou cruzada, mas essa é a mais comum que encontrei.


1
Exatamente, eu me pergunto por que o PostgreSQL usa em lateralvez de apply. Talvez a Microsoft tenha patenteado a sintaxe?
Andomar 16/02

9
O @Andomar AFAIK lateralestá no padrão SQL, mas applynão está.
mu é muito curto

O LEFT JOINrequer uma condição de junção. Faça a ON TRUEmenos que você queira restringir de alguma forma.
Erwin Brandstetter

Erwin está certo, você receberá um erro, a menos que use uma cross joinou uma oncondição
Andomar

1
@Andomar: Estimulado por esta desinformação, adicionei outra resposta para esclarecer.
Erwin Brandstetter

4

Uma coisa que ninguém apontou é que você pode usar LATERALconsultas para aplicar uma função definida pelo usuário em todas as linhas selecionadas.

Por exemplo:

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);

Essa é a única maneira de saber como fazer esse tipo de coisa no PostgreSQL.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.