在mysql中创建日期范围

时间:2010-01-27 19:27:14

标签: mysql datetime

动态创建的最佳方式,日期范围,用于报告。

因此,如果某一天没有任何活动,我可以避免报告中的空行。

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

4 个答案:

答案 0 :(得分:9)

我的建议是:不要让你的生活更加艰难,让事情变得更轻松。只需为每个日历日创建一个包含一行的表,其中包含您认为合理需要持续的行数。在数据仓库中,这是常见的解决方案,并且它以如此广泛的方式实现,使得没有它的dwh具有代码气味。

许多人习惯于处理更传统的oltp /数据输入应用感觉自然会厌恶这种想法,因为感觉无论如何都可以生成数据,因此不应该存储。但是如果你创建了这样的表,你可以用许多有用的属性来装饰它,比如它是一个假期还是周末,你可以在里面存储许多常见的日期表示(iso,europe,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

答案 1 :(得分: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脚本的改进版本,它将自动计算给定列的日期范围并将其与日期维度连接。

答案 2 :(得分:1)

在MySQL中没有直接的方法可以做到这一点。最好的办法是用您选择的服务器端语言生成一个日期范围数组,然后从数据库中提取数据,并使用日期作为键将结果数组与您的日期范围数组合并。

您使用的是哪种服务器端语言?

编辑:

基本上你要做的是(伪代码):

// 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);

}

您最终会得到一个可以循环的日期数组,其中包含与之关联的活动数据的日期。

可以轻松修改以按小时分组/过滤数据。

答案 3 :(得分: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构成范围的端点,并作为参数提供给例程。