Nhận dữ liệu cho biểu đồ biểu đồ


82

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:

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

Dữ liệu đang được tạo đủ 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 các thùng được xác định trước. Tôi có thể thực hiện việc này từ một ngôn ngữ kịch bản, nhưng có cách nào để thực hiện điều đó trực tiếp trong SQL không?

Thí 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 điều này không thể đạt được theo cách thẳng thắn nhưng tham chiếu đến bất kỳ thủ tục lưu trữ liên quan nào cũng sẽ ổn.


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

Lấy làm tiếc! Chỉ cần cập nhật bài đăng của tôi với một ví dụ.
Huyền thoại

Câu trả lời:


159

Đây là một bài đăng về một cách siêu nhanh và đơn giản để tạo biểu đồ trong MySQL cho các 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 câu lệnh CASE và các loại logic phức tạp khác. Phương pháp này khiến tôi chiến thắng hết lần này đến lần khác vì nó rất dễ sửa đổi cho từng trường hợp sử dụng và quá ngắn gọn, súc tích. Đâ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 numeric_value thành bất kỳ cột nào của bạn, thay đổi gia số làm tròn, và thế là xong. Tôi đã tạo các thanh theo thang logarit, để chúng không phát triển quá nhiều khi bạn có các giá trị lớn.

numeric_value phải được bù trừ trong hoạt động ROUNDing, dựa trên 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 (giá trị số, -1), giá trị số 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ử) ở nhóm thứ hai, [15,24] ở nhóm thứ ba, trừ khi giá trị số được bù đắp thích hợp qua ROUND (giá trị số - 5, -1).

Đây là một ví dụ về truy vấn như vậy trên một số dữ liệu ngẫu nhiên trông khá hấp dẫn. Đủ 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 ý: Các phạm vi không khớp sẽ không xuất hiện trong số đếm - bạn sẽ không có số 0 trong cột đếm. Ngoài ra, tôi đang sử dụng hàm ROUND ở đây. Bạn có thể dễ dàng thay thế nó bằng TRUNCATE nếu bạn cảm thấy nó có ý nghĩa hơn 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


1
Tính đến MySQL 8.0.3, bây giờ bạn có khả năng tạo ra các thống kê biểu đồ để cung cấp thêm thống kê để tôi ưu hoa - xem mysqlserverteam.com/histogram-statistics-in-mysql
Jaro

Bạn thậm chí không cần phần "thanh" của truy vấn; bản thân các con số đã tạo thành biểu đồ / biểu đồ thanh logarit.
Âm thanh cao hơn

31

Câu trả lời của Mike DelGaudio là cách tôi làm, nhưng với một chút thay đổi:

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

Lợi thế? Bạn có thể làm cho các thùng lớn hoặc nhỏ tùy thích. Thùng có kích thước 100? floor(mycol/100)*100. Thùng có kích thước 5? floor(mycol/5)*5.

Bernardo.


như carillonator cho biết nhóm của bạn bằng cách & tự do tốt hơn nên bin_floor hoặc 1 - Ill phiếu bầu tán thành nếu bạn sửa nó, đây là câu trả lời tốt nhất cho tôi
BM

Đủ công bằng, @bm. Đã thay đổi theo đề xuất của carillonator.
Bernardo Siu

và nếu bạn muốn có một tên cột đẹp hơn bạn có thể làmconcat(floor(mycol/5)*5," to ",floor(mycol/5)*5+5)
alex9311

Điều này thực sự tốt hơn so với đơn giản round(mycol, -2)từ câu trả lời được chấp nhận vì nó cho phép người dùng xác định bất kỳ "phạm vi" nào không thập phân. Tôi chỉ muốn sử dụng roundthay vì floorvì nó làm tròn các số một cách chính xác.
meridius

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

Các thùng trong 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" là bao gồm.

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


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

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

SQL Guru! Chính xác những gì tôi muốn. Tôi đoán nên cẩn thận khi tạo bảng thùng. Nếu không thì mọi thứ hoạt động hoàn hảo. :) Cảm ơn bạn. Tôi vừa mới viết xong một tập lệnh python nhưng đây chỉ là những gì tôi cần ...
Huyền thoại

@Legend: Trên thực tế, tôi khá là n00b khi nói đến SQL. nhưng đây là một câu hỏi hay và hữu ích nên tôi thích bài tập ...
Ofri Raviv

1
Điều quan trọng là phải xem câu trả lời của @David West (lẽ ra phải là một nhận xét, tại đây) về cách COUNT (*) tạo ra 1 khi nào nó tạo ra 0. Điều đó có thể không phải là một vấn đề lớn đối với bạn, nhưng nó có thể làm sai lệch số liệu thống kê và làm cho bạn trông hơi ngớ ngẩn nếu thông báo một ai đó :)
Christopher Schultz

11

Câu trả lời của Ofri Raviv rất gần nhưng không chính xác. Các count(*)sẽ 1ngay cả khi có không có kết quả trong một khoảng thời gian histogram. Truy vấn cần được sửa đổi để sử dụng đ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;

10
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 ....

Miễn là không có quá nhiều khoảng thời gian, đâ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 thùng là có kích thước khác nhau
Gabe Moothart

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

+1 Đây là giải pháp linh hoạt nhất imo và có vẻ phù hợp nhất với trường hợp sử dụng muốn bin từ bên trong SQL. bất kỳ trường hợp nào trong đó phạm vi bin cần được dẫn xuất theo chương trình, có khả năng tốt hơn nên làm điều đó bên ngoài SQL. một lần nữa imo
Ryan McCoy

4

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

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((@binmax-@binmin)/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ẽ tạo ra số lượng biểu đồ chỉ cho các thùng được điền. David West lẽ ra phải đúng trong việc sửa lỗi của mình, nhưng vì một số lý do, các thùng không được công nhận không xuất hiện trong kết quả đối với tôi (mặc dù tôi đã sử dụng LEFT JOIN - Tôi không hiểu tại sao).


3

Cần làm việc. Không quá thanh lịch 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


3
SELECT
    CASE
        WHEN total <= 30 THEN "0-30"
        WHEN total <= 40 THEN "31-40"       
        WHEN total <= 50 THEN "41-50"
        ELSE "50-"
    END as Total,
    count(*) as count
GROUP BY Total 
ORDER BY Total;

2

Chiều rộng bằng nhau xếp thùng vào một số lượng thùng nhất định:

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 ở đó để đảm bảo rằng các bản ghi có giá trị bằng max (col) không chỉ tạo thùng riêng của nó. Ngoài ra, hằng số cộng ở đó để đảm bảo truy vấn không bị lỗ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 (trong ví dụ là 10) nên được viết bằng dấu thập phân để tránh chia số nguyên (bin_width chưa điều chỉnh có thể là số thập phân).


WITH something ASrất hữu ích nếu bạn phải tính toán giá trị đi vào thùng.
Rúnar Berg

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.