AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 333909
Accepted
Nikita Glukhov
Nikita Glukhov
Asked: 2023-12-10 13:30:58 +0800 CST2023-12-10 13:30:58 +0800 CST 2023-12-10 13:30:58 +0800 CST

Predicado ON de LATERAL JOINs do Postgres

  • 772

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_iddentro da subconsulta!!! Talvez eu tenha entendido errado a mecânica das junções LATERAL?

postgresql
  • 2 2 respostas
  • 51 Views

2 respostas

  • Voted
  1. bobflux
    2023-12-10T18:38:24+08:002023-12-10T18:38:24+08:00
    CREATE TABLE ta (aid INT, a INT);
    CREATE TABLE tb (aid INT, b INT);
    INSERT INTO ta VALUES (1,10),(2,20);
    INSERT INTO tb VALUES (1,100),(1,200);
    SELECT * FROM ta LEFT JOIN LATERAL (SELECT * FROM tb WHERE tb.aid=ta.aid) ON true;
    
     aid | a  | aid  |  b
    -----+----+------+------
       1 | 10 |    1 |  100
       1 | 10 |    1 |  200
       2 | 20 | Null | Null
    
    SELECT * FROM ta LEFT JOIN LATERAL (SELECT * FROM tb) USING (aid);
    
     aid | a  |  b
    -----+----+------
       1 | 10 |  100
       1 | 10 |  200
       2 | 20 | Null
    

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

    SELECT * FROM ta CROSS JOIN LATERAL (SELECT * FROM tb WHERE tb.aid=ta.aid);
    

    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:

    SELECT * FROM ta JOIN LATERAL (SELECT * FROM tb WHERE ...) ON (tb.aid=ta.aid);
    

    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.

    SELECT * FROM ta JOIN LATERAL (SELECT * FROM tb WHERE tb.aid=ta.aid) ON true WHERE ta.aid<10;
    

    Na verdade, no caso de uma junção LATERAL, a cláusula ON pode ser supérflua.

    • 1
  2. Best Answer
    Erwin Brandstetter
    2023-12-11T20:44:47+08:002023-12-11T20:44:47+08:00

    Resposta curta: LEFT JOIN requer uma condição de junção - em oposição a CROSS JOIN. Noções básicas no manual.
    Veja também:

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

    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:

    SELECT r.acceptance_status
         , count(*) AS count_routes
         , count(rts.shipment_ids) AS count_routes_with_more_than_one_shipment
    FROM   route r
    LEFT   JOIN LATERAL (
       SELECT array_agg(rts.shipment_id) shipment_ids
            , count(*) AS shipments
       FROM   route_to_shipment rts
       WHERE  rts.route_id = r.route_id
       -- GROUP  BY rts.route_id  -- just noise
       ) rts ON shipments > 1;  -- !!!
    

    Isso retorna todas as linhas de table route, mas anexa apenas shipment_idsonde mais de uma linha relacionada na tabela route_to_shipmentfor encontrada.

    Não há necessidade de adicionar itens rts.route_idà SELECTlista da subconsulta.
    GROUP BY rts.route_idé apenas barulho depois WHERE rts.route_id = r.route_id.
    E ainda estou gerando o array shipment_idsem 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 WHEREcláusula, onde teria um efeito diferente. Você pode adicionar uma HAVINGcláusula à suquery:

    SELECT r.acceptance_status
         , count(*) AS ct_routes
         , count(rts.shipment_ids) AS ct_routes_with_more_than_1_shipment
    FROM   route r
    LEFT   JOIN LATERAL (
       SELECT array_agg(rts.shipment_id) shipment_ids
       FROM   route_to_shipment rts
       WHERE  rts.route_id = r.route_id
       HAVING count(*) > 1  -- !!!
       ) rts ON true
    GROUP  BY r.acceptance_status;
    

    Mas existem subconsultas laterais sem agregação (portanto, nenhuma HAVINGcláusula é possível). Para o seu caso:

    SELECT r.acceptance_status
         , count(*) AS ct_routes
         , count(rts.shipment_ids) AS ct_routes_with_more_than_1_shipment
    FROM   route r
    LEFT   JOIN LATERAL (
       SELECT ARRAY (
          SELECT rts.shipment_id
          FROM   route_to_shipment rts
          WHERE  rts.route_id = r.route_id
          ) AS shipment_ids
       ) rts ON cardinality(shipment_ids) > 1  -- !!!
    GROUP  BY r.acceptance_status;
    

    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:

    • Por que array_agg() é mais lento que o construtor ARRAY() não agregado?
    • 1

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve