【问题标题】:Get the most common/frequent value for each month获取每个月最常见/最频繁的值
【发布时间】:2017-01-06 11:09:44
【问题描述】:

我有一张如下所示的表格:

 id | age |       date
----|-----|--------------------
 1  | 18  | 2016-07-1 00:00:00
 2  | 20  | 2016-07-1 00:00:00
 3  | 20  | 2016-07-1 00:00:00
 4  | 22  | 2016-08-1 00:00:00
 5  | 22  | 2016-08-1 00:00:00
 6  | 30  | 2016-08-1 00:00:00
 7  | 25  | 2016-09-1 00:00:00

我需要得到每个月和年的最常见年龄。

到目前为止我有这个查询:

$ages = User::selectRaw('age, MONTH(date) as month, YEAR(date) as year, count(*) as count')
        ->groupBy(['age', 'month', 'year'])
        ->orderBy('year', 'asc')
        ->orderBy('month', 'asc')
        ->get();

这只会获取每个月 + 年的每个年龄的计数。我需要看起来像这样的东西:

[
  {
    "age": 20,
    "month": 7,
    "year": 2016,
  },
  {
    "age": 22,
    "month": 8,
    "year": 2016,
  },
  {
    "age": 25,
    "month": 9,
    "year": 2016,
  }
]

即2016 年 7 月(月 == 7)有两个 20 岁和一个 18 岁,所以 20 岁是最常见的年龄。 2016 年 8 月,最常见的是 22,依此类推...

对此有什么好的查询?谢谢。

【问题讨论】:

    标签: mysql sql database laravel eloquent


    【解决方案1】:

    试试这个

    $ages = User::selectRaw('age, MONTH(date) as month, YEAR(date) as year, count(*) as count')
        ->groupBy(['age', 'month', 'year'])
        ->orderBy('year', 'asc')
        ->orderBy('month', 'asc')
        ->havingRaw('count = MAX(count)')
        ->get();
    

    【讨论】:

      【解决方案2】:

      这个操作在 MySQL 中有点难。这里有三个选择:

      • 设计一个执行两次聚合的复杂查询
      • 使用变量
      • 利用“黑客”

      第三个看起来像这样:

      select yyyy, mm,
             substring_index(group_concat(age order by cnt desc), ',', 1) as mode
      from (select age, year(date) as yyyy, month(date) as mm, 
                   count(*) as cnt
            from test6 t
            group by age, MONTH(date), YEAR(date)
           ) t
      group by yyyy, mm;
      

      第一个看起来像这样:

      select year(date), month(date), age
      from test6 t
      group by year(date), month(date)
      having count(*) = (select count(*)
                         from test6 t2
                         where year(t2.date) = year(t.date) and
                               month(t2.date) = month(t.date)
                         group by age
                         order by count(*) desc
                         limit 1
                        );
      

      请注意,这与第一个查询返回的结果有些不同。如果最常见的是多个年龄,则此版本将返回重复项。

      【讨论】:

      • 确定 substring_index 必须返回正确的结果吗?我问是因为它在 GROUP BY 之后起作用似乎有点违反直觉。
      • @Strawberry 。 . . “在GROUP BY 之后工作是什么意思?第一个查询中有两个聚合。
      • 我想我是在问你的“hack”解决方案是否比我的“hack”解决方案更不“hacky”。​​
      • @Strawberry 。 . .是的。当列包含在select 而不是group by 中时,MySQL 被明确记录为从 indeterminate 行返回的值。因此,您的“破解”解决方案不能保证返回正确的结果。此答案中的解决方案产生正确的结果,但使用字符串操作而不是更典型的 SQL 操作。
      • 我明白了。由于将整数转换为字符串的技巧,您将解决方案称为“黑客”。我称我为 hack,正是因为它没有记录、技术上无法验证和不确定的性质(但也许我们对“hack”的定义略有不同 - 或者我只是在滑雪道上徘徊)
      【解决方案3】:

      我不知道如何重新格式化,但在 MySQL 中,有效的查询可能如下所示:

      SELECT a.* 
        FROM 
           ( SELECT DATE_FORMAT(date,'%Y-%m') yearmonth
                  , age
                  , COUNT(*) total 
               FROM my_table 
              GROUP 
                 BY yearmonth
                  , age
           ) a 
        JOIN 
           ( SELECT yearmonth
                  , MAX(total) total 
               FROM 
                  ( SELECT DATE_FORMAT(date,'%Y-%m') yearmonth
                         , age
                         , COUNT(*) total 
                      FROM my_table 
                     GROUP 
                        BY yearmonth
                         , age
                  ) x 
              GROUP 
                 BY yearmonth
           ) b 
          ON b.yearmonth = a.yearmonth 
         AND b.total = a.total;
      

      如果两个年龄在给定月份的顶部并列,则此查询将返回两者。

      如果允许“破解”解决方案,这里有一个非常棒的...

      SELECT yearmonth 
           , age 
        FROM 
           ( SELECT DATE_FORMAT(date,'%Y-%m') yearmonth
                  , age 
               FROM my_table 
              GROUP 
                 BY yearmonth
                  , age 
              ORDER 
                 BY yearmonth
                  , COUNT(*) DESC
           ) x 
       GROUP 
          BY yearmonth;
      

      在结果相同的情况下,此解决方案将以不确定的方式选择一个结果。根据文档,也不能保证产生正确的结果。虽然在实践中总是如此,但我更喜欢第一种解决方案。

      【讨论】:

        【解决方案4】:
        select substr(max(concat(lpad(count,10,'0'),age)),11) as age, month, year
          from (
           select age, MONTH(date) as month, YEAR(date) as year, count(*) as count
             from test6
            group by age, MONTH(date), YEAR(date)
          ) A
         group by month, year
         order by year, month
        

        substr(max(concat(lpad(count,10,'0'),age)),11) 组装字符串“count-age”,获取最大值(具有最大计数(*)的行),并将“年龄”切回。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-08
          • 2018-07-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-22
          • 1970-01-01
          相关资源
          最近更新 更多