Создание диапазона дат в mysql

Лучший способ создания на лету диапазонов дат для использования с отчетом.

Поэтому я могу избежать пустых строк в моем отчете, если нет активности за данный день.

В основном, чтобы избежать этой проблемы: Какой самый простой способ заполнить пустые даты в sql-результатах (на mysql или perl-конце)?

Ответы

Ответ 1

В MySQL нет простого способа сделать это. Лучше всего создать массив daterange на выбранном вами серверном языке, а затем извлечь данные из базы данных и объединить полученный массив с вашим массивом daterange, используя дату в качестве ключа.

Какой серверный язык вы используете?

Edit:

В основном, что вы будете делать (псевдокод):

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

}

В итоге вы получите массив дат, которые вы можете прокручивать, с датами, которые имеют или не имеют связанных с ними данных о деятельности.

Может быть легко изменен для группировки/фильтрации данных по часам.

Ответ 2

Мой совет: не делайте свою жизнь труднее, облегчите ее. Просто создайте таблицу с одной строкой за каждый календарный день, имея столько строк, сколько вы думаете, что вам разумно нужно продлиться. В datawarehousing это общее решение, и оно настолько широко реализовано таким образом, что dwh, у которого его нет, имеет запах кода.

Многие люди привыкли иметь дело с более традиционными приложениями для создания приложений oltp/data, которые испытывают естественное отвращение к этой идее, потому что ощущение, что они могут генерировать данные в любом случае, и поэтому их не следует хранить. Но если вы создадите такую ​​таблицу, вы можете украсить ее множеством полезных атрибутов, например, будь то holdiday или weekend, и вы можете хранить в ней множество общих представлений даты (iso, european, us format и т.д.), Которые может сэкономить вам массу времени при создании отчетов (так как вам не нужно беспокоиться о том, как работает форматирование даты в каждом инструменте создания отчетов. Или вы можете сделать еще один шаг и ежедневно обновлять таблицу дат, чтобы отметить флаги на текущий день, текущую неделю, текущий месяц, текущий год и т.д. - всевозможные полезные инструменты, которые делают это намного проще, гораздо проще создавать отчеты, которые должны работать против некоторого диапазона дат.

Пример кода 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.html а также http://rpbouman.blogspot.com/2010/01/easter-eggs-for-mysql-and-kettle.html

Ответ 3

Недавно я провел некоторое исследование, чтобы найти и оценить возможные варианты. http://www.freeportmetrics.com/devblog/2012/11/02/how-to-quickly-add-date-dimension-to-pentaho-mondrian-olap-cube/.

Вы можете использовать:

  • чайник
  • вырожденные размеры
  • встроенная функция lucidb
  • встроенная функция Mondrian
  • собственный script для создания SQL
  • mysql script, упомянутый ранее

Пожалуйста, проверьте сообщение в блоге для получения более подробной информации. Он также содержит улучшенную версию Roland sql script, которая автоматически рассчитает диапазон дат для данного столбца и присоединит его к размеру даты.

Ответ 4

Попробуйте использовать цикл в хранимой процедуре 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;

startDate и endDate составляют конечные точки диапазона и поставляются в качестве параметров для подпрограммы.