Como obter um grupo em que a contagem é zero?

12

Vou tentar fazer um gráfico com os dados do meu banco de dados SQL Server. Vou ter todas as ruas com a contagem dos usuários que moram nessa rua, mesmo que a contagem seja zero.

Para isso, eu tentei esta consulta:

Create table Streets(
  ID int IDENTITY  primary key,
  Name varchar(100)
);

create table users(
  ID int IDENTITY  primary key,
  Username varchar(100),
  StreetID int references Streets(id)
);

insert into streets values ('1st street'), ('2nd street'), ('3rd street'), 
                           ('4th street'), ('5th street');
insert into users values ('Pol', 1), ('Doortje', 1), ('Marc', 2), ('Bieke', 2), 
                         ('Paulien', 2), ('Fernand', 2), ('Pascal', 2), ('Boma', 3), 
                         ('Goedele', 3), ('Xavier', 4);

select s.name as street, count(s.name) as count 
from users u inner join streets s on u.streetid = s.id
group by s.name

E isso me dá essa saída:

|   | street     | count |
| - | ---------- | ----- |
| 1 | 1st street | 2     |
| 2 | 2nd street | 5     |
| 3 | 3rd street | 2     |
| 4 | 4th street | 1     |

O problema é que a 5ª rua, onde nenhum usuário mora, não aparece no resultado. Eu poderia fazer isso com o SQL server? Aqui você tem um violino

Atualização: Se o fizer right join, obtive este resultado:

|   | street     | count |
| - | ---------- | ----- |
| 1 | 1st street | 2     |
| 2 | 2nd street | 5     |
| 3 | 3rd street | 2     |
| 4 | 4th street | 1     |
| 5 | 5th street | 1     | 

Veja este violino.

H. Pauwelyn
fonte
4
Como ninguém explicou por que sua consulta não retorna o resultado esperado: Como função agregada ignora NULLs, você deve contar uma coluna da tabela interna (contada na tabela externa) que é conhecida por ser definida como NOT NULL (para poder distinguir entre NULLs dentro dos dados e NULL criado pelo Outer Join). A maneira mais fácil é contar a coluna de junção:COUNT(u.streetid)
dnoeth
Porque right joine right outer joinsão as mesmas coisas. Eu adicionei uma explicação na minha resposta, como sugerido por @ jpmc26.
SqlWorldWide

Respostas:

17

O motivo de sua consulta não funcionar como pretendido:

A junção interna fornece a interseção de 2 tabelas. No seu caso, não havia nenhuma entrada 5th streetna tabela de usuários e é por isso que a associação não produziu nenhuma entrada para isso.

A junção externa (direita ou esquerda) fornecerá o resultado da junção interna e, além disso, todos os registros não qualificados da tabela esquerda ou direita, dependendo do tipo (esquerda ou direita) da junção externa.

Nesse caso, coloquei Street à esquerda da junção e usei a junção externa esquerda como você desejava em todas as ruas (a contagem par é zero) no seu conjunto de resultados.

Altere sua consulta de seleção para isso.

SELECT S.Name AS Street,
       Count(U.Username) AS COUNT
FROM Streets S
LEFT OUTER JOIN Users U ON U.Streetid = S.Id
GROUP BY S.Name

Resultado insira a descrição da imagem aqui

SqlWorldWide
fonte
11
Para mim, mudar de count (*) para count (customer.id) - semelhante ao mostrado acima - fez a diferença crítica. Obrigado :)
Zeek 15/02
9

Esta é uma maneira possível.

select s.name as streets,
       (select count(*)
        from   users
        where  StreetID = s.id) cnt
from   streets s;
McNets
fonte
7

Limpando o código para funcionar em uma instância que diferencia maiúsculas de minúsculas ...

CREATE TABLE Streets
(
    ID INT IDENTITY PRIMARY KEY,
    Name VARCHAR(100)
);

CREATE TABLE users
(
    ID INT IDENTITY PRIMARY KEY,
    Username VARCHAR(100),
    StreetID INT
        REFERENCES Streets ( ID )
);

INSERT INTO Streets
VALUES ( '1st street' ),
    ( '2nd street' ),
    ( '3rd street' ),
    ( '4th street' ),
    ( '5th street' );
INSERT INTO users
VALUES ( 'Pol', 1 ),
    ( 'Doortje', 1 ),
    ( 'Marc', 2 ),
    ( 'Bieke', 2 ),
    ( 'Paulien', 2 ),
    ( 'Fernand', 2 ),
    ( 'Pascal', 2 ),
    ( 'Boma', 3 ),
    ( 'Goedele', 3 ),
    ( 'Xavier', 4 );

Quando você usa COUNTcom um nome de coluna, ele conta NOT NULLvalores.

Estou usando um RIGHT JOINaqui para apaziguar Joe Obbish.

SELECT   s.Name AS street, COUNT(u.Username) AS count
FROM     users AS u
RIGHT JOIN Streets AS s
ON u.StreetID = s.ID
GROUP BY s.Name

Resultados:

street      count
1st street  2
2nd street  5
3rd street  2
4th street  1
5th street  0
Erik Darling
fonte
0
  1. Obter a contagem por ID da rua
  2. junte o ID da rua com o ID das ruas
  3. Use Coalsesce, pois o valor nulo resultará

Aqui está a consulta curta:

select Name, coalesce( u.ct,0)ct FROM streets s left join (
select StreetID,count(*)ct from users group by StreetID)u on s.ID=u.StreetID
rakesh
fonte
Ou seja, com junção esquerda
rakesh