Eu tenho isso people
e tags
mesa, 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 people
que contém as tags np
e na tabela usando uma junção, como eu faria isso de forma eficiente no Postgres 9.6?yw
tags
Nesse cenário, devo apenas obter o registro de Joe da people
tabela.
Aqui estão algumas abordagens alternativas que não envolvem o uso do
array_agg
.Use o
INTERSECT
operador contra os conjuntos depeople_id
retornados para cada tag:Ou você pode usar a
COUNT(DISTINCT tag) = 2
para encontrar pessoas com ambas as tags. (Observe que oDISTINCT
foi adicionado para lidar com o caso de uma pessoa ter a mesma tag duas vezes. Se isso for impossível, é seguro removê-la.)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.
Mais duas maneiras - que usam junções ou subconsultas correlacionadas - e não
GROUP BY
:A primeira usa
EXISTS
subconsultas:O segundo assume uma
UNIQUE
restrição em(tag, people_id)
: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 osgroup by / count
mé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.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
, eyw
.Às vezes você pode fazer isso mais rápido, empurrando para baixo a condição
Você também pode simplesmente colocar a matriz de tags
people
diretamente. Em seguida, consultá-lo torna-se muito simples.Uma pequena variante da resposta de mendosi , que evita
WITH
:Esta abordagem tem algumas pequenas diferenças em relação à sua abordagem:
WITH
instruções (não é o caso do PostgreSQL há muito tempo)WITH
WITH
são cercas de otimização; e (a partir de hoje) impedir que , eventualmente, o banco de dados execute algumas otimizações.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.Outra maneira com um simples equi-join:
dbfiddle aqui