ใน SQL คุณสามารถ“ จัดกลุ่มตาม” ในช่วงได้อย่างไร?


181

สมมติว่าฉันมีตารางที่มีคอลัมน์ตัวเลข (ให้เรียกว่า "คะแนน")

ฉันต้องการสร้างตารางการนับที่แสดงจำนวนครั้งที่ปรากฏในแต่ละช่วง

ตัวอย่างเช่น:

ช่วงคะแนน จำนวนครั้ง
-------------------------------------
   0-9 | 11
  10-19 | 14
  20-29 | 3
   ... | ...

ในตัวอย่างนี้มี 11 แถวที่มีคะแนนอยู่ในช่วง 0 ถึง 9, 14 แถวพร้อมคะแนนในช่วง 10 ถึง 19 และ 3 แถวพร้อมคะแนนในช่วง 20-29

มีวิธีง่าย ๆ ในการตั้งค่านี้หรือไม่? คุณแนะนำเมนูใด?

คำตอบ:


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

ฉันสามารถรวมคอลัมน์อื่นด้วย (เช่นการนับกลุ่ม) บอกว่าฉันรวมคอลัมน์ทุนการศึกษาสำหรับแต่ละช่วงคะแนน ฉันพยายาม แต่ไม่ได้ทำให้ถูกต้อง
Munish Goyal

คำตอบที่ดี @Ron Tuffin อย่างไรก็ตามเมื่อคุณมีสองช่วงเช่น 10-20, 100-200 ดังนั้นลำดับไม่ทำงาน คุณจะมีคำสั่งเช่น 10-20, 100-200,20-30 ฯลฯ เคล็ดลับสำหรับการสั่งซื้อโดย?
Zo มี

2
@ ZoHas มันเป็นบิตของการแฮ็ก แต่มันใช้งานได้: เรียงลำดับโดย len (t.range), t.range
Ron Tuffin

คำตอบที่ดีกว่าที่stackoverflow.com/questions/14730380/ …
Thunder

1
หากคุณยังคงมีปัญหาเกี่ยวกับไวยากรณ์ให้ตรวจสอบคำตอบนี้: dba.stackexchange.com/questions/22491/…
Robert Hosking

33

วิธีอื่นจะเกี่ยวข้องกับการจัดเก็บช่วงในตารางแทนที่จะฝังไว้ในแบบสอบถาม คุณจะจบลงด้วยโต๊ะเรียกมันว่าช่วงที่มีลักษณะเช่นนี้:

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 เวอร์ชันที่ฉันกำลังใช้อยู่ แต่เพื่อให้ตัวอย่างของคุณทำงาน (ฉันทดสอบสิ่งต่าง ๆ ก่อนที่ฉันจะลงคะแนน) ฉันต้องย้าย 'คะแนน' จากหลัง 'กรณี' ไปหลังจากแต่ละ 'เมื่อ'
Ron Tuffin

3
คุณพูดถูกและขอบคุณสำหรับการแก้ไข เห็นได้ชัดว่าเมื่อคุณใส่ตัวแปรหลังคำหลัก 'กรณี' คุณจะสามารถจับคู่ที่ตรงกันเท่านั้นไม่ใช่การแสดงออก ฉันเรียนรู้มากจากการตอบคำถามไม่ให้ถามพวกเขา :-)
Ken Paul

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
อ่า ... มีประโยชน์มากจากการใช้เวลาสร้างตาราง (ผมใช้ตารางที่มีอยู่มีน้อยเกินไปแถวช่วงกว่าขนาดเล็กเกินไป)
เจมส์เคอร์แร

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

ขอบคุณ! ฉันลองและแนวคิดพื้นฐานใช้งานได้ดีแม้ว่าไวยากรณ์ที่ฉันต้องใช้แตกต่างกันเล็กน้อย จำเป็นต้องใช้คำหลัก "กรณีแรก" และหลังจากเงื่อนไขสุดท้ายก่อน "ช่วง" คุณต้องใช้คำหลัก "สิ้นสุด" นอกเหนือจากนั้นใช้งานได้ดีมาก - ขอบคุณ!
ฮิวจ์

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 เป็นต้นตามโพสต์ของฉัน ด้านล่างคุณสามารถเพิ่มคำสั่งซื้อเพื่อให้ได้ผลลัพธ์ในลำดับที่ถูกต้อง
Timothy Walters

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

-1

บางทีคุณกำลังถามเกี่ยวกับการทำให้สิ่งต่าง ๆ ดำเนินต่อไป ...

แน่นอนว่าคุณจะเรียกใช้การสแกนแบบเต็มตารางสำหรับการสืบค้นและหากตารางที่มีคะแนนที่จำเป็นต้องมีการนับ (การรวม) มีขนาดใหญ่คุณอาจต้องการโซลูชันที่มีประสิทธิภาพดีกว่าคุณสามารถสร้างตารางรองและใช้กฎเช่นon insert- คุณอาจมองเข้าไป

ไม่ใช่ RDBMS เอ็นจิ้นทุกตัวที่มีกฎเกณฑ์!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.