SQLでは、どのように範囲内で「グループ化」できますか?


181

数値列のあるテーブルがあるとします(「スコア」と呼びましょう)。

各範囲でスコアが出現した回数を示すカウントのテーブルを生成したいと思います。

例えば:

スコア範囲| 発生数
-------------------------------------
   0-9 | 11
  10-19 14
  20-29 | 3
   ... | ...

この例では、スコアが0〜9の範囲にある11行、スコアが10〜19の範囲にある14行、およびスコアが20〜29の範囲にある3行がありました。

これを設定する簡単な方法はありますか?何がお勧めですか?

回答:


143

SQLServer 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

別の列も集計できますか(グループ数など)。各スコアの範囲の奨学金の列を集計したいとします。試してみましたが、うまくいきませんでした
ミュニッシュゴヤール

@Ron Tuffinのいい答えですが、10-20、100-200のような2つの範囲がある場合、順序は機能しません。10-20、100-200、20-30などの注文になります。注文のヒントはありますか?
Zoは2014年

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

これはテーブルを設定することを意味しますが、目的の範囲が変更されたときに維持するのは簡単です。コードを変更する必要はありません。


Database Administrators Table Design for Patterned Data Using Variable Bucket Rangesに質問がありましたが、回答がありませんでしたが、あなたが述べた範囲を持つシステムを設計することになりました。この答えが大好きです。
Ω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のバージョンが原因である可能性がありますが、サンプルを機能させるには(投票する前にテストを行います)、「スコア」を「ケース」の後から各「いつ」の後に移動する必要がありました。
Ron Tuffin 2008年

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 '24年

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 2008年

回答を修正する場合は、最初の行のスコア/ 10を(score / 10)* 10に変更する必要があります。それ以外の場合は、30〜39ではなく3〜12になります。私の投稿のとおり以下で、順序を追加して、正しい順序で結果を取得できます。
ティモシーウォルターズ

5

これにより、範囲を指定する必要がなくなり、SQLサーバーに依存しなくなります。数学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を使用している場合は、「範囲」以外の単語を使用してください。そうしないと、上記の例を実行するとエラーが発生します。


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 carnate 2015年

-1

おそらくあなたはそのようなことを続けることについて尋ねています...

もちろん、クエリの全テーブルスキャンを呼び出します。集計する必要があるスコア(集計)を含むテーブルが大きい場合は、より良いパフォーマンスのソリューションが必要になる場合があります。セカンダリテーブルを作成して、次のようなルールを使用できます。on insert-あなたはそれを調べるかもしれません。

ただし、すべてのRDBMSエンジンにルールがあるわけではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.