【问题标题】:how to return most frequent value for a certain column for each group in GROUP BY query?如何在 GROUP BY 查询中为每个组返回某个列的最频繁值?
【发布时间】:2015-12-30 18:13:52
【问题描述】:

我有这个示例表:

  sort_order  product      color    productid   price
  ----------  -------      ------   ---------   -----
      1       bicycle       red      2573257     50
      2       bicycle       red      0983989     40
      3       bicycle       red      2093802     45
      4       bicycle       blue     9283409     55
      5       bicycle       blue     3982734     60
      1       teddy bear    brown    9847598     20
      2       teddy bear    black    3975897     25
      3       teddy bear    white    2983428     30
      4       teddy bear    brown    3984939     35
      5       teddy bear    brown    0923842     30
      1       tricycle      pink     2356235     25
      2       tricycle      blue     2394823     30
      3       tricycle      blue     9338832     35
      4       tricycle      pink     2383939     30
      5       tricycle      blue     3982982     35

我想要一个返回产品、平均价格和最常见颜色的查询。

所以我在这个例子中的查询应该会返回:

product      most_frequent_color     average_price
-------      -------------------     -------------
bicycle      red                     50
teddy bear   brown                   28
tricycle     blue                    31

平均部分似乎很容易按产品分组并使用 avg(price),但是如何解决最常见的颜色部分?

这是我到目前为止自己能弄清楚的查询,但我不知道如何获得每个组的 most_frequent_color:

SELECT product, avg(price) AS average_price from products
WHERE sort_order <= 5
GROUP BY product

在我的真实世界表中,每个组的行数通常比我感兴趣的要多,所以我只使用 sort_order 字段获得有限的行数

对于在“颜色”的所有行中具有 null 或具有多个最常见颜色的稀有组,我想在返回的 most_frequent_color 列中返回 null

感谢您对此的任何帮助!

【问题讨论】:

  • 如果一个项目有两种(或更多)最常见的颜色,会发生什么?您或许应该将其添加到您的示例数据中。
  • 看起来像家庭作业?我会说你需要把它看作一个两阶段的问题。
  • 预期结果不明确或错误。红色自行车的平均价格是 45 而不是 50。蓝色三轮车的平均价格是 38,33
  • @Alex.. 平均值是针对产品,而不是颜色。
  • @jarlh 好点。我在示例表中添加了一个字段“sort_order”。在我的真实世界表中,我确实有这个,我目前在 where 子句中使用 sort_order

标签: mysql sql group-by


【解决方案1】:

您可以在SELECT 子句中使用附加查询来有效地对相同数据执行聚合查询:

SELECT   t.product,
         Avg ( t.price ) AS average_price,
         (
                  SELECT   IF ( Count(*) = t4.count, NULL, t2.color ) 'color'
                  FROM     products t2
                  JOIN
                           (
                                    SELECT   t3.product,
                                             t3.color,
                                             count(*) 'count'
                                    FROM     products t3
                                    GROUP BY t3.product ,
                                             t3.color
                                    ORDER BY count(*) DESC
                           ) t4
                  ON       t2.product = t4.product
                           AND t2.color <> t4.color
                  WHERE    t2.product = t.product
                  GROUP BY t2.color
                  ORDER BY count(*) DESC limit 1
         ) AS most_frequent_color
FROM     products t
WHERE    t.sort_order <= 5
GROUP BY t.product

所以我们使用product 列链接products 的第二个副本,选择列表顶部出现频率最高的每种颜色(对于该产品)的计数,然后只取第一行 - 因此该产品最常见的颜色值。

这与内联视图(位于查询的FROM 子句中)不同。

注意: 这适用于 MySQL,但它与数据库无关。

更新: 现在检查超过 1 种具有相同频率的颜色并返回 null。

【讨论】:

  • 感谢 Raad 的帮助。我已经在我的真实世界表中尝试了你的方法,它比示例表大得多,它只是保持计算直到它达到我的套接字超时。也许您的方法非常耗费资源?我现在将尝试建议的其他方法。谢谢!
  • 我刚刚意识到我没有为相关字段编制索引。我现在会改进它并再次运行查询并通知您
  • @dodecafonico - 是的,正确的索引肯定会有所帮助!
  • 在对查询进行大量索引后,我总共需要 140 秒才能完成。我想这是可以接受的,因为建议的其他方法需要更长的时间。唯一的问题是它不符合其中一项要求,因为如果多于一种颜色是 most_frequent,则查询在应该返回 null 时返回第一个颜色
  • 非常奇怪但很棒。我的查询保存在我的 DBM(Navicat) 查询中,需要 140 秒。我将它复制并粘贴到一个新的查询窗口中并执行它,它只需要 0.86 秒。不确定这是 Navicat 的错误还是什么,但我的印象是保存的查询神秘地没有使用索引,但我知道什么?
【解决方案2】:
SELECT m.product
     , AVG(m.price) avg_price
     , n.color most_frequent
  FROM my_table m
  JOIN 
     ( SELECT x.product
            , x.color
         FROM 
            ( SELECT product
                   , color
                   , COUNT(color) total
                FROM my_table
               GROUP
                  BY product
                   , color
            ) x
         JOIN
            ( SELECT product
                   , MAX(total) max_total
                FROM 
                   ( SELECT product
                          , color
                          , COUNT(color) total
                       FROM my_table
                      GROUP
                         BY product
                          , color
                   ) a
               GROUP
                  BY product
           ) y
         ON y.product = x.product
        AND y.max_total = x.total
     ) n
    ON n.product = m.product
 GROUP
    BY m.product;

【讨论】:

    【解决方案3】:

    这是一种方法。

    Fiddle with sample data

    select r.product, q.color, r.avgprice
    from
    (
    select product, avg(price) as avgprice
    from t
    group by product
    ) r
    join
    (
    select p.product, p.color
    from
    (
    select product, color, count(*) as cnt
    from t 
    group by product, color
     ) p
    join
        (
        select product, max(cnt) as maxcnt 
        from (
        select product, color, count(*) as cnt
        from t
        group by product, color) x
        group by product) y
    on y.product = p.product and y.maxcnt = p.cnt
     ) q
    on r.product = q.product
    

    【讨论】:

      猜你喜欢
      • 2015-07-07
      • 1970-01-01
      • 2017-04-30
      • 2021-12-09
      • 1970-01-01
      • 1970-01-01
      • 2019-08-27
      • 1970-01-01
      • 2018-01-30
      相关资源
      最近更新 更多