2010-01-27 103 views
3

创建实时,日期范围以及与报表一起使用的最佳方式。在mysql中创建日期范围

因此,如果某一天没有活动,我可以避免在报告上出现空行。

主要是为了避免此问题:What is the most straightforward way to pad empty dates in sql results (on either mysql or perl end)?

+0

gcb,你检查了我的修改答案吗? – 2010-01-28 10:51:30

+0

现在回顾一下,我认为填充空白是由于表示层... – gcb 2010-11-17 09:05:13

+0

@code_burgar:它可以在MySQL中完成,而且你甚至不需要一个过程,在这里看到http:// stackoverflow .com/questions/2157282/generate-days-from-date-range – mathheadinclouds 2014-11-28 06:02:24

回答

1

有没有简单的方法来做到这一点在MySQL 。你最好的办法是用你选择的服务器端语言生成一个daterange数组,然后从数据库中提取数据,并将结果数组与daterange数组合并,并将日期作为关键字。

您正在使用哪种服务器端语言?

编辑:

基本上你会做的是(伪):

// Create an array with all dates for a given range 
dates = makeRange(startDate, endDate); 

getData = mysqlQuery('SELECT date, x, y, z FROM a WHERE a AND b AND c'); 

while (r = fetchRowArray(getData)) { 

    dates[ date(r['date']) ] = Array (x, y, z); 

} 

你有约会,你可以循环数组最终通过,与具有或不具有日期活动数据与它们相关联。

可以很容易地修改以按小时分组/过滤数据。

+0

那么我必须每天使用一个查询? 每小时报告如何? – gcb 2010-01-27 20:01:21

+0

你不需要每天都做一个查询。我会在一秒内编辑我的答案。 – 2010-01-27 20:06:28

+0

如果您在数据库中没有包含日期​​'2010-01-01'的任何记录,那么您的循环将错过那一天,因为它正在循环从mysql查询返回的日期。 – gcb 2010-01-27 21:29:42

9

我的建议是:不要让你的生活困难,使其更容易。只需在每个日历日创建一行表格,并拥有您认为合理需要的行数。在数据仓库中,这是常见的解决方案,并且这种方式的广泛实施使得没有它的dwh具有代码味道。

许多过去处理更传统的oltp /数据输入应用程序的人对这个想法感到自然的厌恶,因为无论如何感觉它可以生成数据,因此它不应该被存储。但是如果你确实创建了这样的表格,你可以用许多有用的属性来装饰它,比如它是一个holdiday还是一个周末,并且你可以在里面存储很多常见的日期表示(iso,european,us格式等)在创建报告时可以为您节省大量时间(因为您不必费心计算出每个报告工具中日期格式的工作方式;或者您可以更进一步,每天更新日期表以标记标志当天,当周,当月,当年,等等 - 各种有用的工具,使它非常非常容易构建需要针对某个日期范围内工作报告

MySQL的代码示例为每个请求在评论中:

delimiter // 

DROP PROCEDURE IF EXISTS p_load_dim_date 
// 

CREATE PROCEDURE p_load_dim_date (
    p_from_date DATE 
, p_to_date DATE 
) 
BEGIN 
    DECLARE v_date DATE DEFAULT p_from_date; 
    DECLARE v_month tinyint; 
    CREATE TABLE IF NOT EXISTS dim_date (
     date_key    int   primary key 
    , date_value    date 
    , date_iso    char(10) 
    , year     smallint 
    , quarter    tinyint 
    , quarter_name   char(2) 
    , month     tinyint 
    , month_name    varchar(10) 
    , month_abbreviation  varchar(10) 
    , week     char(2) 
    , day_of_month   tinyint 
    , day_of_year   smallint 
    , day_of_week   smallint 
    , day_name    varchar(10) 
    , day_abbreviation  varchar(10) 
    , is_weekend    tinyint 
    , is_weekday    tinyint 
    , is_today    tinyint 
    , is_yesterday   tinyint 
    , is_this_week   tinyint 
    , is_last_week   tinyint 
    , is_this_month   tinyint 
    , is_last_month   tinyint 
    , is_this_year   tinyint 
    , is_last_year   tinyint 
    ); 
    WHILE v_date < p_to_date DO 
     SET v_month := month(v_date); 
     INSERT INTO dim_date(
      date_key 
     , date_value 
     , date_iso 
     , year 
     , quarter 
     , quarter_name 
     , month 
     , month_name 
     , month_abbreviation 
     , week 
     , day_of_month 
     , day_of_year 
     , day_of_week 
     , day_name 
     , day_abbreviation 
     , is_weekend 
     , is_weekday 
     ) VALUES (
      v_date + 0 
     , v_date 
     , DATE_FORMAT(v_date, '%y-%c-%d') 
     , year(v_date) 
     , ((v_month - 1) DIV 3) + 1 
     , CONCAT('Q', ((v_month - 1) DIV 3) + 1) 
     , v_month 
     , DATE_FORMAT(v_date, '%M') 
     , DATE_FORMAT(v_date, '%b') 
     , DATE_FORMAT(v_date, '%u') 
     , DATE_FORMAT(v_date, '%d') 
     , DATE_FORMAT(v_date, '%j') 
     , DATE_FORMAT(v_date, '%w') + 1 
     , DATE_FORMAT(v_date, '%W') 
     , DATE_FORMAT(v_date, '%a') 
     , IF(DATE_FORMAT(v_date, '%w') IN (0,6), 1, 0) 
     , IF(DATE_FORMAT(v_date, '%w') IN (0,6), 0, 1) 
     ); 
     SET v_date := v_date + INTERVAL 1 DAY; 
    END WHILE; 
    CALL p_update_dim_date(); 
END; 
// 

DROP PROCEDURE IF EXISTS p_update_dim_date; 
// 

CREATE PROCEDURE p_update_dim_date() 
    UPDATE dim_date 
    SET is_today   = IF(date_value = current_date, 1, 0) 
    ,  is_yesterday  = IF(date_value = current_date - INTERVAL 1 DAY, 1, 0) 
    ,  is_this_week  = IF(year = year(current_date) AND week = DATE_FORMAT(current_date, '%u'), 1, 0) 
    ,  is_last_week  = IF(year = year(current_date - INTERVAL 7 DAY) AND week = DATE_FORMAT(current_date - INTERVAL 7 DAY, '%u'), 1, 0) 
    ,  is_this_month = IF(year = year(current_date) AND month = month(current_date), 1, 0) 
    ,  is_last_month = IF(year = year(current_date - INTERVAL 1 MONTH) AND month = month(current_date - INTERVAL 1 MONTH), 1, 0) 
    ,  is_this_year  = IF(year = year(current_date), 1, 0) 
    ,  is_last_year  = IF(year = year(current_date - INTERVAL 1 YEAR), 1, 0) 
    WHERE is_today 
    OR  is_yesterday 
    OR  is_this_week 
    OR  is_last_week 
    OR  is_this_month 
    OR  is_last_month 
    OR  is_this_year 
    OR  is_last_year 
    OR  IF(date_value = current_date, 1, 0) 
    OR  IF(date_value = current_date - INTERVAL 1 DAY, 1, 0) 
    OR  IF(year = year(current_date) AND week = DATE_FORMAT(current_date, '%u'), 1, 0) 
    OR  IF(year = year(current_date - INTERVAL 7 DAY) AND week = DATE_FORMAT(current_date - INTERVAL 7 DAY, '%u'), 1, 0) 
    OR  IF(year = year(current_date) AND month = month(current_date), 1, 0) 
    OR  IF(year = year(current_date - INTERVAL 1 MONTH) AND month = month(current_date - INTERVAL 1 MONTH), 1, 0) 
    OR  IF(year = year(current_date), 1, 0) 
    OR  IF(year = year(current_date - INTERVAL 1 YEAR), 1, 0) 
    ; 
// 

delimiter ; 

使用p_load_dim_date您将初始加载dim_date表,并说25年的数据。每天,午夜前,你跑p_update_dim_date。然后,您可以使用标志字段is_today,is_yesterday,is_this_week,is_last_week等来选择常用范围。当然,你应该修改这些代码以适应你的特殊需求,但这是主意。所以在飞行中没有生成范围,你只需预先提前足够长的时间。对于一天中的某个时间,可以设置类似的设计 - 您应该能够通过此代码管理自己。

对于更炫日期维度的照顾假期,和月和日本地化名称,你可以看看: http://rpbouman.blogspot.com/2007/04/kettle-tip-using-java-locales-for-date.htmlhttp://rpbouman.blogspot.com/2010/01/easter-eggs-for-mysql-and-kettle.html

+0

小心分享一个解决方案来创建一个每小时一次的表格? – gcb 2010-01-27 20:18:42

+1

gcb:如果您需要基于日期的日期范围,则每小时更新没有多大意义 - 您最终会执行24次中的23次更新。 如果您需要日期和时间范围,解决方案是创建两个表格 - 一个用于日历日(一行= 1天),另一个用于一天中的时间(一行=最小间隔适用于该问题,但通常为1秒,1分钟或1小时)。 我可以将您与解决方案联系起来,但在这种情况下,如果我知道您在使用哪个数据库以及您的报告基于哪种解决方案/语言,这将有所帮助。 – 2010-01-27 20:33:25

+0

需要基于日和小时为基础。 我在MySQL – gcb 2010-01-27 21:27:55

0

尝试使用循环在MySQL存储程序以创建日期范围:

declare iterDate date; 
    set iterDate = startDate; 

    DROP TABLE IF EXISTS MyDates; 
    create temporary table MyDates (
     theDate date 
    ); 

    label1: LOOP 
    insert into MyDates(theDate) values (iterDate); 
    SET iterDate = DATE_ADD(iterDate, INTERVAL 1 DAY); 
    IF iterDate <= endDate THEN 
     ITERATE label1; 
    END IF; 
    LEAVE label1; 
    END LOOP label1; 

    select * from MyDates; 
    DROP TABLE IF EXISTS MyDates; 

startDateendDate构成该范围的端点和作为参数传递给该程序被提供。

2

我最近做了一些研究,以查找和评估可能的选项。 http://www.freeportmetrics.com/devblog/2012/11/02/how-to-quickly-add-date-dimension-to-pentaho-mondrian-olap-cube/

您可以使用:

  • 水壶
  • 退化尺寸
  • lucidb内建功能
  • 上即将蒙德里安内置功能
  • 自己的自定义脚本生成SQL
  • 前面提到的mysql脚本

请查看博客文章了解更多详情。它还包含Roland的sql脚本的改进版本,该脚本将自动计算给定列的日期范围并将其与日期维联接。