PostgreSQL - Correlated Sub-consulta falhar?
-
06-07-2019 - |
Pergunta
Eu tenho uma consulta como esta:
SELECT t1.id,
(SELECT COUNT(t2.id)
FROM t2
WHERE t2.id = t1.id
) as num_things
FROM t1
WHERE num_things = 5;
O objetivo é obter o ID de todos os elementos que aparecem 5 vezes na outra tabela. No entanto, eu recebo este erro:
ERROR: column "num_things" does not exist
SQL state: 42703
Provavelmente estou fazendo algo bobo aqui, como eu sou um pouco novo para bancos de dados. Existe uma maneira de corrigir essa consulta para que eu possa acessar num_things
? Ou, se não, existe alguma outra maneira de alcançar este resultado?
Solução
Eu acho que você poderia simplesmente reescrever sua consulta assim:
SELECT t1.id
FROM t1
WHERE (SELECT COUNT(t2.id)
FROM t2
WHERE t2.id = t1.id
) = 5;
Outras dicas
Alguns pontos importantes sobre como usar SQL:
- Você não pode usar apelidos de coluna na cláusula WHERE, mas você pode na cláusula HAVING. Essa é a causa do erro que você tem.
- Você pode fazer a sua contagem de melhor utilizar um JOIN e GROUP BY que usando subconsultas correlacionados. Vai ser muito mais rápido.
- Use a cláusula HAVING para grupos de filtros.
Esta é a maneira que eu ia escrever esta consulta:
SELECT t1.id, COUNT(t2.id) AS num_things
FROM t1 JOIN t2 USING (id)
GROUP BY t1.id
HAVING num_things = 5;
Eu percebo esta consulta pode ignorar a JOIN
com t1, como na solução de Charles Bretana. Mas eu suponho que você pode querer a consulta para incluir algumas outras colunas de T1.
Re: a pergunta no comentário:
A diferença é que a cláusula WHERE
é avaliada em linhas, antes GROUP BY
reduz grupos para uma única linha por grupo. A cláusula HAVING
é avaliada depois os grupos são formados. Então você não pode, por exemplo, mudar o COUNT()
de um grupo usando HAVING
; você só pode excluir o próprio grupo.
SELECT t1.id, COUNT(t2.id) as num
FROM t1 JOIN t2 USING (id)
WHERE t2.attribute = <value>
GROUP BY t1.id
HAVING num > 5;
Na consulta acima, filtros WHERE
para linhas correspondentes uma condição e filtros HAVING
para grupos que têm pelo menos cinco contagem.
O ponto que faz com que a maioria das pessoas confusão é quando eles não têm uma cláusula GROUP BY
, por isso parece como HAVING
e WHERE
são intercambiáveis.
WHERE
é avaliada antes expressões na lista de seleção. Isto pode não ser óbvio, porque coloca sintaxe SQL do select-lista em primeiro lugar. Então você pode salvar um monte de computação caro usando WHERE
para restringir linhas.
SELECT <expensive expressions>
FROM t1
HAVING primaryKey = 1234;
Se você usar uma consulta como o acima, as expressões na lista de seleção são computados para cada linha , apenas para descartar a maioria dos resultados por causa da condição HAVING
. No entanto, a consulta abaixo calcula a expressão apenas para o única linha combinando a condição WHERE
.
SELECT <expensive expressions>
FROM t1
WHERE primaryKey = 1234;
Então, para recapitular, as consultas são executadas pelo mecanismo de banco de dados de acordo com a série de etapas:
- Gerar conjunto de linhas da tabela (s), incluindo todas as linhas produzidas pela
JOIN
. - Avaliar as condições
WHERE
contra o conjunto de linhas, filtrando as linhas que não correspondem. - expressões de computação em select-list para cada no conjunto de linhas.
- Aplicar aliases de coluna (note que este é um passo separado, o que significa que você não pode usar aliases em expressões na lista de seleção).
- grupos condense em uma única linha por grupo, de acordo com a cláusula
GROUP BY
. - Avaliar as condições
HAVING
contra grupos, filtrando grupos que não correspondem. - Classificar resultar, de acordo com a cláusula
ORDER BY
.
Todas as outras sugestões iria funcionar, mas para responder à sua pergunta básica que seria suficiente para gravação
SELECT id From T2
Group By Id
Having Count(*) = 5
Eu gostaria de mencionar que no PostgreSQL não há nenhuma maneira de usar coluna alias na cláusula having.
i.
SELECIONAR usr_id AS my_id DE usuário ter my_id = 1
não vai funcionar.
Outro exemplo que não está indo para o trabalho:
SELECIONAR su.usr_id AS my_id, COUNT (*) AS val DE sys_user AS su GROUP BY su.usr_id TENDO val> = 1
Não será o mesmo erro:. Coluna val não é conhecida
Im highliting isso porque Bill Karwin escreveu alguma coisa não é realmente verdadeiro para Postgres:
"Você não pode usar apelidos de coluna na cláusula WHERE, mas você pode na cláusula HAVING. Essa é a causa do erro que você tem."
Tente este
SELECT t1.id,
(SELECT COUNT(t2.id) as myCount
FROM t2
WHERE t2.id = t1.id and myCount=5
) as num_things
FROM t1