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 / 168460
Accepted
eComEvo
eComEvo
Asked: 2017-03-29 17:50:50 +0800 CST2017-03-29 17:50:50 +0800 CST 2017-03-29 17:50:50 +0800 CST

Postgres join onde a tabela estrangeira tem TODOS os registros

  • 772

Eu tenho isso peoplee tagsmesa, assim,

CREATE TABLE people
AS
  SELECT *
  FROM ( VALUES
    (1,'Joe'),
    (2,'Jane')
  ) AS t(id,name);

CREATE TABLE tags
AS
  SELECT * FROM ( VALUES
    (1, 1, 'np'),
    (2, 1, 'yw'),
    (3, 2, 'np')
  ) AS t(id, people_id, tag);

Se eu quiser encontrar tudo o peopleque contém as tags npe na tabela usando uma junção, como eu faria isso de forma eficiente no Postgres 9.6?ywtags

Nesse cenário, devo apenas obter o registro de Joe da peopletabela.

postgresql postgresql-9.6
  • 5 5 respostas
  • 636 Views

5 respostas

  • Voted
  1. mendosi
    2017-03-29T19:49:40+08:002017-03-29T19:49:40+08:00

    Aqui estão algumas abordagens alternativas que não envolvem o uso do array_agg.

    Use o INTERSECToperador contra os conjuntos de people_idretornados para cada tag:

    WITH both_tags AS (
        SELECT people_id FROM tags WHERE tag = 'np'
        INTERSECT 
        SELECT people_id FROM tags WHERE tag = 'yw')
    SELECT *
      FROM people 
      WHERE id IN (SELECT people_id FROM both_tags);
    

    Ou você pode usar a COUNT(DISTINCT tag) = 2para encontrar pessoas com ambas as tags. (Observe que o DISTINCTfoi adicionado para lidar com o caso de uma pessoa ter a mesma tag duas vezes. Se isso for impossível, é seguro removê-la.)

    WITH both_tags AS (
        SELECT people_id
          FROM tags
          WHERE tag IN ('np', 'yw')
          GROUP BY people_id
          HAVING COUNT(DISTINCT tag) = 2)
    SELECT *
      FROM people 
      WHERE id IN (SELECT people_id FROM both_tags);
    

    Essa segunda abordagem seria mais fácil de estender para aceitar um número arbitrário de tags, embora a primeira abordagem não fosse impossível.

    • 3
  2. ypercubeᵀᴹ
    2017-03-30T13:37:37+08:002017-03-30T13:37:37+08:00

    Mais duas maneiras - que usam junções ou subconsultas correlacionadas - e não GROUP BY:

    A primeira usa EXISTSsubconsultas:

    select p.id, p.name
    from people as p 
    where exists (select from tags as t where t.people_id = p.id and t.tag = 'np')
      and exists (select from tags as t where t.people_id = p.id and t.tag = 'yw')
    ;
    

    O segundo assume uma UNIQUErestrição em (tag, people_id):

    select p.id, p.name
    from people as p 
      join tags as t1 on t1.people_id = p.id and t1.tag = 'np'
      join tags as t2 on t1.people_id = p.id and t2.tag = 'yw'
    ;
    

    Testes em dbfiddle.uk .

    Confira também esta questão no SO, com mais de 10 maneiras de resolver esse tipo de problema - e análise de desempenho: Como filtrar resultados SQL em uma relação tem-muitos-através .

    Existe até uma tag para eles: divisão relacional

    Pode ser surpreendente, mas na maioria das vezes o método many join, o many existe e os semelhantes (como o que usa INTERSECT) são mais eficientes que os group by / countmétodos. Mas é claro que há muitos detalhes que importam para o desempenho. Parâmetros de consulta, tamanhos de tabela, índices, distribuições de dados e muito mais podem afetar o desempenho dos vários métodos.

    • 3
  3. Evan Carroll
    2017-03-29T19:00:03+08:002017-03-29T19:00:03+08:00

    Aqui selecionamos todas as pessoas e agregamos em array as tags. Fazemos isso em uma única passagem. Em seguida, agrupamos isso em uma subseleção e encontramos todas as correspondências em que elas têm np, e yw.

    SELECT people_id, name, array_agg(tag) AS tags
    FROM people
    JOIN tags ON (people_id = people.id)
    GROUP BY people_id, name
    HAVING array_agg(tag) @> ARRAY['np', 'yw'];
    
     id | name |  tags   
    ----+------+---------
      1 | Joe  | {np,yw}
    (1 row)
    

    Às vezes você pode fazer isso mais rápido, empurrando para baixo a condição

    SELECT people_id, name, array_agg(tag) AS tags
    FROM people
    JOIN tags ON (people_id = people.id)
    
    -- push down
    WHERE tag IN ('np', 'yw')
    
    GROUP BY people_id, name
    HAVING array_agg(tag) @> ARRAY['np', 'yw'];
    

    Você também pode simplesmente colocar a matriz de tags peoplediretamente. Em seguida, consultá-lo torna-se muito simples.

    • 2
  4. Best Answer
    joanolo
    2017-03-29T23:06:48+08:002017-03-29T23:06:48+08:00

    Uma pequena variante da resposta de mendosi , que evita WITH:

    SELECT *
    FROM people 
    WHERE id IN 
    (    
          SELECT people_id
          FROM tags
          WHERE tag IN ('np', 'yw')
          GROUP BY people_id
          HAVING COUNT(DISTINCT tag) = 2
    );
    
    identificação | nome
     -: | :---
      1 | João

    Esta abordagem tem algumas pequenas diferenças em relação à sua abordagem:

    • Se você usa um banco de dados não lida com WITHinstruções (não é o caso do PostgreSQL há muito tempo)
    • Você não se sente confortável comWITH
    • Você quer evitar o fato de que, no PostgreSQL, WITHsão cercas de otimização; e (a partir de hoje) impedir que , eventualmente, o banco de dados execute algumas otimizações.
    • Isso deve estar muito próximo do padrão SQL completo e funciona em todos os bancos de dados disponíveis no DBFiddle (a partir de hoje).

    dbfiddle aqui

    Se você está procurando a solução mais rápida possível , eu verificaria diferentes abordagens em condições práticas e decidiria com base nos tempos que você realmente obtém. Minha consulta proposta é muito padrão e não deve ser mais lenta que aquela com um WITH, mas se é mais lenta ou mais rápida que outras abordagens, eu realmente não sei de antemão.

    • 2
  5. Jack Douglas
    2017-03-30T11:29:17+08:002017-03-30T11:29:17+08:00

    Outra maneira com um simples equi-join:

    select p.id, name
    from people p join tags on tags.people_id=p.id
    where tag in ('np','yw')
    group by p.id, name
    having count(distinct tag)=2;
    
    identificação | nome
    -: | :---
     1 | João
    

    dbfiddle aqui

    • 2

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