Como contar ocorrências de um valor de coluna com eficiência no SQL?

166

Eu tenho uma tabela de alunos:

id | age
--------
0  | 25
1  | 25
2  | 23

Quero consultar todos os alunos e uma coluna adicional que conte quantos alunos têm a mesma idade:

id | age | count
----------------
0  | 25  | 2
1  | 25  | 2
2  | 23  | 1

Qual é a maneira mais eficiente de fazer isso? Receio que uma subconsulta seja lenta e estou me perguntando se existe uma maneira melhor . Existe?

Assaf Lavie
fonte

Respostas:

255

Isso deve funcionar:

SELECT age, count(age) 
  FROM Students 
 GROUP by age

Se você precisar do ID, poderá incluir o acima como uma subconsulta da seguinte forma:

SELECT S.id, S.age, C.cnt
  FROM Students  S
       INNER JOIN (SELECT age, count(age) as cnt
                     FROM Students 
                    GROUP BY age) C ON S.age = C.age
Mike Dinescu
fonte
2
para a segunda consulta, a seleção externa deve estar em C.cnt porque não há S.cnt; caso contrário, você receberá um erro: Nome de coluna inválido 'cnt'
KM.
1
está dando erro para mim quando estou usando select case_id, count (pgm_code) do grupo pgm por pgm_code; -lo dizendo não um grupo de expressão
Rishabh Agarwal
26

Se você estiver usando o Oracle, um recurso chamado analytics fará o truque. Se parece com isso:

select id, age, count(*) over (partition by age) from students;

Se você não estiver usando o Oracle, precisará voltar às contagens:

select a.id, a.age, b.age_count
  from students a
  join (select age, count(*) as age_count
          from students
         group by age) b
    on a.age = b.age
Jeremy Bourque
fonte
2
FYI, no SQL Server 2005, a segunda consulta é executada com quase metade do custo de execução (usando SET SHOWPLAN_ALL ON ) como a primeira. Eu pensei que o primeiro teria sido melhor, mas a junção da velha escola venceu.
KM.
1
"junte-se à velha escola", simplesmente porque a CONTAGEM DE LINHA TOTAL a ser processada é diferente. Na segunda consulta, há um agrupamento incorporado que reduz potencialmente o número de linhas. Tente adicionar distinta para a primeira consulta: "select id DISTINCT, idade, count (*) sobre (partição por idade) dos alunos" - que devem ser comparáveis
quetzalcoatl
19

Aqui está outra solução. este usa uma sintaxe muito simples. O primeiro exemplo da solução aceita não funcionou em versões mais antigas do Microsoft SQL (ou seja, 2000)

SELECT age, count(*)
FROM Students 
GROUP by age
ORDER BY age
Damian
fonte
1
No entanto, se você agrupar por idade, obteria apenas uma entrada para os 25 anos com uma contagem de 2 (quando eles realmente desejam duas entradas com uma contagem de 2 e IDs separados para o exemplo dado)?
24417 Ian
1
Ian, obrigado pelo feedback. Você executou sua reivindicação em um banco de dados MS SQL 2000?
Damian
7

Eu faria algo como:

select
 A.id, A.age, B.count 
from 
 students A, 
 (select age, count(*) as count from students group by age) B
where A.age=B.age;
quosoo
fonte
4
select s.id, s.age, c.count
from students s
inner join (
    select age, count(*) as count
    from students
    group by age
) c on s.age = c.age
order by id
RedFilter
fonte
1

e se os dados na coluna "idade" tiverem registros semelhantes (ou seja, muitas pessoas têm 25 anos, muitas outras 32 e assim por diante), isso causa confusão no alinhamento da contagem correta para cada aluno. a fim de evitá-lo, juntei-me às tabelas de identificação de estudante.

SELECT S.id, S.age, C.cnt
FROM Students S 
INNER JOIN (SELECT id, age, count(age) as cnt  FROM Students GROUP BY student,age) 
C ON S.age = C.age *AND S.id = C.id*
afii_palang
fonte