[Dúvida SQL] Retornar o valor 0 com comando COUNT

23 respostas
Mariana_

Pessoal, bom dia!

Estou desenvolvendo uma aplicação estatística para analisar respostas de um questionário socieconômico de alunos ingressantes de uma universidade. Preciso fazer consultas do tipo “Quantidade de mulheres entre 17 e 20 anos” (só um exemplo). Estou utilizando o comando COUNT nos SQLs e está tudo funcionando, porém encontrei um pequeno problema: quando não há nenhuma mulher entre 17 e 20 anos, o resultado da consulta não retorna linha nenhuma e eu precisava que retornasse o valor 0. Tentei usar COALESCE(nome_do_campo, 0), porém não funciona. Já pesquisei bastante na net. Alguém poderia me ajudar a resolver isso ou realmente não tem solução via SQL?

Abraços,
Mariana

23 Respostas

pmlm

Que BD usas? Em todos os que trabalhei sempre tenho ideia de devolver 0 quando não há registos.

drsmachado

Um case não resolve o problema?
Um if?
Decode?

Mariana_

Estou usando MySQL e um CASE também não funcionou… já utilzei Firebird e SQL Server e me lembro que isso funcionava nesses BDs. O estranho dessa situação é que nem um NULL é exibido, ou seja, não mostra linha nenhuma no resultado quando não há dados.

drsmachado

Posta o case que você criou.

Mariana_
SELECT A.DESCRICAO, 
CASE WHEN COUNT(AL.ID) > 0 THEN COUNT(AL.ID) ELSE 0 END AS TOTAL 
FROM ALUNOS AL 
    INNER JOIN RESPOSTAS R ON R.ALUNO = AL.ID AND R.PERGUNTA = 5 
    INNER JOIN ALTERNATIVAS A ON R.ALTERNATIVA = A.ID 
WHERE AL.ANO = 2012 
GROUP BY A.LETRA
ORDER BY A.LETRA
Rodrigo_Sasaki

já tentou usar IFNULL ao invés do COALESCE?

FabricioPJ

No Oracle, ao executar a seguinte query, que retornará 0 linhas…

… a saída é o número 0. Não sei bem como deve funcionar isso no MySQL, mas deveria ter o mesmo comportamento, visto que essa é uma cláusula comum a todos os bancos que adotam o padrão ANSI SQL. Não ocorre nenhum erro durante a execução de seu código?

Mariana_

Esse comando eu não conheço, vou dar uma pesquisada e tentar usar

Mariana_

FabricioPJ:
No Oracle, ao executar a seguinte query, que retornará 0 linhas…

… a saída é o número 0. Não sei bem como deve funcionar isso no MySQL, mas deveria ter o mesmo comportamento, visto que essa é uma cláusula comum a todos os bancos que adotam o padrão ANSI SQL. Não ocorre nenhum erro durante a execução de seu código?

Então, no Firebird e SQL Server só assim também sairia 0 D:
Não dá nenhum erro, não. Os valores aparecem corretamente para a query que eu postei nos comentários de cima ai, porém a linha que não tem dados não é exibida ao invés de mostrar 0

Rodrigo_Sasaki

A sintaxe de utilização dela é igual a do COALESCE, só que é específica do MySQL, se não me engano// Com IFNULL SELECT ProductName,UnitPrice*(UnitsInStock+IFNULL(UnitsOnOrder,0)) FROM Products // Com COALESCE SELECT ProductName,UnitPrice*(UnitsInStock+COALESCE(UnitsOnOrder,0)) FROM Productshttp://www.w3schools.com/sql/sql_isnull.asp

drsmachado

Seria algo assim

SELECT A.DESCRICAO,   
CASE WHEN COUNT(AL.ID) IS NOT NULL THEN COUNT(AL.ID) ELSE 0 END AS TOTAL   
FROM ALUNOS AL   
    INNER JOIN RESPOSTAS R ON R.ALUNO = AL.ID AND R.PERGUNTA = 5   
    INNER JOIN ALTERNATIVAS A ON R.ALTERNATIVA = A.ID   
WHERE AL.ANO = 2012   
GROUP BY A.LETRA  
ORDER BY A.LETRA

Ou

SELECT A.DESCRICAO,   
IF COUNT(AL.ID) IS NOT NULL THEN COUNT(AL.ID) ELSE 0 END AS TOTAL   
FROM ALUNOS AL   
    INNER JOIN RESPOSTAS R ON R.ALUNO = AL.ID AND R.PERGUNTA = 5   
    INNER JOIN ALTERNATIVAS A ON R.ALTERNATIVA = A.ID   
WHERE AL.ANO = 2012   
GROUP BY A.LETRA  
ORDER BY A.LETRA
A

Acho que o seu problema é que você está gerando os totais a partir dos resultados e não a partir de uma lista de referência.

Imagine por exemplo que quero contar o percentual de torcedores de 5 times, e a tabela tenha os seguintes registros:

ID          NOME            TIME
1           ABEL            PALMEIRAS
2           BRUNO         CORINTHIANS
3           CARLOS       SÃO PAULO
4           DANIEL          SANTOS

E espera o seguinte resultado:

TIME                                 QUANTIDADE
PALMEIRAS                       1
CORINTHIANS                   1
SÃO PAULO                      1
SANTOS                           1
JUVENTUS                        0

Baseado na primeira tabela apenas você nunca conseguirá o resultado abaixo, pois ele nem sabe da existência do Juventus pra começar.

O que você precisaria nesse caso é fazer uma LEFT JOIN da tabela de Times com a tabela de Torcedores.

No seu caso, você precisa criar registros de referência para fazer LEFT JOIN com as respostas dadas.

Mariana_

digaoneves:
A sintaxe de utilização dela é igual a do COALESCE, só que é específica do MySQL, se não me engano// Com IFNULL SELECT ProductName,UnitPrice*(UnitsInStock+IFNULL(UnitsOnOrder,0)) FROM Products // Com COALESCE SELECT ProductName,UnitPrice*(UnitsInStock+COALESCE(UnitsOnOrder,0)) FROM Productshttp://www.w3schools.com/sql/sql_isnull.asp

A função IFFNULL não foi reconhecida aqui não

Mariana_

drsmachado:
Seria algo assim

SELECT A.DESCRICAO,   
CASE WHEN COUNT(AL.ID) IS NOT NULL THEN COUNT(AL.ID) ELSE 0 END AS TOTAL   
FROM ALUNOS AL   
    INNER JOIN RESPOSTAS R ON R.ALUNO = AL.ID AND R.PERGUNTA = 5   
    INNER JOIN ALTERNATIVAS A ON R.ALTERNATIVA = A.ID   
WHERE AL.ANO = 2012   
GROUP BY A.LETRA  
ORDER BY A.LETRA

Ou

SELECT A.DESCRICAO, IF COUNT(AL.ID) IS NOT NULL THEN COUNT(AL.ID) ELSE 0 END AS TOTAL FROM ALUNOS AL INNER JOIN RESPOSTAS R ON R.ALUNO = AL.ID AND R.PERGUNTA = 5 INNER JOIN ALTERNATIVAS A ON R.ALTERNATIVA = A.ID WHERE AL.ANO = 2012 GROUP BY A.LETRA ORDER BY A.LETRA

O primeiro rodou, porém ainda não funcionou D:
O segundo está dando erro de sintaxe

drsmachado

Veja a dica do abelbueno. Inner joins só consideram os valores que existem nas duas tabelas. Caso queira valores, independente de existirem em A ou B, precisa do left join.

Mariana_

AbelBueno, entendi o que você quis dizer, porém eu tenho uma tabela ALTERNATIVAS que contém as opções “16 anos”, “17 anos”, “18 anos”, etc. Só que nenhum registro da tabela ALUNOS está vinculado à alternativa “16 anos”. Nesse caso ele saberia da existência do “16 anos”, certo? Seria o caso de mudar pra LEFT JOIN então?

Rodrigo_Sasaki

É, isso ta bem estranho, a gente até poderia sugerir outras opções de como verificar se o resultado está vindo Null, como:SELECT A.DESCRICAO, CASE WHEN ISNULL(COUNT(AL.ID)) THEN 0 ELSE COUNT(AL.ID) END AS TOTAL FROM ALUNOS AL INNER JOIN RESPOSTAS R ON R.ALUNO = AL.ID AND R.PERGUNTA = 5 INNER JOIN ALTERNATIVAS A ON R.ALTERNATIVA = A.ID WHERE AL.ANO = 2012 GROUP BY A.LETRA ORDER BY A.LETRA Porém aparentemente o problema é que não está retornando nada (o que eu nunca vi acontecer), porque se estivesse vindo nulo, o COALESCE ja resolveria.

Mariana_

Sim!!

Mudei a junção das tabelas para LEFT e deu na mesma, não sei o que fazer mais e é realmente importante que esses zeros apareçam =/

drsmachado

Então, como diria o Jack, vamos por partes. Posta as duas tabelas para vermos.

Rodrigo_Sasaki

Independente de INNER ou LEFT, o seu problema na verdade não é esse (pode ser que seja um outro problema, mas não o em questão), de qualquer maneira, se nada fosse encontrado, deveria retornar um 0.

Vi esse post procurando no google, não sei o que tem dentro porque não tenho acesso, mas pelo nome parece que pode te ajudar.

A

Três observações:

  • O LEFT JOIN dá prioridade a tabela do lado esquerdo no select. Existe também o RIGHT JOIN, para dar prioridade ao lado direito.

  • Se manter um INNER JOIN no meio da query, pode anular o efeito do LEFT/RIGHT join anterior.

  • Se colocar no WHERE alguma condição que dependa do lado sem prioridade, você também vai anular o efeito do LEFT/RIGHT join.

Mariana_

SELECT A.DESCRICAO, COALESCE(COUNT(AL.ID), 0) AS TOTAL
FROM ALUNOS AL 
    INNER JOIN CURSOS C ON C.ID = AL.CURSO
    INNER JOIN RESPOSTAS R ON R.ALUNO = AL.ID AND R.PERGUNTA = 2 
    INNER JOIN RESPOSTAS R1 ON R1.ALUNO = AL.ID AND R1.PERGUNTA = 1 
    INNER JOIN ALTERNATIVAS A ON R.ALTERNATIVA = A.ID 
    INNER JOIN ALTERNATIVAS A1 ON R1.ALTERNATIVA = A1.ID 
WHERE AL.ANO = 2012 AND C.CAMPUS = 5
GROUP BY A.LETRA, A1.LETRA 
ORDER BY A.LETRA, A1.LETRA;

Resultado obtido desse SELECT:

17 anos -> 6
18 anos -> 8
18 anos -> 14
19 anos -> 3
19 anos -> 4
20 anos -> 2
20 anos -> 5
21 anos -> 3
21 anos -> 4
22 a 24 anos -> 2
22 a 24 anos -> 1
25 a 29 anos -> 4
25 a 29 anos -> 8
30 anos ou mais -> 2
30 anos ou mais -> 6

Resultado ESPERADO (pois a pergunta 2 tem oito alternativas e a pergunta 1 tem duas):

[color=red]17 anos -> 0[/color]
17 anos -> 6
18 anos -> 8
18 anos -> 14
19 anos -> 3
19 anos -> 4
20 anos -> 2
20 anos -> 5
21 anos -> 3
21 anos -> 4
22 a 24 anos -> 2
22 a 24 anos -> 1
25 a 29 anos -> 4
25 a 29 anos -> 8
30 anos ou mais -> 2
30 anos ou mais -> 6

Mariana_

Vou fazer uns testes aqui então com RIGHT, etc (acredito que o conteúdo da cláusula WHERE não dará problema)

Criado 15 de junho de 2012
Ultima resposta 15 de jun. de 2012
Respostas 23
Participantes 6