Como funciona o predicado ON do Postgres LATERAL JOIN?
Deixe-me esclarecer um pouco a questão. Eu li a documentação oficial e vários artigos sobre esse tipo de JOIN. Pelo que entendi é um loop foreach com uma subconsulta correlacionada dentro - ele itera sobre todos os registros de uma tabela A, permitindo referenciar colunas de uma linha "atual" em uma subconsulta correlacionada B e juntar um conjunto de resultados do B para aquela linha "atual" de A - se a consulta B retornar 1 linha, haverá apenas um par, e se a consulta B retornar N linhas, haverá N pares com a linha "atual" duplicada de A. O mesmo comportamento dos JOINs usuais .
Mas por que há necessidade do predicado ON? Para mim, em JOINs usuais usamos ON porque temos um produto cartesiano de 2 tabelas a ser filtrado, e não é o caso de LATERAL JOIN, que produz pares resultantes diretamente. Em outras palavras, na minha experiência de desenvolvedor eu só vi CROSS JOIN LATERAL e LEFT JOIN LATERAL () ON TRUE (este último parece bastante desajeitado), mas um dia um colega me mostrou
SELECT
r.acceptance_status, count(*) as count
FROM route r
LEFT JOIN LATERAL (
SELECT rts.route_id, array_agg(rts.shipment_id) shipment_ids
FROM route_to_shipment rts
where rts.route_id = r.route_id
GROUP BY rts.route_id
) rts using (route_id)
e isso explodiu minha mente. Por que using (route_id)
? Já temos where rts.route_id = r.route_id
dentro da subconsulta!!! Talvez eu tenha entendido errado a mecânica das junções LATERAL?
A cláusula USING (colunas) não duplica as colunas especificadas no conjunto de resultados, enquanto ON (ta.column=tb.column) duplica as colunas. Aqui a coluna duplicada é “ajuda”. No caso de um JOIN padrão sobre igualdade, as colunas serão iguais, portanto a duplicação é inútil, o que significa que USING é preferível. Também é mais legível. No caso de um JOIN externo (direita, esquerda, completo), você pode querer que as duas colunas sejam duplicadas para saber se uma delas é NULL.
Se você deseja um CROSS JOIN (sem condição ON):
Você também pode usar um JOIN e mover algumas das condições que estariam no WHERE da tabela LATERAL para a cláusula ON(), o resultado é o mesmo:
Mas não há CROSS LEFT JOIN, portanto, se você quiser um LEFT JOIN LATERAL, deverá declarar explicitamente LEFT JOIN, e isso requer a cláusula ON.
Na verdade, no caso de uma junção LATERAL, a cláusula ON pode ser supérflua.
Resposta curta:
LEFT JOIN
requer uma condição de junção - em oposição aCROSS JOIN
. Noções básicas no manual.Veja também:
Mas a condição de junção ainda pode fazer sentido para filtrar quais linhas anexar no lado direito depois de calcular um conjunto na subqery lateral. Como:
Isso retorna todas as linhas de table
route
, mas anexa apenasshipment_ids
onde mais de uma linha relacionada na tabelaroute_to_shipment
for encontrada.Não há necessidade de adicionar itens
rts.route_id
àSELECT
lista da subconsulta.GROUP BY rts.route_id
é apenas barulho depoisWHERE rts.route_id = r.route_id
.E ainda estou gerando o array
shipment_ids
em vão, como o original.Demonstrando também resultados diferentes para
count(*)
vs.count(shipment_ids)
A condição de junção não pode passar para a
WHERE
cláusula, onde teria um efeito diferente. Você pode adicionar umaHAVING
cláusula à suquery:Mas existem subconsultas laterais sem agregação (portanto, nenhuma
HAVING
cláusula é possível). Para o seu caso:violino
Só faz sentido se usarmos esse array, é claro. Então, de qualquer maneira, um construtor de array é provavelmente o ideal para sua consulta. Ver: