在MySQL中聚合表数据,有更简单的方法吗?

时间:2017-05-18 15:06:01

标签: mysql

我试图编写一个聚合表中数据的查询。

基本上,我有很长的设备列表,这些设备已经过库存并最终安装在过去几年中。

我想查找收到设备和安装设备之间的平均时间,然后按设备安装月份排序。但是在每个月的行中,我还想包括前几个月的数据。

基本上我想看到的是:(抱歉可怕的格式化)

MonthInstalled   | TimeToInstall | Total#Devices
-----------------+---------------+----------------------------
Jan              | 10 Days       | 5
Feb(=Jan+Feb)    | 15 Days       | 18 (5 in Jan + 13 in Feb)    
Mar(=Jan+Feb+Mar)| 13 Days       | 25 (5 + 13 + 7)

...

我目前编写的查询如下所示:

INSERT INTO DevicesInstall
SELECT ROUND(AVG(DATEDIFF(dvc.dt_install , dvc.dt_receive)), 1) AS 'Install', 
  COUNT(dvc.dvc_model) AS 'Total Devices', 
  MAX(dvc.dt_install) AS 'Date', 
  loc.loc_campus AS 'Campus' 
FROM dvc_info dvc, location loc 
WHERE dvc.dvc_loc_bin = loc.loc_bin 
  AND dvc.dt_install < '20160201'
;

虽然这是有用的,但我必须手动每月迭代一次,因此它无法扩展。有没有办法压缩这个?

1 个答案:

答案 0 :(得分:0)

我们可以使用内联视图(派生表)返回日期,然后加入dvc_info表,这样我们就可以获得&#34;累积&#34;结果

获取结果:

Jan
Jan+Feb
Jan+Feb+Mar

我们需要为Jan返回三个行副本,并为Feb返回两个行副本,然后将这些行折叠到适当的组中。

loc_campus被包含在SELECT列表中......不清楚为什么需要它。如果我们想要结果&#34; by campus&#34;,那么我们需要在GROUP BY子句中包含该表达式。否则,为该非聚合返回的值是不确定的...我们将在组&#34;中获得某行&#34;的值,但它可以是任何行。

这样的事情:

SELECT d.dt                                                  AS `before_date`
     , loc.loc_campus                                        AS `Campus`
     , ROUND(AVG(DATEDIFF(dvc.dt_install,dvc.dt_receive)),1) AS `Install`
     , COUNT(dvc.dvc_model)                                  AS `Total Devices`
     , MAX(dvc.dt_install)                                   AS `latest_dt_install`
  FROM ( SELECT           '2016-01-01' + INTERVAL  1 MONTH AS dt
         UNION ALL SELECT '2016-01-01' + INTERVAL  2 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL  3 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL  4 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL  5 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL  6 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL  7 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL  8 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL  9 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL 10 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL 11 MONTH
         UNION ALL SELECT '2016-01-01' + INTERVAL 12 MONTH
       ) d
 CROSS
  JOIN location loc
  LEFT     
  JOIN dvc_info dvc
    ON dvc.dvc_loc_bin = loc.loc_bin
   AND dvc.dt_install  < d.dt
 GROUP
    BY d.dt
     , loc.loc_campus
 ORDER
    BY d.dt
     , loc.loc_campus

请注意,d.dt返回的值将是&#34;直到&#34;日期。我们要去2016-02-01&#39;返回1月份的结果。如果我们想要返回1月日期的值,我们可以使用SELECT列表中的表达式...

SELECT DATE_FORMAT(d.dt + INTERVAL -1 MONTH,'%Y-%m')    AS `month`

有关查询替代方案的大量选项。

但它看起来像是&#34;大驼峰&#34;是为了获得累积结果,我们需要返回dvc_info行的多个副本,因此可以将行折叠到每个&#34;分组&#34;。

我建议先处理SELECT。并且在进行调整之前将其转换为INSERT ... SELECT

关注

我们可以将任何查询用作内联视图(派生表d),它返回我们想要的一组日期。

e.g。

  FROM ( SELECT DATE_FORMAT(m.install_dt,'%Y-%m-01') + INTERVAL 1 MONTH AS dt
           FROM dvc_install m
          WHERE m.install_dt >= '2016-01-01'
          GROUP BY DATE_FORMAT(m.install_dt,'%Y-%m-01') + INTERVAL 1 MONTH
       ) d

请注意,使用这种方法,如果2月份没有install_dt,我们将无法获得2月份的回复。使用静态UNION ALL SELECT方法可以让我们回归&#34;零&#34;计数,即在那个月没有install_dt的月份返回行数。 (但那是另一个问题的答案......如果没有任何行可用于二月,我如何得到2月份的&#34;零&#34;计数?)

或者,如果我们有一个日历表,例如cal包含我们想要的日期列表,我们可以引用该表代替内联视图,或者内联视图查询可以从中获取行。

  FROM ( SELECT cal.dt
           FROM cal cal
          WHERE cal.dt >= '2016-01-01'
            AND cal.dt <= NOW()
            AND DATE_FORMAT(cal.dt,'%d') = '01'
       ) d