Como incluir resultados “zero” / “0” no agregado COUNT?

112

Eu estou um pouco preso a um pouco de SQL. Acho que não consigo formular a pergunta de maneira brilhante - deixe-me mostrar a você.

Tenho duas mesas, uma chamada pessoa e outra chamada compromisso. Estou tentando retornar o número de compromissos de uma pessoa (inclusive se eles tiverem zero). O compromisso contém o person_ide há um person_idpor compromisso. Portanto, COUNT(person_id)é uma abordagem sensata.

A pergunta:

SELECT person_id, COUNT(person_id) AS "number_of_appointments" 
FROM appointment 
GROUP BY person_id;

Retornará corretamente, o número de compromissos que um person_id possui. No entanto, uma pessoa que tem 0 compromissos não é devolvida (obviamente porque não está naquela mesa).

Ajustar a instrução para retirar person_id da tabela de pessoas me dá algo como:

SELECT person.person_id, COUNT(appointment.person_id) AS "number_of_appointments"
FROM appointment
JOIN person ON person.person_id = appointment.person_id
GROUP BY person.person_id;

No entanto, isso ainda retornará apenas um person_id que tenha um compromisso e não o que eu quero que é um retorno com pessoas que não tenham nenhum compromisso!

Alguma sugestão, por favor?

BrownE
fonte
1
E se eu quiser chegar a zero | 0 como resultado em uma única tabela. Eu tenho a tabela vm_tool_licenses e a consulta abaixo selecione vm_tool_id, contagem (vm_tool_license_active) do grupo vm_tool_licenses por vm_tool_license_active, vm_tool_id tendo vm_tool_license_active = false`
Narendra

Respostas:

101

Você quer uma junção externa para isso (e você precisa usar pessoa como a mesa "motriz")

SELECT person.person_id, COUNT(appointment.person_id) AS "number_of_appointments"
FROM person 
  LEFT JOIN appointment ON person.person_id = appointment.person_id
GROUP BY person.person_id;

O motivo pelo qual isso está funcionando é que a junção externa (esquerda) retornará NULLpara aquelas pessoas que não têm um compromisso. A função de agregação count()não contará NULLvalores e, portanto, você obterá um zero.

Se você quiser aprender mais sobre junções externas, aqui está um bom tutorial: http://sqlzoo.net/wiki/Using_Null

um cavalo sem nome
fonte
mas e se alguns dos campos do compromisso NÃO forem NULL (na definição da tabela)?
Lior Yehezkely
@LiorYehezkely: "algumas das colunas" não importa. A contagem () usa a coluna de junção. Se não for nulo, então há um compromisso que deve ser contado
a_horse_with_no_name
21

Você deve usar em LEFT JOINvez deINNER JOIN

SELECT person.person_id, COUNT(appointment.person_id) AS "number_of_appointments"
FROM person 
LEFT JOIN appointment ON person.person_id = appointment.person_id
GROUP BY person.person_id;
Hamlet Hakobyan
fonte
1
O GROUP BY parece um erro de digitação na pergunta original, pois a tabela não está incluída na consulta.
Joachim Isaksson
7

se você fizer a junção externa (com a contagem) e, em seguida, usar este resultado como uma subtabela, você pode obter 0 conforme o esperado (graças à função nvl)

Ex:

select P.person_id, nvl(A.nb_apptmts, 0) from 
(SELECT person.person_id
FROM person) P
LEFT JOIN 
(select person_id, count(*) as nb_apptmts
from appointment 
group by person_id) A
ON P.person_id = A.person_id
François Mazet
fonte
5

USE join para obter 0 contagem no resultado usando GROUP BY.

simplesmente 'join' faz o Inner join no MS SQL, então, vá para left ou right join.

Se a tabela que contém a chave primária for mencionada primeiro na QUERY, use a junção LEFT, senão a junção RIGHT.

POR EXEMPLO:

select WARDNO,count(WARDCODE) from MAIPADH 
right join  MSWARDH on MSWARDH.WARDNO= MAIPADH.WARDCODE
group by WARDNO

.

select WARDNO,count(WARDCODE) from MSWARDH
left join  MAIPADH on MSWARDH.WARDNO= MAIPADH.WARDCODE group by WARDNO

Pegue o agrupamento da mesa que possui a chave primária e conte a partir da outra mesa que possui entradas / detalhes reais.

Rohini CG
fonte
2

Para alterar ainda menos sua consulta original, você pode transformar sua associação em uma RIGHTassociação

SELECT person.person_id, COUNT(appointment.person_id) AS "number_of_appointments"
FROM appointment
RIGHT JOIN person ON person.person_id = appointment.person_id
GROUP BY person.person_id;

Isso apenas se baseia na resposta selecionada, mas como a junção externa está na RIGHTdireção, apenas uma palavra precisa ser adicionada e menos alterações. - Lembre-se de que ele está lá e, às vezes, pode tornar as consultas mais legíveis e exigir menos reconstrução.

sfj
fonte
0

O problema com LEFT JOIN é que se não houver compromissos, ele ainda retornará uma linha com um valor nulo, que quando agregado por COUNT se tornará 1, e parecerá que a pessoa tem um compromisso quando na verdade não tem nenhum. Acho que isso dará os resultados corretos:

SELECT person.person_id,
(SELECT COUNT(*) FROM appointment WHERE person.person_id = appointment.person_id) AS 'Appointments'
FROM person;
dannyw
fonte
Eu ia postar essa mesma solução, bem simples.
Joel