2016-09-05 12 views
5

Tôi đang ghi sự kiện vào cơ sở dữ liệu mySQL và muốn nhận được 3 sự kiện hàng đầu cho mục đích giám sát.Giới hạn GROUP BY dựa trên giá trị COUNT() trong mySQL

bảng My eventlog trông như thế này:

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

Truy vấn cuối cùng nên trở về sau, giữ

  • 3 sự kiện hàng đầu đã xảy ra hầu hết thường (dựa trên cột eventcount s được tạo bởi chức năng COUNT() của mySQL), được nhóm theo eventname
  • chỉ 2 hàng có eventcount = 1, nhưng chỉ khi 1 nằm trong top 3 eventcounts (vì có rất nhiều sự kiện xảy ra chỉ một lần và do đó sẽ quá tải frontend của tôi)

Ví dụ về kết quả mong muốn, dựa trên bảng trên:

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

Xin lưu ý rằng Tôi không chỉ cần 3 hàng trả lại mà là các hàng có 3 số cao nhất là eventcount s.

Tôi đã làm rất nhiều thử nghiệm bằng cách xáo trộn chuỗi truy vấn bên dưới, bao gồm nhiều lựa chọn và các điều kiện có điều kiện CASE ... WHEN, nhưng không thể làm cho nó hoạt động theo cách tôi cần.

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

Cách tiếp cận tốt nhất để đạt được kết quả mong muốn theo cách hiệu quả là gì?

+0

@RiggsKhông, không. Kết quả mong muốn cho dữ liệu mẫu có 5 hàng – Lamak

+0

@Lamak Điểm tốt – RiggsFolly

Trả lời

2

đây là một cách để làm việc đó sử dụng các biến SQL Fiddle cho nó: 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

Đối với bất cứ ai tự hỏi: @Gordon Linoff và giải pháp của Matt cả hai công việc hoàn hảo cho mục đích của tôi. Tôi chấp nhận câu trả lời này vì truy vấn kết thúc một dấu tick nhanh hơn. – IggyBlob

2

Những loại điều kiện này trong MySQL rất đau đớn. Một phương pháp sử dụng các biến. Đây là một phương pháp mà không:

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; 

EDIT:

Các giải pháp sử dụng các biến trông như thế này và bao gồm các giới hạn về 2 cho một số trong tổng số 1:

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); 

Các thâm tâm đếm đếm số đếm. Thứ hai liệt kê trong một số. Trên thực tế, cả hai có thể có thể được kết hợp thành một truy vấn con duy nhất, nhưng cẩn thận.

+0

do đó điều này sẽ thực hiện đúng thứ hạng, nhưng trong trường hợp eventcount = 1 nằm trong 3 kết quả hàng đầu thì OP chỉ muốn trả lại 2 trong số những bản ghi này sẽ không giới hạn số đó và sẽ trả về tất cả 4 trong trường hợp thử nghiệm ở trên – Matt

+0

@Matt đúng vậy, thứ hạng hoạt động tốt và nó cũng có vẻ chạy rất hiệu quả nhưng không xem xét giới hạn cho sự kiện = 1 ... – IggyBlob

+0

DISTINCT COUNT GROUP BY ?? Eh? – Strawberry

-1

Bạn có thể thử này ra:

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

này sẽ có thể được refactored một chút, nhưng nó trả về câu trả lời chính xác kể từ bây giờ:

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; 

SQL Fiddle: http://sqlfiddle.com/#!9/10f0d/92

0

Nếu bạn có thể sử dụng các bảng tạm thời ..

precalculate các eventcounts và lưu trữ kết quả trong một bảng tạm thời:

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

Nội dung :

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

Chọn 3 eventcounts đầu và lưu trữ chúng trong một bảng tạm thời:

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

Nội dung của tmp_top3counts:

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

Bây giờ chọn tất cả eventnames với top 3 eventcounts nhưng eventcount> 1. Cũng chọn tối đa hai eventnames với top 3 eventcounts nhưng eventcount = 1. Sử dụng UNION để kết hợp hai kết quả:

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; 

Kết quả:

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

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

Nếu bạn không thể sử dụng bảng tạm thời bạn có thể thay thế sự xuất hiện của họ với các định nghĩa của họ một tạo một truy vấn cao không đọc được nhưng làm việc:

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 ;-)

Trong khi điều này có thể trông điên, nó có thể dễ dàng được tạo bằng 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 
"; 

Lưu ý: Có vẻ như MySQL sẽ cần thực thi cùng một truy vấn con một lần nữa. Nhưng nó sẽ có thể lưu trữ kết quả và tái sử dụng chúng.