Создать диапазон дат в MySQL - PullRequest
       20

Создать диапазон дат в MySQL

3 голосов
/ 27 января 2010

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

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

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

Ответы [ 4 ]

9 голосов
/ 27 января 2010

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

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

Пример кода 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

2 голосов
/ 05 ноября 2012

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

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

  • чайник
  • вырожденные размеры
  • Встроенная функция lucidb
  • Предстоящая встроенная функция Мондриана
  • ваш собственный скрипт для генерации SQL
  • MySQL скрипт, упомянутый ранее

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

1 голос
/ 27 января 2010

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

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

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

}

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

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

0 голосов
/ 28 марта 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...