Home > database >  MariaDB: Select average per month per item (Pivot table)
MariaDB: Select average per month per item (Pivot table)

Time:12-23

I have MariaDB table that looks like this:

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

What I'd like to do is show the average duration of each component over the last three months. It should look something like this :

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

I tried messing around with a pivot table I found online, but the below query still results in multiple lines (1 for each month, with the other months empty on that line)

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;

So if I run that query the output for component 1 (for example) is like this :

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

It's probably the group-by expression having the log_timestamp, but if I remove that one the concat function just writes the average duration (of all months) in the first available month. I don't have any experience with pivotting tables so I'm a bit out of my depth here. Any help would be appreciated.

CodePudding user response:

Do it in 2 steps:

  1. Write a query to compute the averages.
  2. Write a query to pivot, using the step-1 query in a 'derived table'.

CodePudding user response:

Perhaps a final query like this:

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;

And prepared statement syntax of the following:

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

  • Related