mySQL

2016-09-05 5 views
5

में COUNT() मानों के आधार पर सीमित समूह द्वारा मैं एक mySQL डेटाबेस में ईवेंट लॉगिंग कर रहा हूं और निगरानी उद्देश्यों के लिए शीर्ष 3 ईवेंट प्राप्त करना चाहता हूं।mySQL

मेरे तालिका eventlog इस तरह दिखता है:

+----+------------------+---------------------+ 
| id | eventname  |  eventdate  | 
+----+------------------+---------------------+ 
| 0 | machine1.started | 2016-09-04 19:22:23 | 
| 1 | machine2.reboot | 2016-09-04 20:23:11 | 
| 2 | machine1.stopped | 2016-09-04 20:24:12 | 
| 3 | machine1.started | 2016-09-04 20:25:12 | 
| 4 | machine1.stopped | 2016-09-04 23:23:16 | 
| 5 | machine0.started | 2016-09-04 23:24:00 | 
| 6 | machine1.started | 2016-09-04 23:24:16 | 
| 7 | machine3.started | 2016-09-04 23:25:00 | 
| 8 | machine4.started | 2016-09-04 23:26:00 | 
| 9 | cluster.alive | 2016-09-04 23:30:00 | 
| 10 | cluster.alive | 2016-09-05 11:30:00 | 
+----+------------------+---------------------+ 

क्वेरी अंत में निम्नलिखित लौटना चाहिए, पकड़े

  • शीर्ष 3 घटनाओं है कि अक्सर सबसे हुआ (स्तंभ eventcount कि उत्पन्न होता है के आधार पर mySQL के COUNT() फ़ंक्शन द्वारा), उनके eventname
  • द्वारा केवल 2 पंक्तियां जहां eventcount = 1, लेकिन केवल तभी 1 शीर्ष 3 के भीतर हैवांछित परिणाम की

उदाहरण के लिए, उपरोक्त तालिका के आधार पर (के बाद से वहाँ की घटनाओं है कि बस एक बार होते हैं और इसलिए मेरी दृश्यपटल ओवरलोड हैं की एक बहुत कुछ कर रहे हैं):

+------------+------------------+ 
| eventcount | eventname  | 
+------------+------------------+ 
|   3 | machine1.started | 
|   2 | machine1.stopped | 
|   2 | cluster.alive | 
|   1 | machine0.started | 
|   1 | machine2.started | 
+------------+------------------+ 

कृपया ध्यान दें कि मुझे केवल 3 लौटाई पंक्तियों की आवश्यकता नहीं है, लेकिन 3 उच्चतम eventcount एस के साथ पंक्तियां हैं।

मैंने एकाधिक चयन और संदिग्ध CASE ... WHEN स्थितियों सहित नीचे क्वेरी स्ट्रिंग के साथ गड़बड़ करके बहुत प्रयोग किया, लेकिन मुझे जिस तरह से आवश्यकता है, उसे काम करने में सक्षम नहीं था।

SELECT COUNT(id) AS 'eventcount', eventname 
FROM eventlog 
GROUP BY eventname 
ORDER BY eventcount DESC; 

वांछित परिणाम प्राप्त करने के लिए सबसे अच्छा तरीका क्या है?

+0

@RiggsFolly नहीं, यह नहीं होगा। नमूना डेटा के वांछित परिणाम में 5 पंक्तियां – Lamak

+0

@ लामक गुड प्वाइंट – RiggsFolly

उत्तर

2

यहां इसके लिए चर एसक्यूएल फिडल का उपयोग कर यह कर का एक तरीका है: http://sqlfiddle.com/#!9/b3458b/16

SELECT 
    t2.eventcount 
    ,t2.eventname 
FROM 
(
    SELECT 
     t.eventname 
     ,t.eventcount 
     ,@Rank:=IF(@PrevCount=t.eventcount,@Rank,@Rank+1) Rank 
     ,@CountRownum:=IF(@PrevCount=t.eventcount,@CountRowNum + 1,1) CountRowNum 
     ,@PrevCount:= t.eventcount 
    FROM 
     (
     SELECT 
      l.eventname 
      ,COUNT(*) as eventcount 
     FROM 
      eventlog l 
     GROUP BY 
      l.eventname 
     ORDER BY 
      COUNT(*) DESC 
    ) t 
     CROSS JOIN (SELECT @Rank:=0, @CountRowNum:=0, @PrevCount:=-1) var 
    ORDER BY 
     t.eventcount DESC 
) t2 
WHERE 
    t2.Rank < 4 
    AND NOT (t2.eventcount = 1 AND t2.CountRowNum > 2) 
+0

किसी के लिए सोच रहा है: @ गॉर्डन लिनॉफ और मैट का समाधान दोनों मेरे उद्देश्य के लिए पूरी तरह से काम करते हैं। मैंने इस जवाब को स्वीकार कर लिया क्योंकि क्वेरी तेजी से टिक टिकती है। – IggyBlob

2

MySQL में इस प्रकार की स्थितियां दर्दनाक हैं। एक विधि चर का उपयोग करता है। यहाँ एक विधि है कि ऐसा नहीं करता है:

SELECT el.eventcount, el.eventname 
FROM (SELECT COUNT(el.id) AS eventcount, el.eventname 
     FROM eventlog el 
     GROUP BY el.eventname 
    ) el JOIN 
    (SELECT cnt 
     FROM (SELECT DISTINCT COUNT(el.id) as cnt 
      FROM eventlog el 
      GROUP BY el.eventname 
      ) el 
     ORDER BY cnt DESC 
     LIMIT 3 
    ) ell 
    ON ell.cnt = el.eventcount 
ORDER BY el.eventcount DESC; 

संपादित करें:

वैरिएबल का उपयोग कर समाधान इस तरह दिखता है और 1 की गिनती के लिए 2 पर सीमा में शामिल हैं:

SELECT * 
FROM (SELECT e.*, 
      (@rn1 := if(@c1 = eventcount, @rn1 + 1, 
         if(@c1 := eventcount, 1, 1) 
         ) 
      ) as rn 
     FROM (SELECT e.*, 
        (@rn := if(@c = eventcount, @rn, 
           if(@c := eventcount, @rn + 1, @rn + 1) 
          ) 
        ) as rank 
      FROM (SELECT COUNT(el.id) AS eventcount, el.eventname 
        FROM eventlog el 
        GROUP BY el.eventname 
       ) e CROSS JOIN 
       (SELECT @c := 0, @rn := 0) params 
      ORDER BY eventcount DESC 
      ) e CROSS JOIN 
      (SELECT @c1 := 0, @rn1 := 0) params 
     ORDER BY eventcount DESC 
    ) e 
WHERE rank <= 3 AND 
     (eventcount > 1 OR rn <= 2); 

अंतरतम गिनती गणना गिनती है। दूसरा गिनती के भीतर गणना करता है। असल में, दोनों को शायद एक ही सबक्वायरी में जोड़ा जा सकता है, लेकिन देखभाल के साथ।

+0

है, इसलिए यह रैंकिंग सही ढंग से करेगी, लेकिन किसी मामले में eventcount = 1 शीर्ष 3 परिणामों में है, ओपी केवल उन रिकॉर्डों में से 2 को वापस करना चाहता है उस सीमा को सीमित नहीं करेगा और – Matt

+0

@ मैट हाँ से ऊपर परीक्षण मामले में सभी 4 वापस कर देगा, आप सही हैं, रैंकिंग बहुत अच्छा काम करती है और यह भी अत्यधिक कुशलता से चलती है लेकिन घटनाक्रम = 1 के लिए सीमा पर विचार नहीं करती है ... – IggyBlob

+0

DISTINCT COUNT समूह द्वारा ?? एह? – Strawberry

-1

आप इस बाहर की कोशिश कर सकते:

SELECT count(eventname), eventname FROM table 
group by eventname 
HAVING(count(eventname)) > 1 
order by count(eventname) DESC 
limit 3 
0

इसमें कुछ समय पुनर्संशोधित जा करने के लिए सक्षम होना चाहिए, लेकिन यह अभी सही जवाब देता है:

SELECT eventcount, eventname 
FROM 
(SELECT el.eventcount, el.eventname 
FROM (SELECT COUNT(el.id) AS eventcount, el.eventname 
     FROM eventlog el 
     GROUP BY el.eventname 
    ) el JOIN 
    (SELECT counts 
     FROM (SELECT DISTINCT COUNT(el.id) as counts 
      FROM eventlog el 
      GROUP BY el.eventname 
      ) el 
     ORDER BY counts DESC 
     LIMIT 3 
    ) el2 
    ON el2.counts = el.eventcount 
    WHERE el.eventcount != 1 
UNION ALL 
(SELECT el.eventcount, el.eventname 
FROM (SELECT COUNT(el.id) AS eventcount, el.eventname 
     FROM eventlog el 
     GROUP BY el.eventname 
    ) el JOIN 
    (SELECT counts 
     FROM (SELECT DISTINCT COUNT(el.id) as counts 
      FROM eventlog el 
      GROUP BY el.eventname 
      ) el 
     ORDER BY counts DESC 
     LIMIT 3 
    ) el2 
    ON el2.counts = el.eventcount AND el2.counts = 1 
    LIMIT 2)) tmp 
ORDER BY tmp.eventcount DESC; 

एसक्यूएल फिडल: http://sqlfiddle.com/#!9/10f0d/92

0

यदि आप अस्थायी तालिकाओं का उपयोग कर सकते हैं ..

Precalculate eventcounts और एक अस्थायी तालिका में परिणाम की दुकान:

create temporary table tmp_eventcounts 
    select eventname, count(1) as eventcount 
    from eventlog 
    group by eventname 
    order by eventcount desc 
; 

की सामग्री:

|  eventname | eventcount | 
|------------------|------------| 
| machine1.started |   3 | 
| machine1.stopped |   2 | 
| cluster.alive |   2 | 
| machine3.started |   1 | 
| machine2.reboot |   1 | 
| machine4.started |   1 | 
| machine0.started |   1 | 

शीर्ष 3 eventcounts का चयन करें और उन्हें एक अस्थायी तालिका में स्टोर:

create temporary table tmp_top3counts 
    select distinct eventcount 
    from tmp_eventcounts 
    order by eventcount desc 
    limit 3 
; 

tmp_top3counts की सामग्री:

| eventcount | 
|------------| 
|   3 | 
|   2 | 
|   1 | 

अब शीर्ष 3 eventcounts के साथ सभी eventnames चयन लेकिन eventcount> 1. इसके अलावा अप शीर्ष 3 eventcounts के साथ दो eventnames लेकिन eventcount करने के लिए चयन = 1. उपयोग यूनिअन दो परिणामों को जोड़ रहे हैं:

select eventcount, eventname 
from tmp_top3counts 
join tmp_eventcounts using(eventcount) 
where eventcount > 1 
union all (
    select eventcount, eventname 
    from tmp_top3counts 
    join tmp_eventcounts using(eventcount) 
    where eventcount = 1 
    limit 2 
) 
order by eventcount desc; 

परिणाम:

| eventcount |  eventname | 
|------------|------------------| 
|   3 | machine1.started | 
|   2 | machine1.stopped | 
|   2 | cluster.alive | 
|   1 | machine2.reboot | 
|   1 | machine3.started | 

http://sqlfiddle.com/#!9/b332df/1

आप अस्थायी तालिकाओं का उपयोग नहीं कर सकते हैं, तो आप उनकी परिभाषा के साथ अपने घटनाओं की जगह एक एक बेहद अपठनीय लेकिन काम कर क्वेरी बना सकते हैं: इस पागल लग सकता है जबकि

select eventcount, eventname 
from (
    select distinct eventcount 
    from (
    select eventname, count(1) as eventcount 
    from eventlog 
    group by eventname 
) tmp_eventcounts 
    order by eventcount desc 
    limit 3 
) tmp_top3counts 
join (
    select eventname, count(1) as eventcount 
    from eventlog 
    group by eventname 
) tmp_eventcounts using(eventcount) 
where eventcount > 1 
union all (
    select eventcount, eventname 
    from (
    select distinct eventcount 
    from (
     select eventname, count(1) as eventcount 
     from eventlog 
     group by eventname 
    ) tmp_eventcounts 
    order by eventcount desc 
    limit 3 
) tmp_top3counts 
    join (
    select eventname, count(1) as eventcount 
    from eventlog 
    group by eventname 
) tmp_eventcounts using(eventcount) 
    where eventcount = 1 
    limit 2 
) 
order by eventcount desc; 

http://sqlfiddle.com/#!9/2eea6/4 ;-)

यह, आसानी से PHP में बनाया जा सकता है:

$tmp_eventcounts = " 
    select eventname, count(1) as eventcount 
    from eventlog 
    group by eventname 
"; 

$tmp_top3counts = " 
    select distinct eventcount 
    from ({$tmp_eventcounts}) tmp_eventcounts 
    order by eventcount desc 
    limit 3 
"; 

$sql = " 
    select eventcount, eventname 
    from ({$tmp_top3counts}) tmp_top3counts 
    join ({$tmp_eventcounts}) tmp_eventcounts using(eventcount) 
    where eventcount > 1 
    union all (
     select eventcount, eventname 
     from ({$tmp_top3counts}) tmp_top3counts 
     join ({$tmp_eventcounts}) tmp_eventcounts using(eventcount) 
     where eventcount = 1 
     limit 2 
    ) 
    order by eventcount desc 
"; 

नोट: ऐसा लगता है कि MySQL की तरह एक ही सबक्वेरी बार-बार निष्पादित करने के लिए की आवश्यकता होगी। लेकिन यह परिणामों को कैश करने और उनका पुन: उपयोग करने में सक्षम होना चाहिए।

 संबंधित मुद्दे