Создать диапазон дат в MySQL
Лучший способ создания на лету диапазонов дат, для использования с отчетом.
Таким образом, я могу избежать пустых строк в моем отчете, если в данный день нет активности.
Главным образом, чтобы избежать этой проблемы: Каков самый простой способ дополнить пустые даты в результатах sql (либо в mysql, либо в perl end)?
5 ответов
В 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);
}
В итоге вы получите массив дат, который вы можете просмотреть, с датами, которые имеют или не имеют данных об активности, связанных с ними.
Может быть легко изменен для группировки / фильтрации данных по часам.
Мой совет: не усложняйте свою жизнь, сделайте ее легче. Просто создайте таблицу с одной строкой для каждого календарного дня, имеющую столько строк, сколько вы считаете нужным продлить. В хранилище данных это общее решение, и оно настолько широко реализовано, что у 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
Недавно я провел некоторое исследование, чтобы найти и оценить возможные варианты. http://www.freeportmetrics.com/devblog/2012/11/02/how-to-quickly-add-date-dimension-to-pentaho-mondrian-olap-cube/.
Ты можешь использовать:
- чайник
- вырожденные размеры
- встроенная функция lucidb
- Предстоящая встроенная функция Мондриана
- ваш собственный скрипт для генерации SQL
- MySQL скрипт, упомянутый ранее
Пожалуйста, проверьте сообщение в блоге для более подробной информации. Он также содержит улучшенную версию sql-скрипта Роланда, который автоматически вычислит диапазон дат для данного столбца и объединит его с измерением даты.
Я понимаю, что это старый пост, но, чтобы держать Stack Overflow немного в курсе, я чувствую необходимость ответить.
С новым движком SEQUENCE в MariaDB это возможно за считанные минуты.SELECT
оператор без какой-либо сохраненной процедуры или временной таблицы:
SELECT
DATE_ADD(
CAST('2022-06-01' AS DATE),
INTERVAL `s1`.`seq` DAY
) AS `dates`
FROM `seq_0_to_364` AS `s1`;
Любой интервал будет работать, пока он находится в пределахBIGINT(20) UNSIGNED
, так как это предел движка SEQUENCE.
Попробуйте использовать цикл в хранимой подпрограмме 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
составляют конечные точки диапазона и предоставляются в качестве параметров для процедуры.