SQL'de aralıklarda nasıl “gruplama” yapabilirsiniz?


181

Sayısal sütunlu bir tablom olduğunu varsayalım ("skor" diyelim).

Her aralıkta puanların kaç kez göründüğünü gösteren bir sayım tablosu oluşturmak istiyorum.

Örneğin:

skor aralığı | oluşum sayısı
-------------------------------------
   0-9 | 11
  10-19 | 14
  20-29 | 3
   ... | ...

Bu örnekte, 0-9 arasında puan alan 11 satır, 10-19 arasında puan alan 14 satır ve 20-29 aralığında puan alan 3 satır vardı.

Bunu ayarlamanın kolay bir yolu var mı? Ne önerirsiniz?

Yanıtlar:


143

En yüksek oylanan cevapların hiçbiri SQL Server 2000'de doğru değildir. Belki de farklı bir sürüm kullanıyorlardı.

SQL Server 2000'de her ikisinin de doğru sürümleri.

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

veya

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

Başka bir sütunu da toplayabilir miyim (grup sayıları gibi). her bir skor aralığı için burs sütununu topladığımı varsayalım. Denedim, ama doğru yapamadım
Munish Goyal

Güzel cevap @ Ron Tuffin, ancak 10-20, 100-200 gibi iki aralığınız olduğunda, sipariş çalışmaz. gibi sipariş olurdu 10-20, 100-200,20-30 vb tarafından sipariş için herhangi bir ipucu?
Zo

2
@ZoHas biraz hack ama bu işe yarıyor: len (t.range) tarafından sipariş, t.range
Ron Tuffin


1
Hala sözdizimi sorunlarınız varsa, şu yanıtı kontrol edin: dba.stackexchange.com/questions/22491/…
Robert Hosking

33

Alternatif bir yaklaşım, aralıkları sorguya gömmek yerine bir tabloda depolamayı içerir. Bir tablo ile sonuçlanır, buna Ranges adı verilir, şöyle görünür:

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

Ve şuna benzer bir sorgu:

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

Bu, bir tablo oluşturmak anlamına gelir, ancak istenen aralıklar değiştiğinde bakımı kolaydır. Kod değişikliği gerekmez!


Değişken Kova Aralıklarını Kullanarak Desenli Veriler İçin Veritabanı Yöneticileri Tablosu Tasarımı hakkında bir soru sordum , ancak cevap verdiğiniz aralıkları olan bir sistem tasarladım. Bu yanıtı seviyorum.
MmegaMan

31

Burada SQL Server'ın sözdiziminde çalışmayan cevaplar görüyorum. Kullanmak istiyorum:

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: yorumlara bakın


Muhtemelen kullandığım SQLServer sürümü nedeniyle ama örnek almak için (bir şey onları oylamadan önce test) Ben 'durum' sonra her 'ne zaman' sonra 'puan' taşımak zorunda kaldı.
Ron Tuffin

3
Haklısın ve düzeltme için teşekkürler. Görünüşe göre değişkeni 'case' anahtar kelimesinden sonra koyduğunuzda, ifadeleri değil yalnızca tam eşleşmeleri yapabilirsiniz. Soruları cevaplamaktan çok soruları yanıtlayarak öğreniyorum. :-)
Ken Paul

23

Postgres'de ( ||dize birleştirme operatörü nerede ):

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

verir:

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

11

James Curran'ın cevabı bence en özlü, ama çıktı doğru değildi. SQL Server için en basit ifade aşağıdaki gibidir:

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

Bu, test etmek için kullandığım #Scores geçici tablosunu varsayar, sadece 0 ile 99 arasında rasgele sayı ile 100 satır doldurdum.


1
Ah ... Masayı oluşturmak için zaman ayırmanın avantajı var. (Çok küçük bir aralıkta çok az satır içeren mevcut bir tablo kullandım)
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

Teşekkürler! Bunu denedim ve kullanmak zorunda olduğum sözdizimi biraz farklı olsa da temel fikir harika çalışıyor. Yalnızca ilk "case" anahtar kelimesine ihtiyaç duyulur ve daha sonra son koşuldan sonra "range" olarak önce "end" anahtar kelimesine ihtiyacınız vardır. Bunun dışında çok çalıştı - teşekkürler!
Hugh

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

Bunu beğendim, ancak sorguyu görüntüleyecekseniz sorgu dışındaki aralıkları düzeltmeniz gerekir.
tvanfosson

Cevabınızı düzeltmeye karar vermeniz durumunda ilk satırdaki skorunuzu / 10'unuzu (skor / 10) * 10 olarak değiştirmeniz gerekir, aksi takdirde her ikisi için 30-39 vb. Yerine 3-12 alırsınız. Yazıma göre Aşağıda sonuçları doğru sırayla almak için bir sipariş ekleyebilirsiniz.
Timothy Walters

5

Bu, aralıkları belirtmenize gerek kalmayacak ve SQL sunucusu agnostik olmalıdır. Matematik FTW!

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

3

Bunu her seferinde tanımlamak zorunda kalmadan ölçeklendirmek için biraz farklı yapacağım:

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

Test edilmedi, ama fikri anladınız ...


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'deyseniz, 'range' dışında bir kelime kullandığınızdan emin olun, aksi takdirde yukarıdaki örneği çalıştırırken hata alırsınız.


1

( Range) Öğesinde sıralanan sütun bir dize olduğundan, sayısal sıralama yerine dize / kelime sıralama kullanılır.

Dizelerde sayı uzunluklarını doldurmak için sıfırlar olduğu sürece sıralama hala anlamsal olarak doğru olmalıdır:

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

Aralık karıştırılırsa, fazladan bir sıfır ekleyin:

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

Deneyin

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
sorgunuzun sorunu nasıl çözdüğü hakkında bazı açıklamalar ekleyebilmeniz yararlı olacaktır.
devlin carnate

-1

Belki de böyle şeyleri devam ettirmek istiyorsun ...

Tabii ki sorgular için tam bir masa taraması çağıracaksınız ve tartışılması gereken puanları içeren tablolar (toplamalar) büyükse daha iyi performans gösteren bir çözüm isteyebilirsiniz, ikincil bir tablo oluşturabilir ve kurallar kullanabilirsiniz. on insert- içine bakabilirsiniz.

Yine de tüm RDBMS motorlarının kuralları yoktur!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.