Dalam SQL, bagaimana Anda bisa "mengelompokkan" dalam rentang?

181

Misalkan saya memiliki tabel dengan kolom numerik (sebut saja "skor").

Saya ingin menghasilkan tabel hitungan, yang menunjukkan berapa kali skor muncul di setiap rentang.

Sebagai contoh:

rentang skor | jumlah kejadian
-------------------------------------
   0-9 | 11
  10-19 | 14
  20-29 | 3
   ... | ...

Dalam contoh ini ada 11 baris dengan skor di kisaran 0 hingga 9, 14 baris dengan skor di kisaran 10 hingga 19, dan 3 baris dengan skor di kisaran 20-29.

Apakah ada cara mudah untuk mengatur ini? Apa yang kamu sarankan?

Hugh
sumber

Jawaban:

143

Tak satu pun dari jawaban pilihan tertinggi benar pada SQL Server 2000. Mungkin mereka menggunakan versi yang berbeda.

Berikut adalah versi yang benar dari keduanya di SQL Server 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

atau

select t.range as [score range], count(*) as [number of occurrences]
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
sumber
Bisakah saya menggabungkan kolom lain juga (seperti jumlah grup). katakanlah saya ingin mengumpulkan kolom beasiswa untuk setiap rentang skor. Saya mencoba, tetapi tidak melakukannya dengan benar
Munish Goyal
Jawaban yang bagus @Ron Tuffin, namun ketika Anda memiliki dua rentang seperti 10-20, 100-200, maka urutannya tidak berfungsi. Anda akan memiliki pesanan seperti 10-20, 100-200,20-30 dll. Ada tip untuk pesanan oleh?
Zo Has
2
@ZoMemiliki sedikit peretasan tetapi ini bekerja: dipesan oleh len (t.range), t.range
Ron Tuffin
Jawaban yang lebih baik di stackoverflow.com/questions/14730380/…
Thunder
1
Jika Anda masih memiliki masalah sintaksis, periksa jawaban ini: dba.stackexchange.com/questions/22491/…
Robert Hosking
33

Pendekatan alternatif akan melibatkan menyimpan rentang dalam tabel, alih-alih menyematkannya dalam kueri. Anda akan berakhir dengan sebuah meja, sebut saja Ranges, yang terlihat seperti ini:

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

Dan permintaan yang terlihat seperti ini:

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

Ini berarti menyiapkan tabel, tetapi akan mudah untuk mempertahankannya ketika rentang yang diinginkan berubah. Tidak diperlukan perubahan kode!

Walter Mitty
sumber
Saya mengajukan pertanyaan tentang Desain Tabel Administrator Database Untuk Data Berpola Menggunakan Variable Bucket Ranges yang tidak mendapatkan jawaban, tetapi saya akhirnya merancang sistem yang memiliki rentang yang Anda sebutkan. Sukai jawaban ini.
ΩmegaMan
31

Saya melihat jawaban di sini yang tidak akan berfungsi dalam sintaks SQL Server. Saya akan menggunakan:

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

Sunting: lihat komentar

Ken Paul
sumber
Mungkin karena versi SQLServer yang saya gunakan tetapi untuk mendapatkan contoh Anda untuk bekerja (saya menguji hal-hal sebelum saya memilihnya) saya harus memindahkan 'skor' dari setelah 'kasus' ke setelah setiap 'kapan'.
Ron Tuffin
3
Anda benar, dan terima kasih atas koreksinya. Tampaknya ketika Anda meletakkan variabel setelah kata kunci 'huruf', Anda hanya dapat melakukan pencocokan tepat, bukan ekspresi. Saya belajar banyak dari menjawab pertanyaan dan dari menanyakannya. :-)
Ken Paul
23

Dalam postgres (di mana ||operator string concatenation):

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

memberi:

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

Jawaban James Curran adalah yang paling ringkas menurut saya, tetapi hasilnya tidak benar. Untuk SQL Server pernyataan paling sederhana adalah sebagai berikut:

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

Ini mengasumsikan tabel sementara #Scores yang saya gunakan untuk mengujinya, saya baru saja mengisi 100 baris dengan angka acak antara 0 dan 99.

Timothy Walters
sumber
1
Ah ... Ada keuntungan sebenarnya meluangkan waktu untuk membuat tabel. (Saya menggunakan meja yang sudah ada dengan terlalu sedikit baris pada rentang yang terlalu kecil)
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
tvanfosson
sumber
Terima kasih! Saya mencoba ini dan ide dasarnya bekerja dengan baik, walaupun sintaks yang saya gunakan sedikit berbeda. Hanya kata kunci "case" pertama yang diperlukan dan kemudian setelah kondisi terakhir, sebelum "as range" Anda memerlukan kata kunci "end". Selain itu, bekerja dengan baik- terima kasih!
Hugh
5
select cast(score/10 as varchar) + '-' + cast(score/10+9 as varchar), 
       count(*)
from scores
group by score/10
James Curran
sumber
Saya suka ini, tetapi Anda harus memperbaiki rentang di luar kueri jika Anda akan menampilkannya.
tvanfosson
Jika Anda memutuskan untuk memperbaiki jawaban Anda, Anda perlu mengubah skor Anda / 10 pada baris pertama menjadi (skor / 10) * 10 untuk keduanya jika tidak Anda mendapatkan 3 - 12 bukannya 30-39 dll. Sesuai posting saya di bawah ini Anda dapat menambahkan pesanan dengan untuk mendapatkan hasil dalam urutan yang benar.
Timothy Walters
5

Ini akan memungkinkan Anda untuk tidak harus menentukan rentang, dan harus agnostik SQL server. FTW Matematika!

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

Saya akan melakukan ini sedikit berbeda sehingga skala tanpa harus mendefinisikan setiap kasus:

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

Tidak diuji, tetapi Anda mendapatkan idenya ...

JoshNaro
sumber
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)
Aheho
sumber
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

Pastikan Anda menggunakan kata selain 'rentang' jika Anda menggunakan MySQL, atau Anda akan mendapatkan kesalahan saat menjalankan contoh di atas.

Danny Hui
sumber
1

Karena kolom yang diurutkan pada ( Range) adalah string, pengurutan string / kata digunakan bukan pengurutan numerik.

Selama string memiliki nol untuk menambah panjang angka, penyortiran harus secara semantik benar:

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

Jika rentang tersebut dicampur, cukup masukkan nol tambahan:

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
Kevin Hogg
sumber
1

Mencoba

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;
Stubo
sumber
3
akan sangat membantu jika Anda dapat menambahkan beberapa penjelasan tentang bagaimana permintaan Anda menyelesaikan masalah.
devlin carnate
-1

Mungkin Anda bertanya tentang menjaga hal-hal seperti itu berjalan ...

Tentu saja Anda akan meminta pemindaian tabel penuh untuk kueri dan jika tabel yang berisi skor yang perlu dihitung (agregasi) besar, Anda mungkin menginginkan solusi yang berkinerja lebih baik, Anda bisa membuat tabel sekunder dan menggunakan aturan, seperti on insert - Anda mungkin melihatnya.

Namun, tidak semua mesin RDBMS memiliki aturan!

Richard T
sumber