Comment compter efficacement les occurrences d'une valeur de colonne dans SQL?

166

J'ai une table d'étudiants:

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

Je souhaite interroger tous les élèves et une colonne supplémentaire qui compte le nombre d'élèves du même âge:

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

Quelle est la manière la plus efficace d'y parvenir? Je crains qu'une sous-requête ne soit lente et je me demande s'il existe un meilleur moyen . Y a-t-il?

Assaf Lavie
la source

Réponses:

256

Cela devrait fonctionner:

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

Si vous avez également besoin de l'identifiant, vous pouvez inclure ce qui précède en tant que sous-requête comme ceci:

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
la source
2
pour la deuxième requête, la sélection externe doit être sur C.cnt car il n'y a pas de S.cnt, sinon vous obtenez une erreur: Nom de colonne non valide 'cnt'
KM.
1
cela me donne une erreur lorsque j'utilise select case_id, count (pgm_code) du groupe pgm par pgm_code; il ne dit pas un groupe par expression
Rishabh Agarwal
26

Si vous utilisez Oracle, une fonctionnalité appelée analytique fera l'affaire. Cela ressemble à ceci:

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

Si vous n'utilisez pas Oracle, vous devrez revenir aux décomptes:

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
Jérémy Bourque
la source
2
Pour info, sur SQL Server 2005, la deuxième requête s'exécute avec près de la moitié du coût d'exécution (en utilisant SET SHOWPLAN_ALL ON ) comme première. Je pensais que le premier aurait été mieux, mais la vieille école l'a battu.
KM.
1
"old school join beat it" simplement parce que le TOTAL ROW COUNT à traiter est différent. Dans la deuxième requête, il y a un group-by intégré qui réduit potentiellement considérablement le nombre de lignes. Essayez d'ajouter DISTINCT à la première requête: "sélectionnez DISTINCT id, age, count (*) over (partition by age) from
Students
19

Voici une autre solution. celui-ci utilise une syntaxe très simple. Le premier exemple de solution acceptée ne fonctionnait pas sur les anciennes versions de Microsoft SQL (c'est-à-dire 2000)

SELECT age, count(*)
FROM Students 
GROUP by age
ORDER BY age
Damian
la source
1
Si vous groupez par âge, vous n'obtiendrez qu'une seule entrée pour 25 ans avec un nombre de 2 (quand ils veulent en fait 2 entrées avec un nombre de 2 et des identifiants séparés pour l'exemple donné)?
Ian
1
Ian, merci pour les commentaires. Avez-vous exécuté votre réclamation contre une base de données MS SQL 2000?
Damian
7

Je ferais quelque chose comme:

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
la source
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
la source
1

et si les données de la colonne «âge» ont des enregistrements similaires (c'est-à-dire que de nombreuses personnes ont 25 ans, beaucoup d'autres 32 ans et ainsi de suite), cela crée de la confusion en alignant le bon décompte sur chaque élève. afin de l'éviter, j'ai également rejoint les tables sur la carte d'étudiant.

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
la source