Trong SQL, làm thế nào bạn có thể nhóm nhóm của bạn theo phạm vi phạm vi?


181

Giả sử tôi có một bảng có một cột số (hãy gọi nó là "điểm số").

Tôi muốn tạo một bảng tổng số, cho biết số lần xuất hiện trong mỗi phạm vi.

Ví dụ:

phạm vi điểm | Số lần xuất hiện
-------------------------------------
   0-9 | 11
  10-19 | 14
  20-29 | 3
   ... | ...

Trong ví dụ này, có 11 hàng có điểm số trong phạm vi từ 0 đến 9, 14 hàng có điểm số trong phạm vi từ 10 đến 19 và 3 hàng có điểm số trong phạm vi 20-29.

Có một cách dễ dàng để thiết lập này? Bạn đề xuất món gì?

Câu trả lời:


143

Cả hai câu trả lời được bình chọn cao nhất đều đúng trên SQLServer 2000. Có lẽ họ đang sử dụng một phiên bản khác.

Dưới đây là các phiên bản chính xác của cả hai trên 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

hoặc là

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

Tôi có thể tổng hợp một cột khác không (như đếm nhóm). nói rằng tôi wana tổng hợp cột học bổng cho mỗi phạm vi điểm. Tôi đã thử, nhưng không đúng
Munish G Lòng tin

Câu trả lời hay @Ron Tuffin, tuy nhiên khi bạn có hai phạm vi như 10-20, 100-200, thì đơn hàng không hoạt động. bạn sẽ có thứ tự như 10-20, 100-200,20-30, v.v ... Có mẹo nào cho đơn hàng không?
Zo có

2
@Zo đó là một chút hack nhưng điều này hoạt động: đặt hàng bởi len (t.range), t.range
Ron Tuffin

Câu trả lời tốt hơn tại stackoverflow.com/questions/14730380/ Mạnh
Sấm

1
Nếu bạn vẫn gặp vấn đề về cú pháp, hãy kiểm tra câu trả lời này: dba.stackexchange.com/questions/22491/ dọa
Robert Hosking

33

Một cách tiếp cận khác sẽ liên quan đến việc lưu trữ các phạm vi trong một bảng, thay vì nhúng chúng vào truy vấn. Bạn sẽ kết thúc với một bảng, gọi nó là Phạm vi, trông giống như thế này:

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

Và một truy vấn trông như thế này:

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

Điều này không có nghĩa là thiết lập một bảng, nhưng nó sẽ dễ dàng duy trì khi phạm vi mong muốn thay đổi. Không có thay đổi mã cần thiết!


Tôi đã hỏi một câu hỏi về Thiết kế bảng quản trị viên cơ sở dữ liệu cho dữ liệu theo khuôn mẫu bằng cách sử dụng các biến nhóm biến không có câu trả lời, nhưng cuối cùng tôi đã thiết kế một hệ thống có các phạm vi bạn đã đề cập. Yêu câu trả lời này.
ΩmegaMan

31

Tôi thấy câu trả lời ở đây sẽ không hoạt động theo cú pháp của SQL Server. Tôi sẽ dùng:

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: xem ý kiến


Có thể là do phiên bản SQLServer tôi đang sử dụng nhưng để làm cho ví dụ của bạn hoạt động (tôi kiểm tra mọi thứ trước khi bỏ phiếu cho chúng) Tôi đã phải chuyển 'điểm' từ sau 'trường hợp' sang sau mỗi 'khi'.
Ron Tuffin

3
Bạn nói đúng, và cảm ơn vì đã sửa. Rõ ràng khi bạn đặt biến sau từ khóa 'trường hợp', bạn chỉ có thể thực hiện khớp chính xác, không phải biểu thức. Tôi học được nhiều từ việc trả lời các câu hỏi cũng như hỏi họ. :-)
Ken Paul

23

Trong postgres ( ||toán tử nối chuỗi):

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

cho:

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

11

Câu trả lời của James Curran là ngắn gọn nhất theo ý kiến ​​của tôi, nhưng kết quả không chính xác. Đối với SQL Server, câu lệnh đơn giản nhất như sau:

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

Điều này giả sử một bảng tạm thời #Scores tôi đã sử dụng để kiểm tra nó, tôi chỉ điền 100 hàng với số ngẫu nhiên trong khoảng từ 0 đến 99.


1
À ... Có lợi thế là thực sự dành thời gian để tạo bảng. (Tôi đã sử dụng một bảng hiện có với quá ít hàng trên phạm vi quá nhỏ)
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

Cảm ơn! Tôi đã thử điều này và ý tưởng cơ bản hoạt động rất tốt, mặc dù cú pháp mà tôi phải sử dụng hơi khác nhau. Chỉ cần từ khóa "trường hợp" đầu tiên và sau điều kiện cuối cùng, trước "phạm vi" bạn cần từ khóa "kết thúc". Ngoài ra, làm việc rất tốt - cảm ơn!
Hugh

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

Tôi thích điều này, nhưng bạn phải sửa các phạm vi bên ngoài truy vấn nếu bạn sẽ hiển thị nó.
tvanfosson

Trong trường hợp bạn quyết định sửa câu trả lời của mình, bạn cần thay đổi điểm số của mình / 10 trên dòng đầu tiên thành (điểm / 10) * 10 cho cả hai nếu không bạn sẽ nhận được 3 - 12 thay vì 30-39, v.v. Theo bài đăng của tôi bên dưới bạn có thể thêm một đơn đặt hàng bằng cách nhận kết quả theo đúng thứ tự.
Timothy Walters

5

Điều này sẽ cho phép bạn không phải chỉ định phạm vi và phải là bất khả tri của máy chủ SQL. Toán FTW!

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

3

Tôi sẽ làm điều này một chút khác nhau để nó có tỷ lệ mà không phải xác định mọi trường hợp:

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

Không được thử nghiệm, nhưng bạn có ý tưởng ...


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

Đảm bảo bạn sử dụng một từ khác ngoài 'phạm vi' nếu bạn đang ở trong MySQL hoặc bạn sẽ gặp lỗi khi chạy ví dụ trên.


1

Vì cột được sắp xếp trên ( Range) là một chuỗi, nên sắp xếp chuỗi / từ thay vì sắp xếp số.

Miễn là các chuỗi có số không để điền vào độ dài số, việc sắp xếp vẫn phải chính xác về mặt ngữ nghĩa:

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

Nếu phạm vi được trộn, chỉ cần thêm một số không:

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

Thử

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
Sẽ rất hữu ích nếu bạn có thể thêm một số giải thích về cách truy vấn của bạn giải quyết vấn đề.
devlin Carnate

-1

Có lẽ bạn đang hỏi về việc giữ những thứ như vậy diễn ra ...

Tất nhiên, bạn sẽ gọi quét toàn bộ bảng cho các truy vấn và nếu bảng chứa điểm cần được tính (tổng hợp) lớn, bạn có thể muốn một giải pháp hoạt động tốt hơn, bạn có thể tạo bảng phụ và sử dụng quy tắc, chẳng hạn như on insert- bạn có thể nhìn vào nó

Tuy nhiên, không phải tất cả các công cụ RDBMS đều có các quy tắc!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.