【问题标题】:SQL sort by count with condition, show historySQL 按条件计数排序,显示历史记录
【发布时间】:2013-06-22 01:32:23
【问题描述】:

考虑以下简单的 MySQL 查询:

SELECT date, count(*) cnt, ip 
FROM fwlog
GROUP BY ip, date
HAVING cnt>100
ORDER BY date DESC, cnt DESC

它给了我类似的东西:

date    cnt     src
2013-06-20      14441   172.16.a
2013-06-20      8887    172.16.b
....
2013-06-19      14606   172.16.b
2013-06-19      12455   172.16.a
2013-06-19      5205    172.16.c

也就是说,它按日期排序 IP,然后按计数排序。

现在我希望结果是:

  • 首先显示 TODAY 计数最高的 IP,
  • 然后显示过去几天该 IP 的计数(与 cnt 无关),
  • 然后显示 TODAY 计数第二高的 IP,
  • 然后是该 IP 的历史,
  • etc.pp.

期望结果示例:

date    cnt     src
2013-06-20      14441   172.16.a
2013-06-19      12455   172.16.a
2013-06-18      ....    172.16.a
2013-06-17      ....    172.16.a
....
2013-06-20      8887    172.16.b
2013-06-19      14606   172.16.b
2013-06-18      ....    172.16.b
2013-06-17      ....    172.16.b
...
2013-06-20      ....    172.16.c
2013-06-19      ....    172.16.c
2013-06-18      ....    172.16.c
2013-06-17      ....    172.16.c
...
...

这甚至可以使用纯 SQL 来完成吗?

再见,

马克

============================================

@戈登林诺夫:

SELECT 
    datex,
    cnt,
    ip
FROM
    fwlog
WHERE
    ...
GROUP BY ip , datex
ORDER BY SUM(case
    when datex = DATE(NOW()) then 1
    else 0
end) DESC , src, date DESC, cnt DESC

2013-06-20      47      10.11.y
2013-06-19      47      10.11.y
2013-06-18      45      10.11.y
2013-06-17      42      10.11.y
2013-06-16      14      10.11.y
....
2013-06-20      592     172.16.a
2013-06-19      910     172.16.a
2013-06-18      594     172.16.a
2013-06-17      586     172.16.a
2013-06-20      299     172.16.b

这还不完全正确,下块应该在顶部。

【问题讨论】:

  • 您使用的是哪个 RDBMS?
  • “显示过去几天该 IP 的计数” - 多少天?
  • 数据库中只有那么多天的历史。不管有多少,都要显示出来。

标签: mysql sql


【解决方案1】:

试试:

SELECT a.`date`, count(*) cnt, a.ip 
FROM fwlog a
JOIN (SELECT ip, count(*) today_count
      FROM fwlog 
      where `date` = date(now()) 
      group by ip) t
  ON a.ip = t.ip and t.today_count > 100
GROUP BY a.ip, a.date
ORDER BY t.today_count DESC, a.ip, a.`date` DESC

【讨论】:

  • 我相信您忘记了内部语句中的 GROUP BY,但对于其余部分,您的想法有效,如下所示。
【解决方案2】:

您需要在order by 子句中添加当前日期的计数。获取当前日期因数据库而异。这是 SQL Server 的版本:

SELECT date, count(*) cnt, ip 
FROM fwlog
GROUP BY ip, date
HAVING cnt > 100
ORDER BY SUM(case when date = cast(GETDATE() as date) then 1 else 0 end) desc,
         ip,
         date DESC, cnt DESC

ip 上的排序会处理两个当前计数相同的情况。

其他数据库可能使用now()、sysdate()`、CURRENT_TIMESTAMP 或类似的名称或当前日期。

【讨论】:

    【解决方案3】:

    以下似乎有效。谢谢。

    SELECT 
        str_to_date(a.date, _utf8'%e%b%Y') datex,
        count(*) cnt,
        a.src
    FROM
        fwlog a
            JOIN
        (SELECT 
            src, count(*) latest_count
        FROM
            fwlog
        WHERE
            str_to_date(fwlog.date, '%e%b%Y') = subdate(current_date, 1)
                AND ((fwlog.action <> 'accept')
                AND (fwlog.message_info <> 'Implied rule')
                AND (fwlog.proto NOT IN ('icmp' , 'igmp')))
        GROUP BY src) b ON a.src = b.src AND b.latest_count > 10
    WHERE
        ((a.action <> 'accept')
            AND (a.message_info <> 'Implied rule')
            AND (a.proto NOT IN ('icmp' , 'igmp')))
    GROUP BY a.src , datex
    ORDER BY b.latest_count DESC , a.src , datex DESC
    
    • 使用str_to_date是因为格式最初不是真正的日期类型
    • 有一些 WHERE 条件,您必须注意它们存在于内部和外部查询中
    • 使用 subdate 是因为 latest_count 来自昨天(但这是一个细节,它也适用于今天)

    最后你会得到这样一个不错的结果:

    '2013-06-20', '14441', '172.16.a'
    '2013-06-19', '14606', '172.16.a'
    '2013-06-18', '14338', '172.16.a'
    '2013-06-17', '7665', '172.16.a'
    '2013-06-20', '8887', '172.16.b'
    '2013-06-19', '12455', '172.16.b'
    '2013-06-18', '10040', '172.16.b'
    '2013-06-20', '5744', '172.16.c'
    ....
    

    我接受 Mark Ba​​nnister 的回答,因为它最接近有效。

    【讨论】:

      猜你喜欢
      • 2020-04-05
      • 2012-10-18
      • 1970-01-01
      • 1970-01-01
      • 2020-10-30
      • 2014-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多