2009-11-19 14 views
68

क्या MySQL में बिन आकार निर्दिष्ट करने का कोई तरीका है? अभी, मैं निम्नलिखित SQL क्वेरी कोशिश कर रहा हूँ:हिस्टोग्राम प्लॉट के लिए डेटा प्राप्त करना

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

डेटा है कि उत्पन्न किया जा रहा काफी अच्छा है, लेकिन अभी बहुत अधिक पंक्तियाँ हैं। मुझे पूर्वनिर्धारित डिब्बे में डेटा को समूहित करने का एक तरीका है। मैं इसे एक स्क्रिप्टिंग भाषा से कर सकता हूं, लेकिन क्या इसे सीधे एसक्यूएल में करने का कोई तरीका है?

उदाहरण:

+-------+--------------+ 
| 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 | 
------------------------ 

क्या मैं देख रहा हूँ:

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

मुझे लगता है कि यह एक सीधे आगे ढंग से प्राप्त नहीं किया जा सकता है, लेकिन किसी भी संबंधित संग्रहीत प्रक्रिया के लिए एक संदर्भ के रूप में अच्छी तरह से ठीक हो सकती है ।

+0

मुझे बिल्कुल यकीन नहीं है कि आप क्या पूछ रहे हैं। उदाहरण आउटपुट मदद कर सकता है। –

+0

क्षमा करें! एक उदाहरण के साथ बस मेरी पोस्ट अद्यतन किया। – Legend

उत्तर

127

यह संख्यात्मक मानों के लिए MySQL में हिस्टोग्राम बनाने के लिए एक सुपर त्वरित और गंदे तरीके के बारे में एक पोस्ट है।

सीएसईई कथन और अन्य प्रकार के जटिल तर्कों का उपयोग करते हुए बेहतर और अधिक लचीला बनाने वाले कई प्रकार के हिस्टोग्राम बनाने के कई अन्य तरीके हैं। यह विधि मुझे समय और समय के साथ फिर से जीतती है क्योंकि प्रत्येक उपयोग मामले के लिए संशोधित करने के लिए यह बहुत आसान है, और इतना छोटा और संक्षिप्त है। यह तुम कैसे करते है:

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

बस जो कुछ भी अपने स्तंभ, गोलाई वेतन वृद्धि बदल जाता है करने के लिए numeric_value बदलने के लिए, और बस इतना ही। मैंने बार को लॉगरिदमिक स्केल में बनाया है, ताकि बड़े मान होने पर वे बहुत अधिक न हों।

अंकुरित_वल्यू राउंडिंग वृद्धि के आधार पर राउंडिंग ऑपरेशन में ऑफसेट होना चाहिए, ताकि पहली बाल्टी में निम्नलिखित बाल्टी के रूप में कई तत्व शामिल हों।

उदा। ROUND (numeric_value, -1) के साथ, संख्या [0,4] (5 तत्व) में numeric_value पहली बाल्टी में रखा जाएगा, जबकि [5,14] (10 तत्व) दूसरे में, [15,24] तीसरे में, जब तक numeric_value ROUND के माध्यम से उचित रूप से ऑफ़सेट है (numeric_value - 5, -1)।

यह कुछ यादृच्छिक डेटा पर ऐसी क्वेरी का एक उदाहरण है जो सुंदर मीठा दिखता है। डेटा के त्वरित मूल्यांकन के लिए पर्याप्त है।

+--------+----------+-----------------+ 
| 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 | *    | 
+--------+----------+-----------------+ 

कुछ नोट: सीमाओं की कोई मुकाबला नहीं गिनती में दिखाई नहीं देगा है - आप की संख्या, कॉलम में एक शून्य नहीं होगा। इसके अलावा, मैं यहां राउंड फ़ंक्शन का उपयोग कर रहा हूं। यदि आप महसूस करते हैं कि यह आपको अधिक समझ में आता है तो आप इसे आसानी से TRUNCATE से प्रतिस्थापित कर सकते हैं।

मैं इसे यहाँ http://blog.shlomoid.com/2011/08/how-to-quickly-create-histogram-in.html

+0

MySQL 8.0.3 के रूप में कर सकते हैं, अब आप ऑप्टिमाइज़र को अधिक आंकड़े प्रदान करने के लिए हिस्टोग्राम आंकड़े बनाने की क्षमता है - 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 

तालिका डिब्बे में कॉलम min_value और max_value शामिल हैं जो डिब्बे को परिभाषित करते हैं। ध्यान दें कि ऑपरेटर "जुड़ें ... x BETWEEN y और z" पर समावेशी है।

table1 डेटा तालिका

+2

एसक्यूएल के लिए सिंटैक्स रंग इतना बुरा क्यों है? मैं इसे कैसे सुधार सकता हूं? शायद मुझे इसे मेटा पर पोस्ट करना चाहिए;) –

+0

@Ofri Raviv हाँ, आपको चाहिए! – Ismael

+2

इस मामले में न्यूनतम अधिकतम परिभाषित करने के लिए एक टेम्पलेट तालिका आवश्यक है। केवल एसक्यूएल के साथ संभव नहीं है। – Cesar

9

Ofri Raviv का जवाब बहुत करीब है, लेकिन वह सही नहीं है का नाम है। count(*)1 होगा भले ही हिस्टोग्राम अंतराल में शून्य परिणाम हों। क्वेरी का उपयोग करने के लिए संशोधित करने की आवश्यकता है एक सशर्त 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

मैं 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((@[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 

इससे जनसंख्या वाले डिब्बे के लिए हिस्टोग्राम गिनती उत्पन्न होगी। डेविड वेस्ट को उनके सुधार में सही होना चाहिए, लेकिन किसी कारण से, मेरे लिए परिणाम में अप्रचलित डिब्बे दिखाई नहीं दे रहे हैं (बाएं जॉइन के उपयोग के बावजूद - मुझे समझ में नहीं आता क्यों)।

3

यह काम करना चाहिए। ऐसा नहीं है, लेकिन अभी भी सुरुचिपूर्ण: के माध्यम से

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 

Mike DelGaudio

21

माइक DelGaudio का जवाब जिस तरह से मैं यह कर रहा है, लेकिन एक मामूली परिवर्तन के साथ:

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

लाभ? आप डिब्बे को जितना चाहें उतना बड़ा या छोटा कर सकते हैं। आकार 100 के डिब्बे? floor(mycol/100)*100। आकार 5 के डिब्बे? floor(mycol/5)*5

बर्नार्डो।

+0

समूह 1 ऑर्डर द्वारा 1 – carillonator

+0

के रूप में कैरिलोनेटर ने कहा कि आपके समूह द्वारा क्रमशः क्रमशः क्रमबद्ध होना चाहिए और bin_floor होना चाहिए या 1 - अगर आप इसे सही करते हैं तो बीमार अपवित्र, यह मेरे लिए सबसे अच्छा जवाब है –

+0

पर्याप्त मेला, @ बीएम। कैरिलोनेटर द्वारा सुझाए गए अनुसार बदला गया। –

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

जब तक वहाँ भी कई अंतराल नहीं हैं पाया है, यह एक बहुत अच्छा समाधान है।

+1

+1 देखें यह एकमात्र समाधान है जो डिब्बे को अलग होने की अनुमति देता है आकार –

+0

महान - अतिरिक्त तालिकाओं की कोई आवश्यकता नहीं है – 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

समान चौड़ाई डिब्बे की दी गई गिनती में binning:

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; 

ध्यान दें कि 0.0000001 यह सुनिश्चित करने के लिए है कि अधिकतम (कोला) के बराबर मान वाले रिकॉर्ड स्वयं ही अपने स्वयं के बिन नहीं बनाते हैं। साथ ही, यह सुनिश्चित करने के लिए योजक निरंतर है कि क्वेरी शून्य पर विभाजन पर विफल नहीं होती है जब कॉलम के सभी मान समान होते हैं।

यह भी ध्यान दें कि पूर्णांक विभाजन से बचने के लिए डिब्बे (उदाहरण में 10) को दशमलव चिह्न के साथ लिखा जाना चाहिए (असंगत bin_width दशमलव हो सकता है)।