PostgreSQL - correlacionados Sub-consulta falhar?

votos
1

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

Eu 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?

Publicado 08/01/2009 em 23:11
fonte usuário
Em outras línguas...                            


5 respostas

votos
1

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;
Respondeu 08/01/2009 em 23:15
fonte usuário

votos
10

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 correlacionadas. Vai ser muito mais rápido.
  • Utilizar a cláusula HAVING para filtrar grupos.

Aqui é 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;

Sei que esta consulta pode pular o JOINcom 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 WHEREcláusula é avaliada em linhas, antes GROUP BYreduz grupos para uma única linha por grupo. A HAVINGcláusula é 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, WHEREFiltros para linhas correspondentes uma condição, e HAVINGfiltros para os grupos que têm pelo menos cinco contagem.

O ponto que causa confusão a maioria das pessoas é quando eles não têm uma GROUP BYcláusula, por isso parece como HAVINGe WHEREsão intercambiáveis.

WHEREé avaliada antes expressões na lista de seleção. Isto pode não ser óbvio porque a sintaxe SQL coloca o select-lista em primeiro lugar. Então você pode salvar um monte de computação caro usando WHEREpara 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 HAVINGcondição. No entanto, a consulta abaixo calcula a expressão apenas para a única linha correspondentes a WHEREcondição.

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

  1. Gerar um conjunto de linhas da tabela (s), incluindo quaisquer linhas produzidas pela JOIN.
  2. Avaliar WHEREcondições contra o conjunto de linhas, filtrando as linhas que não correspondem.
  3. Calcule expressões na lista de seleção para cada no conjunto de linhas.
  4. 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).
  5. Condensar grupos de uma única linha por grupo, de acordo com a GROUP BYcláusula.
  6. Avaliar HAVINGcondições contra grupos, filtrando grupos que não combinam.
  7. Ordenar resultar, de acordo com a ORDER BYcláusula.
Respondeu 08/01/2009 em 23:18
fonte usuário

votos
3

Todas as outras sugestões iria funcionar, mas para responder à sua pergunta básica que seria suficiente para escrever

  SELECT id  From T2
  Group By Id
  Having Count(*) = 5
Respondeu 08/01/2009 em 23:18
fonte usuário

votos
0

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
Respondeu 08/01/2009 em 23:20
fonte usuário

votos
3

Eu gostaria de mencionar que no PostgreSQL não há nenhuma maneira de usar coluna alias na cláusula having.

ou seja

SELECIONE usr_id AS my_id DE usuário ter my_id = 1

não vai funcionar.

Outro exemplo que não vai funcionar:

su.usr_id SELECT como 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 é conhecido.

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."

Respondeu 29/07/2009 em 15:37
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more