2009-11-19 44 views
68

Có cách nào để chỉ định kích thước thùng trong MySQL không? Ngay bây giờ, tôi đang thử truy vấn SQL sau:Lấy dữ liệu cho ô biểu đồ

select total, count(total) from faults GROUP BY total; 

Dữ liệu được tạo ra đủ tốt nhưng chỉ có quá nhiều hàng. Những gì tôi cần là một cách để nhóm dữ liệu vào thùng được xác định trước. Tôi có thể làm điều này từ một ngôn ngữ kịch bản, nhưng có cách nào để làm điều đó trực tiếp trong SQL?

Ví dụ:

+-------+--------------+ 
| total | count(total) | 
+-------+--------------+ 
| 30 |   1 | 
| 31 |   2 | 
| 33 |   1 | 
| 34 |   3 | 
| 35 |   2 | 
| 36 |   6 | 
| 37 |   3 | 
| 38 |   2 | 
| 41 |   1 | 
| 42 |   5 | 
| 43 |   1 | 
| 44 |   7 | 
| 45 |   4 | 
| 46 |   3 | 
| 47 |   2 | 
| 49 |   3 | 
| 50 |   2 | 
| 51 |   3 | 
| 52 |   4 | 
| 53 |   2 | 
| 54 |   1 | 
| 55 |   3 | 
| 56 |   4 | 
| 57 |   4 | 
| 58 |   2 | 
| 59 |   2 | 
| 60 |   4 | 
| 61 |   1 | 
| 63 |   2 | 
| 64 |   5 | 
| 65 |   2 | 
| 66 |   3 | 
| 67 |   5 | 
| 68 |   5 | 
------------------------ 

Những gì tôi đang tìm kiếm:

+------------+---------------+ 
| total  | count(total) | 
+------------+---------------+ 
| 30 - 40 |   23 | 
| 40 - 50 |   15 | 
| 50 - 60 |   51 | 
| 60 - 70 |   45 | 
------------------------------ 

Tôi đoán này không thể đạt được một cách thẳng về phía trước nhưng một tham chiếu đến bất kỳ thủ tục lưu trữ liên quan sẽ là tốt cũng .

+0

tôi không chắc chắn chính xác những gì bạn đang yêu cầu. đầu ra ví dụ có thể hữu ích. –

+0

Xin lỗi! Chỉ cần cập nhật bài đăng của tôi với một ví dụ. – Legend

Trả lời

127

Đây là bài đăng về cách siêu nhanh và bẩn để tạo biểu đồ trong MySQL cho giá trị số.

Có nhiều cách khác để tạo biểu đồ tốt hơn và linh hoạt hơn, sử dụng báo cáo CASE và các loại logic phức tạp khác. Phương pháp này thắng tôi theo thời gian và thời gian một lần nữa vì nó rất dễ dàng để sửa đổi cho từng trường hợp sử dụng và ngắn gọn và ngắn gọn. Đây là cách bạn làm điều đó:

SELECT ROUND(numeric_value, -2) AS bucket, 
     COUNT(*)     AS COUNT, 
     RPAD('', LN(COUNT(*)), '*') AS bar 
FROM my_table 
GROUP BY bucket; 

Chỉ cần thay đổi bất cứ điều gì để numeric_value cột của bạn, thay đổi increment làm tròn, và đó là nó. Tôi đã làm cho các thanh nằm ở mức quy mô lôgarít, để chúng không phát triển quá nhiều khi bạn có giá trị lớn.

numeric_value phải được bù trừ trong hoạt động ROUNDing, dựa trên số gia số làm tròn, để đảm bảo nhóm đầu tiên chứa nhiều phần tử như các nhóm sau.

ví dụ: với ROUND (numeric_value, -1), numeric_value trong phạm vi [0,4] (5 phần tử) sẽ được đặt trong nhóm đầu tiên, trong khi [5,14] (10 phần tử) trong giây, [15,24] ở vị trí thứ ba, trừ khi numeric_value được bù đắp một cách thích hợp thông qua ROUND (numeric_value - 5, -1).

Đây là ví dụ về truy vấn như vậy trên một số dữ liệu ngẫu nhiên trông khá ngọt ngào. Đủ tốt để đánh giá nhanh dữ liệu.

+--------+----------+-----------------+ 
| bucket | count | bar    | 
+--------+----------+-----------------+ 
| -500 |  1 |     | 
| -400 |  2 | *    | 
| -300 |  2 | *    | 
| -200 |  9 | **    | 
| -100 |  52 | ****   | 
|  0 | 5310766 | *************** | 
| 100 | 20779 | **********  | 
| 200 |  1865 | ********  | 
| 300 |  527 | ******   | 
| 400 |  170 | *****   | 
| 500 |  79 | ****   | 
| 600 |  63 | ****   | 
| 700 |  35 | ****   | 
| 800 |  14 | ***    | 
| 900 |  15 | ***    | 
| 1000 |  6 | **    | 
| 1100 |  7 | **    | 
| 1200 |  8 | **    | 
| 1300 |  5 | **    | 
| 1400 |  2 | *    | 
| 1500 |  4 | *    | 
+--------+----------+-----------------+ 

Một số lưu ý: Phạm vi mà không có trận đấu sẽ không xuất hiện trong đếm - bạn sẽ không có một số không trong cột đếm. Ngoài ra, tôi đang sử dụng hàm ROUND tại đây. Bạn có thể dễ dàng thay thế bằng TRUNCATE nếu bạn cảm thấy điều đó có ý nghĩa hơn đối với bạn.

tôi tìm thấy nó ở đây http://blog.shlomoid.com/2011/08/how-to-quickly-create-histogram-in.html

+0

Kể từ MySQL 8.0.3, bây giờ bạn có khả năng tạo số liệu thống kê biểu đồ để cung cấp thêm số liệu thống kê cho trình tối ưu hóa - xem http://mysqlserverteam.com/histogram-statistics-in-mysql/ – Jaro

16
SELECT b.*,count(*) as total FROM bins b 
left outer join table1 a on a.value between b.min_value and b.max_value 
group by b.min_value 

Thùng bảng chứa các cột min_value và max_value xác định các thùng. lưu ý rằng toán tử "tham gia ... trên x GIỮA y và z" được bao gồm.

table1 là tên của bảng dữ liệu

+2

Tại sao cú pháp tô màu cho SQL quá tệ? Làm thế nào tôi có thể cải thiện điều này? Có lẽ tôi nên đăng nó trên meta;) –

+0

@Ofri Raviv Vâng, bạn nên! – Ismael

+2

Trong trường hợp này là cần thiết một bảng mẫu để xác định min tối đa. Chỉ với SQL là không thể. – Cesar

9

Câu trả lời của Ofri Raviv rất gần nhưng không chính xác. count(*) sẽ là 1 ngay cả khi không có kết quả trong khoảng thời gian biểu đồ. Các truy vấn cần phải được sửa đổi để sử dụng một điều kiện sum:

SELECT b.*, SUM(a.value IS NOT NULL) AS total FROM bins b 
    LEFT JOIN a ON a.value BETWEEN b.min_value AND b.max_value 
GROUP BY b.min_value; 
3

tôi đã thực hiện một thủ tục có thể được sử dụng để tự động tạo ra một bảng tạm thời cho thùng theo một số hoặc kích thước quy định, để sử dụng sau với giải pháp Ofri Raviv của .

CREATE PROCEDURE makebins(numbins INT, binsize FLOAT) # binsize may be NULL for auto-size 
BEGIN 
SELECT FLOOR(MIN(colval)) INTO @binmin FROM yourtable; 
SELECT CEIL(MAX(colval)) INTO @binmax FROM yourtable; 
IF binsize IS NULL 
    THEN SET binsize = CEIL((@[email protected])/numbins); # CEIL here may prevent the potential creation a very small extra bin due to rounding errors, but no good where floats are needed. 
END IF; 
SET @currlim = @binmin; 
WHILE @currlim + binsize < @binmax DO 
    INSERT INTO bins VALUES (@currlim, @currlim+binsize); 
    SET @currlim = @currlim + binsize; 
END WHILE; 
INSERT INTO bins VALUES (@currlim, @maxbin); 
END; 

DROP TABLE IF EXISTS bins; # be careful if you have a bins table of your own. 
CREATE TEMPORARY TABLE bins (
minval INT, maxval INT, # or FLOAT, if needed 
KEY (minval), KEY (maxval));# keys could perhaps help if using a lot of bins; normally negligible 

CALL makebins(20, NULL); # Using 20 bins of automatic size here. 

SELECT bins.*, count(*) AS total FROM bins 
LEFT JOIN yourtable ON yourtable.value BETWEEN bins.minval AND bins.maxval 
GROUP BY bins.minval 

Điều này sẽ chỉ tạo số lượng biểu đồ cho các thùng được điền. David West nên được đúng trong sự điều chỉnh của mình, nhưng vì một lý do nào đó, các thùng không phổ biến không xuất hiện trong kết quả cho tôi (mặc dù việc sử dụng LEFT JOIN - tôi không hiểu tại sao).

3

Điều đó sẽ hiệu quả. Không phải như vậy tao nhã nhưng vẫn:

select count(mycol - (mycol mod 10)) as freq, mycol - (mycol mod 10) as label 
from mytable 
group by mycol - (mycol mod 10) 
order by mycol - (mycol mod 10) ASC 

qua Mike DelGaudio

21

câu trả lời Mike DelGaudio là cách tôi làm điều đó, nhưng với một thay đổi nhỏ:

select floor(mycol/10)*10 as bin_floor, count(*) 
from mytable 
group by 1 
order by 1 

Ưu điểm? Bạn có thể làm cho các thùng lớn hoặc nhỏ như bạn muốn. Thùng cỡ 100? floor(mycol/100)*100. Thùng cỡ 5? floor(mycol/5)*5.

Bernardo.

+0

nhóm theo 1 đơn đặt hàng 1 – carillonator

+0

như carillonator cho biết nhóm của bạn theo thứ tự tốt hơn nên là bin_floor hoặc 1 - Ill upvote nếu bạn sửa nó, đây là câu trả lời tốt nhất cho tôi –

+0

Đủ công bằng, @bm. Thay đổi theo đề nghị của carillonator. –

9
select "30-34" as TotalRange,count(total) as Count from table_name 
    where total between 30 and 34 
union (
select "35-39" as TotalRange,count(total) as Count from table_name 
    where total between 35 and 39) 
union (
select "40-44" as TotalRange,count(total) as Count from table_name 
    where total between 40 and 44) 
union (
select "45-49" as TotalRange,count(total) as Count from table_name 
    where total between 45 and 49) 
etc .... 

Chừng nào không có quá nhiều khoảng, đây là một giải pháp khá tốt.

+1

+1 Đây là giải pháp duy nhất ở đây cho phép các thùng khác nhau kích thước –

+0

tuyệt vời - không cần thêm bảng – NiRR

1
select case when total >= 30 and total <= 40 THEN "30-40"  
     else when total >= 40 and total <= 50 then "40-50" 
     else "50-60" END as Total , count(total) 
group by Total 
0

chiều rộng bình đẳng binning vào một số nhất định của thùng:

WITH bins AS(
    SELECT min(col) AS min_value 
     , ((max(col)-min(col))/10.0) + 0.0000001 AS bin_width 
    FROM cars 
) 
SELECT tab.*, 
    floor((col-bins.min_value)/bins.bin_width) AS bin 
FROM tab, bins; 

Lưu ý rằng 0.0000001 là có để đảm bảo rằng các bản ghi với giá trị bằng max (col) không làm cho chính nó bin riêng của chính nó. Ngoài ra, hằng số phụ gia ở đó để đảm bảo truy vấn không thất bại khi chia cho 0 khi tất cả các giá trị trong cột giống hệt nhau.

Cũng lưu ý rằng số lượng thùng (10 trong ví dụ) phải được viết bằng dấu thập phân để tránh phân chia số nguyên (bin_width không điều chỉnh có thể là số thập phân).

Các vấn đề liên quan