23 ответов:
это решение использует нет циклов, процедур или временных таблиц. Подзапрос генерирует даты за последнюю тысячу дней, и может быть продлен, чтобы идти так далеко назад или вперед, как вы хотите.
select a.Date from ( select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c ) a where a.Date between '2010-01-20' and '2010-01-24'выход:
Date ---------- 2010-01-24 2010-01-23 2010-01-22 2010-01-21 2010-01-20примечания по производительности
тестирование здесь, производительность удивительно хороша:выше запрос занимает 0.0009 сек.
Если мы расширим подзапрос для создания ок. 100 000 номеров (и, таким образом, около 274 лет стоит дат), он работает в 0.0458 сек.
кстати, это очень портативный метод, который работает с большинством баз данных с незначительными коррективами.
вот еще один вариант представления:
CREATE VIEW digits AS SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9; CREATE VIEW numbers AS SELECT ones.digit + tens.digit * 10 + hundreds.digit * 100 + thousands.digit * 1000 AS number FROM digits as ones, digits as tens, digits as hundreds, digits as thousands; CREATE VIEW dates AS SELECT SUBDATE(CURRENT_DATE(), number) AS date FROM numbers;и тогда вы можете просто сделать (видите, как это элегантно?):
SELECT date FROM dates WHERE date BETWEEN '2010-01-20' AND '2010-01-24' ORDER BY dateобновление
стоит отметить, что вы сможете создать прошлые даты, начиная с текущей даты. Если вы хотите создать какой-либо диапазон дат (прошлое, будущее, и между ними), вы должны использовать это вместо:
CREATE VIEW dates AS SELECT SUBDATE(CURRENT_DATE(), number) AS date FROM numbers UNION ALL SELECT ADDDATE(CURRENT_DATE(), number + 1) AS date FROM numbers;
принятый ответ не работал для PostgreSQL (синтаксическая ошибка В или около "a").
способ, которым вы делаете это в PostgreSQL, заключается в использовании
generate_series
используя рекурсивное общее табличное выражение (CTE), вы можете создать список дат, а затем выбрать из него. Очевидно, что вы обычно не хотите создавать три миллиона дат, так что это просто иллюстрирует возможности. Вы можете просто ограничить диапазон дат внутри CTE и опустить предложение where из инструкции select с помощью CTE.
with [dates] as ( select convert(datetime, '1753-01-01') as [date] --start union all select dateadd(day, 1, [date]) from [dates] where [date] < '9999-12-31' --end ) select [date] from [dates] where [date] between '2013-01-01' and '2013-12-31' option (maxrecursion 0)в Microsoft SQL Server 2005 генерация списка CTE всех возможных дат заняла 1: 08. Создание ста лет заняло меньше чем на секунду.
MSSQL Query
select datetable.Date from ( select DATEADD(day,-(a.a + (10 * b.a) + (100 * c.a)),getdate()) AS Date from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c ) datetable where datetable.Date between '2014-01-20' and '2014-01-24' order by datetable.Date DESCвыход
Date ----- 2014-01-23 12:35:25.250 2014-01-22 12:35:25.250 2014-01-21 12:35:25.250 2014-01-20 12:35:25.250
старое школьное решение для этого без цикла / курсора-создать
NUMBERSтаблица, которая имеет один целочисленный столбец со значениями, начинающимися с 1.CREATE TABLE `example`.`numbers` ( `id` int(10) unsigned NOT NULL auto_increment, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;вам нужно заполнить таблицу с достаточно записей, чтобы покрыть ваши потребности:
INSERT INTO NUMBERS (id) VALUES (NULL);после
NUMBERSтаблица, вы можете использовать:SELECT x.start_date + INTERVAL n.id-1 DAY FROM NUMBERS n JOIN (SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d') AS start_date FROM DUAL) x WHERE x.start_date + INTERVAL n.id-1 DAY <= '2010-01-24'абсолютное низкотехнологичное решение было бы:
SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d') FROM DUAL UNION ALL SELECT STR_TO_DATE('2010-01-21', '%Y-%m-%d') FROM DUAL UNION ALL SELECT STR_TO_DATE('2010-01-22', '%Y-%m-%d') FROM DUAL UNION ALL SELECT STR_TO_DATE('2010-01-23', '%Y-%m-%d') FROM DUAL UNION ALL SELECT STR_TO_DATE('2010-01-24', '%Y-%m-%d') FROM DUALдля чего бы вы его использовали?
To сгенерируйте списки дат или чисел, чтобы левое соединение было включено. Вы бы это сделали, чтобы увидеть, где есть пробелы в данных, потому что вы оставляете присоединение к списку последовательных данных - нулевые значения сделают очевидным, где существуют пробелы.
Для Access 2010 - требуется несколько шагов; я следовал той же схеме, что и выше, но думал, что могу помочь кому-то в доступе. Работал отлично для меня, мне не нужно было держать посевную таблицу дат.
создайте таблицу с именем DUAL (аналогично тому, как работает Oracle DUAL table)
- ID (AutoNumber)
- DummyColumn (Текст)
- добавить значения одной строки (1,"DummyRow")
создать запрос с именем "ZeroThru9Q"; вручную введите следующий синтаксис:
SELECT 0 AS a FROM dual UNION ALL SELECT 1 FROM dual UNION ALL SELECT 2 FROM dual UNION ALL SELECT 3 FROM dual UNION ALL SELECT 4 FROM dual UNION ALL SELECT 5 FROM dual UNION ALL SELECT 6 FROM dual UNION ALL SELECT 7 FROM dual UNION ALL SELECT 8 FROM dual UNION ALL SELECT 9 FROM dual;создайте запрос с именем " TodayMinus1KQ "(для дат до сегодняшнего дня); вручную введите следующий синтаксис:
SELECT date() - (a.a + (10 * b.a) + (100 * c.a)) AS MyDate FROM (SELECT * FROM ZeroThru9Q) AS a, (SELECT * FROM ZeroThru9Q) AS b, (SELECT * FROM ZeroThru9Q) AS cсоздайте запрос с именем " TodayPlus1KQ "(для дат после сегодняшнего дня); вручную введите следующий синтаксис:
SELECT date() + (a.a + (10 * b.a) + (100 * c.a)) AS MyDate FROM (SELECT * FROM ZeroThru9Q) AS a, (SELECT * FROM ZeroThru9Q) AS b, (SELECT * FROM ZeroThru9Q) AS c;создайте запрос объединения с именем "TodayPlusMinus1KQ" (для дат + / - 1000 дней):
SELECT MyDate FROM TodayMinus1KQ UNION SELECT MyDate FROM TodayPlus1KQ;теперь вы можете использовать этот запрос:
SELECT MyDate FROM TodayPlusMinus1KQ WHERE MyDate BETWEEN #05/01/2014# and #05/30/2014#
thx Pentium10-вы заставили меня присоединиться к stackoverflow :) - это мой перенос в msaccess - думаю, что он будет работать на любой версии:
SELECT date_value FROM (SELECT a.espr1+(10*b.espr1)+(100*c.espr1) AS integer_value, dateadd("d",integer_value,dateserial([start_year], [start_month], [start_day])) as date_value FROM (select * from ( select top 1 "0" as espr1 from MSysObjects union all select top 1 "1" as espr2 from MSysObjects union all select top 1 "2" as espr3 from MSysObjects union all select top 1 "3" as espr4 from MSysObjects union all select top 1 "4" as espr5 from MSysObjects union all select top 1 "5" as espr6 from MSysObjects union all select top 1 "6" as espr7 from MSysObjects union all select top 1 "7" as espr8 from MSysObjects union all select top 1 "8" as espr9 from MSysObjects union all select top 1 "9" as espr9 from MSysObjects ) as a, ( select top 1 "0" as espr1 from MSysObjects union all select top 1 "1" as espr2 from MSysObjects union all select top 1 "2" as espr3 from MSysObjects union all select top 1 "3" as espr4 from MSysObjects union all select top 1 "4" as espr5 from MSysObjects union all select top 1 "5" as espr6 from MSysObjects union all select top 1 "6" as espr7 from MSysObjects union all select top 1 "7" as espr8 from MSysObjects union all select top 1 "8" as espr9 from MSysObjects union all select top 1 "9" as espr9 from MSysObjects ) as b, ( select top 1 "0" as espr1 from MSysObjects union all select top 1 "1" as espr2 from MSysObjects union all select top 1 "2" as espr3 from MSysObjects union all select top 1 "3" as espr4 from MSysObjects union all select top 1 "4" as espr5 from MSysObjects union all select top 1 "5" as espr6 from MSysObjects union all select top 1 "6" as espr7 from MSysObjects union all select top 1 "7" as espr8 from MSysObjects union all select top 1 "8" as espr9 from MSysObjects union all select top 1 "9" as espr9 from MSysObjects ) as c ) as d) WHERE date_value between dateserial([start_year], [start_month], [start_day]) and dateserial([end_year], [end_month], [end_day]);ссылки на MSysObjects просто потому, что для доступа нужна таблица, насчитывающая не менее 1 записи, в предложении from - любая таблица с не менее чем 1 записью.
процедура + временная таблица:
DELIMITER $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `days`(IN dateStart DATE, IN dateEnd DATE) BEGIN CREATE TEMPORARY TABLE IF NOT EXISTS date_range (day DATE); WHILE dateStart <= dateEnd DO INSERT INTO date_range VALUES (dateStart); SET dateStart = DATE_ADD(dateStart, INTERVAL 1 DAY); END WHILE; SELECT * FROM date_range; DROP TEMPORARY TABLE IF EXISTS date_range; END
попробуйте это.
SELECT TO_DATE('20160210','yyyymmdd') - 1 + LEVEL AS start_day from DUAL connect by level <= (TO_DATE('20160228','yyyymmdd') + 1) - TO_DATE('20160210','yyyymmdd') ;
Если вам когда-нибудь понадобится больше, чем пару дней, вам нужна таблица.
затем,
select from days.day, count(mytable.field) as fields from days left join mytable on day=date where date between x and y;
как указано (или, по крайней мере, упоминалось) во многих замечательных ответах, уже приведенных, эта проблема легко решается, как только у вас есть набор чисел для работы.
Примечание: ниже приведен T-SQL, но это просто моя конкретная реализация общих концепций, уже упомянутых здесь и в интернете в целом. Это должно быть относительно просто преобразовать код в ваш диалект выбора.
как? рассмотреть этот запрос:
SELECT DATEADD(d, N, '0001-01-22') FROM Numbers -- A table containing the numbers 0 through N WHERE N <= 5;вышеизложенное создает диапазон дат 1/22/0001-1/27/0001 и является чрезвычайно тривиальным. В приведенном выше запросе есть 2 ключевых элемента информации:дата начала на
0001-01-22и смещение на5. Если мы объединим эти две части информации, то у нас, очевидно, есть наша конечная дата. Таким образом, учитывая две даты, генерация диапазона может быть разбита следующим образом:
найти разницу между двумя заданными даты (смещение), легко:
-- Returns 125 SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))используя
ABS()здесь гарантирует, что порядок дат не имеет значения.создать ограниченный набор чисел, также легко:
-- Returns the numbers 0-2 SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1 FROM(SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A')обратите внимание, что мы на самом деле не волнует, что мы выбираем
FROMздесь. Нам просто нужен набор для работы, чтобы мы подсчитали количество строк в нем. Я лично использую TVF, некоторые используют CTE, другие используют таблицу чисел вместо этого, вы получаете идея. Я выступаю за использование наиболее эффективного решения, которое вы также понимаете.объединение этих двух методов решит нашу проблему:
DECLARE @date1 DATE = '9001-11-21'; DECLARE @date2 DATE = '9001-11-23'; SELECT D = DATEADD(d, N, @date1) FROM ( SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1 FROM (SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A') S ) Numbers WHERE N <= ABS(DATEDIFF(d, @date1, @date2));приведенный выше пример ужасный код, но демонстрирует, как все приходит вместе.
Больше Удовольствия
мне нужно много делать такого рода вещи, поэтому я инкапсулировал логику в два TVFs. Первый генерирует диапазон чисел и второй использует эту функцию для создания диапазона дат. Математика заключается в том, чтобы гарантировать, что порядок ввода не имеет значения, и потому что я хотел использовать полный спектр чисел, доступных в
GenerateRangeSmallInt.следующая функция занимает ~16 мс процессорного времени, чтобы вернуть максимальный диапазон 65536 дат.
CREATE FUNCTION dbo.GenerateRangeDate ( @date1 DATE, @date2 DATE ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( SELECT D = DATEADD(d, N + 32768, CASE WHEN @date1 <= @date2 THEN @date1 ELSE @date2 END) FROM dbo.GenerateRangeSmallInt(-32768, ABS(DATEDIFF(d, @date1, @date2)) - 32768) ); GO CREATE FUNCTION dbo.GenerateRangeSmallInt ( @num1 SMALLINT = -32768 , @num2 SMALLINT = 32767 ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( WITH Numbers(N) AS ( SELECT N FROM(VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 16 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 32 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 48 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 64 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 80 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 96 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 112 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 128 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 144 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 160 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 176 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 192 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 208 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 224 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 240 , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 256 ) V (N) ) SELECT TOP(ABS(CAST(@num1 AS INT) - CAST(@num2 AS INT)) + 1) N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + CASE WHEN @num1 <= @num2 THEN @num1 ELSE @num2 END - 1 FROM Numbers A , Numbers B );
генерировать даты между двумя полями даты
если вы знаете с запросом SQL CTE, то это решение поможет вам решить Ваш вопрос
вот пример
у нас есть даты в одной таблице
имя таблицы: "testdate"
STARTDATE ENDDATE 10/24/2012 10/24/2012 10/27/2012 10/29/2012 10/30/2012 10/30/2012Требуют Результат:
STARTDATE 10/24/2012 10/27/2012 10/28/2012 10/29/2012 10/30/2012устранение:
WITH CTE AS (SELECT DISTINCT convert(varchar(10),StartTime, 101) AS StartTime, datediff(dd,StartTime, endTime) AS diff FROM dbo.testdate UNION ALL SELECT StartTime, diff - 1 AS diff FROM CTE WHERE diff<> 0) SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime FROM CTEпояснение: рекурсивный запрос CTE объяснение
первая часть запрос:
SELECT DISTINCT convert(varchar(10), StartTime, 101) AS StartTime, datediff(dd, StartTime, endTime) AS diff FROM dbo.testdate
Пояснение: firstcolumn-это "startdate", второй столбец-разница начала и конца дата в днях и она будет рассматриваться как столбец "diff"вторая часть запрос:
UNION ALL SELECT StartTime, diff-1 AS diff FROM CTE WHERE diff<>0
Пояснение: Union all будет наследовать результат вышеуказанного запроса, пока результат не станет нулевым, Так Результат "StartTime" наследуется от сгенерированного запроса CTE, а от diff, decrease - 1, поэтому он выглядит как 3, 2 и 1 до 0STARTDATE DIFF 10/24/2012 0 10/27/2012 0 10/27/2012 1 10/27/2012 2 10/30/2012 0Результат Спецификация
STARTDATE Specification 10/24/2012 --> From Record 1 10/27/2012 --> From Record 2 10/27/2012 --> From Record 2 10/27/2012 --> From Record 2 10/30/2012 --> From Record 3
3-я часть запроса
SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime FROM CTE
Он добавит день "diff" в "startdate", поэтому результат должен быть как показано нижерезультат
STARTDATE 10/24/2012 10/27/2012 10/28/2012 10/29/2012 10/30/2012
короче принятого ответа, та же идея:
(SELECT TRIM('2016-01-05' + INTERVAL a + b DAY) date FROM (SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 ) d, (SELECT 0 b UNION SELECT 10 UNION SELECT 20 UNION SELECT 30 UNION SELECT 40) m WHERE '2016-01-05' + INTERVAL a + b DAY <= '2016-01-21')
для тех, кто хочет это как сохраненное представление (MySQL не поддерживает вложенные операторы select в представлениях):
create view zero_to_nine as select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9; create view date_range as select curdate() - INTERVAL (a.n + (10 * b.n) + (100 * c.n)) DAY as date from zero_to_nine as a cross join zero_to_nine as b cross join zero_to_nine as c;вы можете сделать
select * from date_rangeи
date --- 2017-06-06 2017-06-05 2017-06-04 2017-06-03 2017-06-02 ...
Это хорошая идея с созданием этих дат на лету. Однако я не чувствую себя комфортно, чтобы сделать это с довольно большим диапазоном, поэтому я получил следующее решение:
- создана таблица "DatesNumbers", которая будет содержать числа, используемые для расчета дат:
CREATE TABLE DatesNumbers ( i MEDIUMINT NOT NULL, PRIMARY KEY (i) ) COMMENT='Used by Dates view' ;
- заполняется таблица с использованием вышеуказанных методов с номерами от -59999 до 40000. Этот диапазон даст мне даты от 59999 дней (~164 лет) за 40000 дней (109 лет) вперед:
INSERT INTO DatesNumbers SELECT a.i + (10 * b.i) + (100 * c.i) + (1000 * d.i) + (10000 * e.i) - 59999 AS i FROM (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a , (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b , (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS c , (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS d , (SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS e ;
- создано представление "даты":
SELECT i , CURRENT_DATE() + INTERVAL i DAY AS Date FROM DatesNumbersвот именно.
- (+) легко читать запросы
- (+) нет на лету числа поколений
- (+) дает даты в прошлом и в будущем, и для этого нет союза, как в этот пост.
- (+) "только в прошлом" или " только в будущем" сроки могут быть отфильтрованы с помощью
WHERE i < 0илиWHERE i > 0(PK)- ( -) используется временная таблица и представление
хорошо.. Попробовать это: http://www.devshed.com/c/a/MySQL/Delving-Deeper-into-MySQL-50/
http://dev.mysql.com/doc/refman/5.0/en/loop-statement.html
http://www.roseindia.net/sql/mysql-example/mysql-loop.shtmlиспользуйте это, скажем, для создания временной таблицы, а затем выполните select * на временной таблице. Или выводите результаты по одному за раз.
то, что вы говорите, что хотите сделать, не может быть сделано с помощью SELECT заявление, но это может быть выполнимо с вещами, специфичными для MySQL.
Опять же, может быть, вам нужны курсоры:http://dev.mysql.com/doc/refman/5.0/en/cursors.html
для Oracle, мое решение такое:
select trunc(sysdate-dayincrement, 'DD') from dual, (select level as dayincrement from dual connect by level <= 30)Sysdate может быть изменен на конкретную дату и номер уровня может быть изменен, чтобы дать больше дат.
Если вы хотите получить список дат между двумя датами:
create table #dates ([date] smalldatetime) while @since < @to begin insert into #dates(dateadd(day,1,@since)) set @since = dateadd(day,1,@since) end select [date] from #dates*скрипка здесь: http://sqlfiddle.com/#!6/9eecb/3469
set language 'SPANISH' DECLARE @table table(fechaDesde datetime , fechaHasta datetime ) INSERT @table VALUES('20151231' , '20161231'); WITH x AS ( SELECT DATEADD( m , 1 ,fechaDesde ) as fecha FROM @table UNION ALL SELECT DATEADD( m , 1 ,fecha ) FROM @table t INNER JOIN x ON DATEADD( m , 1 ,x.fecha ) <= t.fechaHasta ) SELECT LEFT( CONVERT( VARCHAR, fecha , 112 ) , 6 ) as Periodo_Id ,DATEPART ( dd, DATEADD(dd,-(DAY(fecha)-1),fecha)) Num_Dia_Inicio ,DATEADD(dd,-(DAY(fecha)-1),fecha) Fecha_Inicio ,DATEPART ( mm , fecha ) Mes_Id ,DATEPART ( yy , fecha ) Anio ,DATEPART ( dd, DATEADD(dd,-(DAY(DATEADD(mm,1,fecha))),DATEADD(mm,1,fecha))) Num_Dia_Fin ,DATEADD(dd,-(DAY(DATEADD(mm,1,fecha))),DATEADD(mm,1,fecha)) ultimoDia ,datename(MONTH, fecha) mes ,'Q' + convert(varchar(10), DATEPART(QUARTER, fecha)) Trimestre_Name FROM x OPTION(MAXRECURSION 0)
DELIMITER $$ CREATE PROCEDURE GenerateRangeDates(IN dateStart DATE, IN dateEnd DATE) BEGIN CREATE TEMPORARY TABLE IF NOT EXISTS dates (day DATE); loopDate: LOOP INSERT INTO dates(day) VALUES (dateStart); SET dateStart = DATE_ADD(dateStart, INTERVAL 1 DAY); IF dateStart <= dateEnd THEN ITERATE loopDate; ELSE LEAVE loopDate; END IF; END LOOP loopDate; SELECT day FROM dates; DROP TEMPORARY TABLE IF EXISTS dates; END $$ -- Call procedure call GenerateRangeDates( now() - INTERVAL 40 DAY, now() );
SQLite версия RedFilters top solution
select d.Date from ( select date(julianday('2010-01-20') + (a.a + (10 * b.a) + (100 * c.a))) as Date from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c ) d where d.Date between '2010-01-20' and '2010-01-24' order by d.Date
улучшено с будним днем присоединение к пользовательскому праздничному столу microsoft MSSQL 2012 для таблицы дат powerpivot https://gist.github.com/josy1024/cb1487d66d9e0ccbd420bc4a23b6e90e
with [dates] as ( select convert(datetime, '2016-01-01') as [date] --start union all select dateadd(day, 1, [date]) from [dates] where [date] < '2018-01-01' --end ) select [date] , DATEPART (dw,[date]) as Wochentag , (select holidayname from holidaytable where holidaytable.hdate = [date]) as Feiertag from [dates] where [date] between '2016-01-01' and '2016-31-12' option (maxrecursion 0)
Comments