代码之家  ›  专栏  ›  技术社区  ›  Kevin Sun

在MySql(8.0.13)中,不使用ORDERBY和limit子句,按列分组,查找max和second max之间的差异

  •  1
  • Kevin Sun  · 技术社区  · 7 年前

    我有一个包含ID、ad_ID、amount_time列的表,其中显示了用户的ID、他们看到的广告以及他们看到广告的时间。以下是数据示例:

    table name: ads
    
    ID | ad_id | amount_time
     1      2        600            
     1      3        300
     3      1        400
     1      3        100
     1      1        700
    

    我们希望结果显示最大和第二最大时间量之间的差异,按ID和ad ID分组

    所以结果是

    ID |ad_id | diff_amount_time
     1    3      200
    

    我可以通过执行以下命令来获得total表的max和second_max:

    select
    (SELECT MAX(amount_time) FROM ads) maxtime,
    (SELECT MAX(amount_time) FROM ads
    WHERE amount_time NOT IN (SELECT MAX(amount_time) FROM ads )) as 
    second_max_time
    

    然而,为了得到结果,我在合并GROUPBY子句时遇到了困难。我知道有一种合并的方法 order by limit 2 获取最大值和第二个最大值,但这在计算上很昂贵,并且想知道是否有另一个解决方案,而无需对amount\u time列排序。

    1 回复  |  直到 7 年前
        1
  •  1
  •   Madhur Bhaiya    7 年前

    在MySQL 8.0.2+中,最简单的 而且可能是最有效的 方法是使用 Window Functions .

    我们将使用 Row_Number() ID ad_id . 行号将按降序排列 amount_time 价值所以最高的 时间量 值的行号为1,第二高的行号为2。

    现在,我们将使用此结果集作为 Derived Table ,及合计( GROUP BY )在 身份证件 . 我们可以使用条件语句 CASE .. WHEN 表达式,以确定每个组中最高值和第二高值之间的差异。

    SELECT
      dt.ID, 
      dt.ad_id, 
      (MAX(CASE WHEN dt.row_no = 1 THEN dt.amount_time END) - 
      MAX(CASE WHEN dt.row_no = 2 THEN dt.amount_time END)) AS diff_amount_time
    FROM 
    (
     SELECT 
       ID, 
       ad_id, 
       amount_time, 
       ROW_NUMBER() OVER (PARTITION BY CONCAT(ID, '-', ad_id)
                          ORDER BY amount_desc) AS row_no
     FROM ads 
    ) AS dt 
    GROUP BY dt.ID, dt.ad_id 
    -- to remove cases where there is no second highest
    -- when there is no second highest amount, then the difference will be null
    -- because 5 - null = null
    HAVING diff_amount_time IS NOT NULL