在SQL中,如何对范围进行“分组”?


181

假设我有一个带有数字列的表(我们称其为“分数”)。

我想生成一张计数表,该表显示每个范围内得分出现的次数。

例如:

分数范围| 出现次数
-------------------------------------
   0-9 | 11
  10-19 | 14
  20-29 | 3
   ... | ...

在此示例中,有11行的分数在0到9之间,有14行的分数在10到19之间,还有3行的分数在20-29之间。

有一个简单的方法来设置它吗?您有什么推荐的吗?

Answers:


143

在SQL Server 2000上,投票率最高的答案均不正确。也许它们使用的是其他版本。

这是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

要么

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

我是否也可以汇总另一列(如组计数)。说我想汇总每个分数范围的奖学金列。我尝试了一下,但没有弄对
Munish Goyal

@Ron Tuffin是个不错的答案,但是,当您有两个范围(例如10-20,100-200)时,该命令将不起作用。您将收到10-20、100-200、20-30等订单。有关此订单的提示?
Zo

2
@ZoHas有点黑,但这行得通:len(t.range),t.range的订购
Ron Tuffin


1
:如果您仍然有语法问题,检查这个答案dba.stackexchange.com/questions/22491/...
罗伯特·霍斯金

33

另一种方法是将范围存储在表中,而不是将其嵌入查询中。您将得到一个表,将其称为Ranges,如下所示:

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

和一个查询,看起来像这样:

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

这确实意味着要建立一个表格,但是当所需范围更改时,将易于维护。无需更改代码!


我问了一个有关使用可变桶范围的模式化数据的数据库管理员表设计的问题,但没有得到答案,但最终我设计了一个具有您提到的范围的系统。喜欢这个答案。
ΩmegaMan

31

我在这里看到的答案在SQL Server的语法中不起作用。我会用:

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

编辑:查看评论


可能是因为我使用的是SQLServer版本,但为了使您的示例正常工作(我在对事情进行投票之前先进行了测试),我不得不将“得分”从“案例”之后移到每个“时间”之后。
罗恩·塔芬

3
您说得对,感谢您的纠正。显然,将变量放在关键字“ case”之后时,只能进行完全匹配,而不能进行表达式匹配。我从回答问题到向他们学习都学到了很多东西。:-)
肯·保罗

23

在postgres中(||字符串连接运算符在哪里):

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

给出:

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

11

在我看来,詹姆斯·柯兰(James Curran)的回答是最简洁的,但是输出是不正确的。对于SQL Server,最简单的语句如下:

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

假设我曾经使用#Scores临时表进行测试,我只是填充了100行,其随机数介于0到99之间。


1
啊。。。花点时间创建表是有好处的。(我使用的现有表行数太少,范围太小)
James Curran,

5
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

谢谢!尽管我必须使用的语法略有不同,但我尝试了这一操作,并且基本思路很好用。仅需要第一个“ case”关键字,然后在最后一个条件之后,在“ as range”之前,您需要关键字“ end”。除此之外,还不错,谢谢!

5
select cast(score/10 as varchar) + '-' + cast(score/10+9 as varchar), 
       count(*)
from scores
group by score/10

我喜欢这样,但是如果要显示查询,则必须修复查询之外的范围。
tvanfosson

如果您决定解决问题,则需要将第一行的得分/ 10都更改为(分数/ 10)* 10,否则您的得分将为3-12(而不是30-39),依此类推。您可以在下面添加订单,以按正确的顺序获取结果。
蒂莫西·沃尔特斯

5

这将使您不必指定范围,并且应与SQL Server无关。数学FTW!

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

3

我会做一些不同的事情,以便无需定义每种情况就可以扩展:

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

未经测试,但您知道了...


2
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)

1
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

如果您在MySQL中,请确保使用'range'以外的其他词,否则运行上述示例将会出错。


1

因为在(Range)上排序的列是字符串,所以使用字符串/单词排序而不是数字排序。

只要字符串有零来填充数字长度,排序在语义上仍然应该是正确的:

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

如果范围是混合的,只需填充一个额外的零即可:

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

尝试

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;

3
如果您可以添加有关查询如何解决问题的一些说明,将会很有帮助。
devlin康乃馨

-1

也许您在问要让这种事情继续下去...

当然,您将为查询调用全表扫描,并且如果包含需要计算分数(汇总)的表很大,则可能需要更好的性能解决方案,则可以创建辅助表并使用规则,例如on insert-您可能会调查。

但是,并非所有RDBMS引擎都有规则!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.