web-dev-qa-db-fra.com

En SQL, comment pouvez-vous "grouper par" dans des plages?

Supposons que j'ai un tableau avec une colonne numérique (appelons-le "score").

J'aimerais générer un tableau de comptes indiquant le nombre de fois où des scores sont apparus dans chaque plage.

Par exemple:

 plage de scores | nombre d'occurrences 
 ------------------------------------- 
 0-9 | 11 
 10-19 | 14 
 20-29 | 3 
 ... | ... 

Dans cet exemple, il y avait 11 lignes avec des scores allant de 0 à 9, 14 lignes avec des scores de 10 à 19 et 3 lignes avec des scores de 20 à 29.

Y a-t-il un moyen facile de le configurer? Que recommandez-vous?

168
Hugh

Aucune des réponses les plus votées n'est correcte sur SQLServer 2000. Ils utilisaient peut-être une version différente.

Voici les versions correctes des deux sur SQLServer 2000.

select t.range as [score range], count(*) as [number of occurences]
from (
  select case  
    when score between 0 and 9 then ' 0- 9'
    when score between 10 and 19 then '10-19'
    else '20-99' end as range
  from scores) t
group by t.range

ou

select t.range as [score range], count(*) as [number of occurences]
from (
      select user_id,
         case when score >= 0 and score< 10 then '0-9'
         when score >= 10 and score< 20 then '10-19'
         else '20-99' end as range
     from scores) t
group by t.range
132
Ron Tuffin

Une approche alternative consisterait à stocker les plages dans une table, au lieu de les incorporer dans la requête. Vous vous retrouveriez avec une table, appelez-la Ranges, qui ressemble à ceci:

LowerLimit   UpperLimit   Range 
0              9          '0-9'
10            19          '10-19'
20            29          '20-29'
30            39          '30-39'

Et une requête qui ressemble à ceci:

Select
   Range as [Score Range],
   Count(*) as [Number of Occurences]
from
   Ranges r inner join Scores s on s.Score between r.LowerLimit and r.UpperLimit
group by Range

Cela signifie qu’il faut mettre en place une table, mais il serait facile à maintenir lorsque les plages souhaitées changent. Aucun changement de code nécessaire!

32
Walter Mitty

Je vois ici des réponses qui ne fonctionneront pas dans la syntaxe de SQL Server. J'utiliserais:

select t.range as [score range], count(*) as [number of occurences]
from (
  select case 
    when score between  0 and  9 then ' 0-9 '
    when score between 10 and 19 then '10-19'
    when score between 20 and 29 then '20-29'
    ...
    else '90-99' end as range
  from scores) t
group by t.range

EDIT: voir les commentaires

30
Ken Paul

En postgres (où || est l'opérateur de concaténation de chaînes):

select (score/10)*10 || '-' || (score/10)*10+9 as scorerange, count(*)
from scores
group by score/10
order by 1

donne:

 scorerange | count 
------------+-------
 0-9        |    11
 10-19      |    14
 20-29      |     3
 30-39      |     2
21
mhawke

La réponse de James Curran était la plus concise à mon avis, mais le résultat n'était pas correct. Pour SQL Server, l’instruction la plus simple est la suivante:

SELECT 
    [score range] = CAST((Score/10)*10 AS VARCHAR) + ' - ' + CAST((Score/10)*10+9 AS VARCHAR), 
    [number of occurrences] = COUNT(*)
FROM #Scores
GROUP BY Score/10
ORDER BY Score/10

Cela suppose une table temporaire #Scores que j'ai utilisée pour la tester. Je viens de remplir 100 lignes avec un nombre aléatoire compris entre 0 et 99.

10
Timothy Walters
create table scores (
   user_id int,
   score int
)

select t.range as [score range], count(*) as [number of occurences]
from (
      select user_id,
         case when score >= 0 and score < 10 then '0-9'
         case when score >= 10 and score < 20 then '10-19'
         ...
         else '90-99' as range
     from scores) t
group by t.range
5
tvanfosson
select cast(score/10 as varchar) + '-' + cast(score/10+9 as varchar), 
       count(*)
from scores
group by score/10
5
James Curran

Cela vous permettra de ne pas avoir à spécifier de plages et devrait être agnostique pour SQL Server. Math FTW!

SELECT CONCAT(range,'-',range+9), COUNT(range)
FROM (
  SELECT 
    score - (score % 10) as range
  FROM scores
)
4
trevorgrayson

Je le ferais un peu différemment pour qu'il évolue sans avoir à définir chaque cas:

select t.range as [score range], count(*) as [number of occurences]
from (
  select FLOOR(score/10) as range
  from scores) t
group by t.range

Non testé, mais vous avez l'idée ...

3
JoshNaro
declare @RangeWidth int

set @RangeWidth = 10

select
   Floor(Score/@RangeWidth) as LowerBound,
   Floor(Score/@RangeWidth)+@RangeWidth as UpperBound,
   Count(*)
From
   ScoreTable
group by
   Floor(Score/@RangeWidth)
2
Aheho

Comme la colonne en cours de tri (Range) est une chaîne, le tri chaîne/mot est utilisé à la place du tri numérique.

Tant que les chaînes ont des zéros pour compléter les longueurs de nombres, le tri doit toujours être correct sémantiquement:

SELECT t.range AS ScoreRange,
       COUNT(*) AS NumberOfOccurrences
  FROM (SELECT CASE
                    WHEN score BETWEEN 0 AND 9 THEN '00-09'
                    WHEN score BETWEEN 10 AND 19 THEN '10-19'
                    ELSE '20-99'
               END AS Range
          FROM Scores) t
 GROUP BY t.Range

Si la plage est mélangée, il suffit de remplir un zéro supplémentaire:

SELECT t.range AS ScoreRange,
       COUNT(*) AS NumberOfOccurrences
  FROM (SELECT CASE
                    WHEN score BETWEEN 0 AND 9 THEN '000-009'
                    WHEN score BETWEEN 10 AND 19 THEN '010-019'
                    WHEN score BETWEEN 20 AND 99 THEN '020-099'
                    ELSE '100-999'
               END AS Range
          FROM Scores) t
 GROUP BY t.Range
1
Kevin Hogg

Essayer

SELECT (str(range) + "-" + str(range + 9) ) AS [Score range], COUNT(score) AS [number of occurances]
FROM (SELECT  score,  int(score / 10 ) * 10  AS range  FROM scoredata )  
GROUP BY range;
1
Stubo
select t.blah as [score range], count(*) as [number of occurences]
from (
  select case 
    when score between  0 and  9 then ' 0-9 '
    when score between 10 and 19 then '10-19'
    when score between 20 and 29 then '20-29'
    ...
    else '90-99' end as blah
  from scores) t
group by t.blah

Assurez-vous que vous utilisez un mot autre que "range" si vous êtes dans MySQL, sinon vous obtiendrez une erreur en exécutant l'exemple ci-dessus.

1
Danny Hui