【问题标题】:MariaDB: Select average per month per item (Pivot table)MariaDB:选择每个项目每月的平均值(数据透视表)
【发布时间】:2022-01-22 23:16:23
【问题描述】:

我的 MariaDB 表如下所示:

Component Timestamp Duration
Component one 2021-10-01 14:04:54 40
Component one 2021-11-01 14:04:45 10
Component one 2021-11-11 11:05:23 20
Component one 2021-12-01 13:04:43 20
Component one 2021-12-12 12:14:11 30
Component two 2021-11-01 14:04:27 45
Component two 2021-12-01 13:04:08 23

我想做的是显示过去三个月中每个组件的平均持续时间。它应该看起来像这样:

Component AVG Duration (October) AVG Duration (November) AVG Duration (December)
Component one 40 15 25
Component two 45 23

我尝试弄乱我在网上找到的数据透视表,但下面的查询仍然导致多行(每个月 1 行,该行其他月份为空)

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
               CONCAT('(IF(MONTHNAME(s.LOG_TIMESTAMP) = "', MONTHNAME(`LOG_TIMESTAMP`),'", AVG(`DURATION`),"")) AS ',MONTHNAME(LOG_TIMESTAMP) )
              ) INTO @sql
FROM table1;


SET @sql = CONCAT('SELECT s.COMPONENT,  ', @sql, ' 
                  FROM table1 s
                 GROUP BY s.COMPONENT, MONTHNAME(s.LOG_TIMESTAMP)
                 ORDER BY s.COMPONENT');
SELECT @sql;
PREPARE stmt FROM @sql;
EXECUTE stmt;

因此,如果我运行该查询,组件 1 的输出(例如)如下所示:

Component AVG Duration (October) AVG Duration (November) AVG Duration (December)
Component one 40
Component one 15
Component one 25

这可能是具有 log_timestamp 的 group-by 表达式,但如果我删除它,concat 函数只会写入第一个可用月份的平均持续时间(所有月份)。我对数据透视表没有任何经验,所以我在这里有点超出我的深度。任何帮助将不胜感激。

【问题讨论】:

    标签: mysql mariadb pivot-table


    【解决方案1】:

    分两步完成:

    1. 编写查询以计算平均值。
    2. 使用“派生表”中的第 1 步查询编写要进行透视的查询。

    【讨论】:

      【解决方案2】:

      可能是这样的最终查询:

      SELECT Component,
             MAX(IF(mth=10,avgd,0)) AS 'October',
             MAX(IF(mth=11,avgd,0)) AS 'November',
             MAX(IF(mth=12,avgd,0)) AS 'December'
       FROM
      (SELECT Component,
             MONTH(LOG_TIMESTAMP) mth,
             AVG(Duration) Avgd
      FROM table1
      GROUP BY Component, mth) v
      GROUP BY Component;
      

      和prepared statement的语法如下:

      SET @sql := NULL;
      
      SELECT CONCAT('SELECT Component,',
             GROUP_CONCAT(CONCAT('
             MAX(IF(mth=',mth,',avgd,0)) AS "',mthname,'"') 
             SEPARATOR ','),
             '
      FROM (SELECT Component,
             MONTH(LOG_TIMESTAMP) mth,
             AVG(Duration) Avgd
      FROM table1
      GROUP BY Component, mth) v
      GROUP BY Component') INTO @sql
      FROM
      (SELECT DISTINCT MONTH(LOG_TIMESTAMP) mth, 
             MONTHNAME(LOG_TIMESTAMP) mthname
      FROM table1) a;
      
      SELECT @sql;
      
      PREPARE stmt FROM @sql;
      EXECUTE stmt;
      DEALLOCATE PREPARE stmt;
      

      Demo fiddle

      【讨论】:

      • 太完美了!似乎正是我需要的。如果您愿意回答,我只有两个问题; 1. 假设我有 2021 年 12 月的结果,但也有 2020 年 12 月的结果。MAX 是否确保我总是得到最新的结果?我想防止使用明确的年份,这样我就不必每年更改查询 2. 准备好的语句和最终查询之间的区别是在最终查询中我得到所有三个月的平均值(无论数据如何)并且使用准备好的语句,我只能得到至少 1 个组件有数据的月份?这是预期的效果吗?
      • 好吧,MAX() 实际上只是为了从子查询中获取 avgd 值。在哪一年,当前查询将采用与定义的月份相对应的所有内容,而不管任何年份。如果您的条件仅适用于当年,那么您应该在子查询中添加 WHERE YEAR(LOG_TIMESTAMP)=YEAR(NOW()) 以确保这一点。 See this.
      • 我想对于你的第二个问题,我猜你的意思是无论一个组件是否有数据,你仍然想返回它们。假设有两个表,一个是组件表,另一个是日志表。然后,您可以在它们上添加LEFT JOIN 并返回每个现有组件,而不管数据如何。也许like in this fiddle 的东西?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-12
      • 2013-12-04
      • 2021-10-13
      • 1970-01-01
      相关资源
      最近更新 更多