使用日期列创建类似于表的数据透视表的最佳方法

时间:2013-08-29 01:28:02

标签: php mysql sql pivot

我有一些包含数据的表,我正在尝试按以下格式生成类似于表的数据集:

Area  | Type | 2013-08-25 | 2013-08-26 | 2013-08-27, etc..
South | Red  |     5      |      2     |       9
North | Blue |     3      |      0     |       7

目前为了生成这个,我使用PHP遍历每个日期并生成总和(如果每个日期的语句基于给定的开始和结束日期。

我知道我可以先运行查询并按日期分组,然后在PHP中组装它,但SQL解决方案似乎比这更容易。

这是完成我想要做的事情的最佳方式,还是有另一种我更容易丢失的方式?

示例查询:

SELECT 
iw.display_name as Area, 
iet.type as 'Type', 
sum(if(date(iac.created_at) = '2013-08-25',1,0)) as '2013-08-25', 
sum(if(date(iac.created_at) = '2013-08-26',1,0)) as '2013-08-26', 
sum(if(date(iac.created_at) = '2013-08-27',1,0)) as '2013-08-27', 
sum(if(date(iac.created_at) = '2013-08-28',1,0)) as '2013-08-28', 
sum(if(date(iac.created_at) = '2013-08-29',1,0)) as '2013-08-29', 
sum(if(date(iac.created_at) = '2013-08-30',1,0)) as '2013-08-30', 
sum(if(date(iac.created_at) = '2013-08-31',1,0)) as '2013-08-31',  
count(iac.id) as total 
FROM iac
JOIN ioc on ioc.id = iac.ioc_id
JOIN iet on iet.id = ioc.iet_id
JOIN iw  on  iw.id = iac.iw_id
WHERE date(iac.created_at) between '2013-08-25' and '2013-08-31'
GROUP BY iw.id, iet.id
ORDER BY iw.display_name, iet.type

2 个答案:

答案 0 :(得分:1)

你的方法很好。您可以通过删除if条件来简化它(因为您使用的是MySQL)。对于true,布尔值被视为1,对于false,被视为0

SELECT iw.display_name as Area, iet.type as `Type`, 
       sum(date(iac.created_at) = '2013-08-25') as `2013-08-25`, 
       sum(date(iac.created_at) = '2013-08-26') as `2013-08-26`, 
       sum(date(iac.created_at) = '2013-08-27') as `2013-08-27`, 
       sum(date(iac.created_at) = '2013-08-28') as `2013-08-28`, 
       sum(date(iac.created_at) = '2013-08-29') as `2013-08-29`, 
       sum(date(iac.created_at) = '2013-08-30') as `2013-08-30`, 
       sum(date(iac.created_at) = '2013-08-31') as `2013-08-31`,  
       count(iac.id) as total 
FROM iac
JOIN ioc on ioc.id = iac.ioc_id
JOIN iet on iet.id = ioc.iet_id
JOIN iw  on  iw.id = iac.iw_id
WHERE iac.created_at >= date('2013-08-25') and iac.created_at < date('2013-09-01')
GROUP BY iw.id, iet.id
ORDER BY iw.display_name, iet.type;

我还用后引号替换列别名中的单引号。您应该只对字符串常量使用单引号。

我还用不等式替换了日期逻辑。这将删除列周围的函数调用(date()),使得更有可能将索引用于此过滤。

答案 1 :(得分:1)

在枢转方面,这与MySql中的一样好。您可以通过使用动态SQL实现此查询并将其包装到像这样的存储过程中来为自己简化事情(为了不每次都键入所有可能的日期)

DELIMITER $$
CREATE PROCEDURE sp_report(IN dt_start DATE, IN dt_end DATE)
BEGIN
  SET @sql = NULL;

  SELECT GROUP_CONCAT(DISTINCT
           CONCAT('SUM(CASE WHEN DATE(iac.created_at) = ''',
           DATE_FORMAT(created_at, '%Y-%m-%d'),
           ''' THEN 1 ELSE 0 END) `',
           DATE_FORMAT(created_at, '%Y-%m-%d'), '`'))
    INTO @sql
    FROM iac
   WHERE iac.created_at >= DATE_FORMAT(dt_start, '%Y-%m-%d 00:00:00') 
     AND iac.created_at <= DATE_FORMAT(dt_end,   '%Y-%m-%d 23:59:59') ;

  SET @sql = CONCAT('SELECT iw.display_name area, iet.type `type`, ', @sql, 
                    ', COUNT(iac.id) total 
                       FROM iac JOIN ioc 
                         ON ioc.id = iac.ioc_id JOIN iet 
                         ON iet.id = ioc.iet_id JOIN iw  
                         ON iw.id  = iac.iw_id
                      WHERE iac.created_at >= ', DATE_FORMAT(dt_start, '%Y-%m-%d 00:00:00'),  
                    '   AND iac.created_at <= ', DATE_FORMAT(dt_end,   '%Y-%m-%d 23:59:59'),
                    ' GROUP BY iw.id, iet.id
                      ORDER BY iw.display_name, iet.type');
  PREPARE stmt FROM @sql;
  EXECUTE stmt;
  DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;

样本用法:

CALL sp_report('2013-08-25', '2013-08-31');