Нет другой проблемы, связанной с этим вопросом. Выше вопрос является проблемой, освоив курсы SQL.
Pentium10
Вам просто нужен массив дат на основе выбранного диапазона дат?
Дерек Адэйр
1
Я думаю об использовании, чтобы найти вам проблему ... Если у вас есть задача, чтобы заполнить некоторые пропущенные записи в вашей таблице. И вы должны выполнить запрос для каждого дня, я думаю, что-то вродеinsert into table select ... as days date between '' and ''
Pentium10
13
Примером его использования может быть создание статистики и включение строки для дат, по которым у вас нет данных. Если вы выполняете какое-то группирование, может быть гораздо быстрее на самом деле сгенерировать всю информацию в SQL и добавить ее в любой необходимый вам формат, вместо того, чтобы выкидывать данные как есть на ваш язык, и начинать цикл и добавлять свои пустеет.
Нанн
1
@ Нэнн, именно поэтому я сохранил этот вопрос. Мне нужно выше, чтобы присоединиться к данным, которые могут не существовать на определенные даты.
Джош Дил
Ответы:
318
В этом решении не используются циклы, процедуры или временные таблицы . Подзапрос генерирует даты за последние 10 000 дней и может быть расширен, чтобы перейти так далеко назад или вперед, как вы хотите.
select a.Date from(select curdate()- INTERVAL (a.a +(10* b.a)+(100* c.a)+(1000* d.a)) DAY as Datefrom(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as acrossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as bcrossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as ccrossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as d) awhere a.Date between'2010-01-20'and'2010-01-24'
Вы увидите лучшую производительность, если вы перейдете UNIONна UNION ALL- это тратит время на проверку дубликатов для удаления, которые не существуют. Хотя это слишком сложный IMO - если вы собираетесь создавать набор результатов с помощью UNION, почему бы просто не указать дату и покончить с этим?
OMG Ponies
7
почему бы просто не указать дату и покончить с ней - потому что вышеописанный метод позволяет вам создавать произвольно большие наборы чисел (и дат), не требующие создания таблицы, что было бы больно жестко кодировать, как вы предлагаете. Очевидно, для 5 дат это излишне; но даже в этом случае, если вы объединяетесь с таблицей, в которой вы заранее не знаете даты, а только потенциальные минимальные и максимальные значения, это имеет смысл.
RedFilter
2
«Больно» просто использовать функцию DATETIME вместо оператора UNION, который вы уже создали? Это избавляет вас от необходимости добавлять логику . Следовательно - вы слишком усложнили запрос. Оператор UNION, в любом случае, не масштабируется - указывается дата или число, кто хочет обновить его, чтобы вместить, скажем, 20 или 30 дат?
OMG Ponies
23
Очень приятно видеть ответ на вопрос, а не бесконечные комментарии о том, как это невозможно или не следует делать. Большинство вещей можно сделать, и «должен» имеет смысл только в контексте, который отличается для всех. Этот ответ помог мне, хотя я хорошо понимаю, что в большинстве ситуаций есть лучшие способы.
Джо
7
Те из вас, кто не может заставить этот запрос работать: пожалуйста, дайте себе пощечину, а затем перечитайте комментарий ОП об этом запросе, генерирующем 1000 дат. Поскольку 2010 год был более 1000 дней назад, вам необходимо соответствующим образом изменить запрос.
Ноэль Барон
32
Вот еще один вариант с использованием представлений:
CREATEVIEW digits ASSELECT0AS digit UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9;CREATEVIEW numbers ASSELECT
ones.digit + tens.digit *10+ hundreds.digit *100+ thousands.digit *1000AS numberFROM
digits as ones,
digits as tens,
digits as hundreds,
digits as thousands;CREATEVIEW dates ASSELECT
SUBDATE(CURRENT_DATE(), number)AS dateFROM
numbers;
И тогда вы можете просто сделать (посмотреть, как это элегантно?):
SELECT
dateFROM
datesWHERE
date BETWEEN'2010-01-20'AND'2010-01-24'ORDERBY
date
Обновить
Стоит отметить, что вы сможете генерировать только прошлые даты, начиная с текущей даты . Если вы хотите сгенерировать любой диапазон дат (прошлые, будущие и промежуточные), вам придется использовать это представление:
Это не работает во всех случаях. ВЫБЕРИТЕ дату ОТ даты ГДЕ дата МЕЖДУ «2014-12-01» И «2014-12-28» ПОРЯДОК ПО
ДАТЕ
3
Хороший звонок @ user927258. Это связано с тем, что в первом представлении, datesупомянутом выше, вычисляются даты, начиная с текущей даты, поэтому вы не сможете получить даты, установленные в будущем. Ответ от @RedFilter страдает тем же недостатком дизайна. Я добавил обходной путь в моем ответе все же.
Стефан
Использование некоторых представлений определенно упрощает запросы и делает их многократно используемыми. Хотя они по сути делают одно и то же, все эти UNIONпредложения выглядят странно в одном операторе SQL.
Стюарт
24
Принятый ответ не работает для PostgreSQL (синтаксическая ошибка в или около "a").
Вы делаете это в PostgreSQL с помощью generate_seriesфункции, то есть:
SELECT day::date
FROM generate_series('2010-01-20','2010-01-24', INTERVAL '1 day') day;
day
------------2010-01-202010-01-212010-01-222010-01-232010-01-24(5rows)
Используя рекурсивное выражение общих таблиц (CTE), вы можете создать список дат, а затем выбрать его. Очевидно, что вы обычно не хотите создавать три миллиона дат, так что это просто иллюстрирует возможности. Вы можете просто ограничить диапазон дат внутри CTE и опустить предложение where в операторе select, используя CTE.
select datetable.Date
from(select DATEADD(day,-(a.a +(10* b.a)+(100* c.a)),getdate())AS Date
from(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as a
crossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as b
crossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as c
) datetable
where datetable.Date between'2014-01-20'and'2014-01-24'orderby datetable.Date DESC
Вывод
Date
-----2014-01-2312:35:25.2502014-01-2212:35:25.2502014-01-2112:35:25.2502014-01-2012:35:25.250
Если бы я только прокрутил немного больше ... вздох. В любом случае, спасибо. Я добавил CAST (<выражение> AS DATE), чтобы удалить время в моей версии. Также используется, где a. Дата между GETDATE () - 365 и GETDATE () ... если вы выполните свой запрос сегодня, он не даст никаких строк, если вы не заметите даты в WHERE = P
Рикардо C
4
Старое школьное решение для этого без цикла / курсора состоит в том, чтобы создать NUMBERSтаблицу, которая имеет единственный столбец Integer со значениями, начинающимися с 1.
Вам нужно заполнить таблицу достаточным количеством записей, чтобы удовлетворить ваши потребности:
INSERTINTO 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'
Сформировать списки дат или чисел для того, чтобы присоединиться к. Вы должны сделать это, чтобы увидеть, где есть пропуски в данных, потому что вы ЛЕВЫЕ ПРИСОЕДИНЯЕТЕСЬ к списку последовательных данных - нулевые значения сделают очевидным, где существуют пропуски.
DUALТаблица поддерживается Oracle и MySQL для использования в качестве дублера в таблице в FROMп. Он не существует, выбор значений из него вернет любое значение. Идея состояла в том, чтобы иметь замену, потому что запрос SELECT требует FROMпредложения, определяющего по крайней мере одну таблицу.
OMG Ponies
1
+1 за создание таблицы постоянных чисел вместо того, чтобы СУБД собирала ее каждый раз, когда вам нужен запрос. Вспомогательные столы не зло, люди!
Бекон Бит
4
Для Access 2010 - требуется несколько шагов; Я следовал той же схеме, что и выше, но думал, что смогу помочь кому-то в Access. Отлично сработало для меня, мне не нужно было хранить таблицу с датами.
Создайте таблицу с именем DUAL (аналогично тому, как работает таблица Oracle DUAL)
ID (AutoNumber)
DummyColumn (Text)
Добавить значения одной строки (1, «DummyRow»)
Создайте запрос с именем «ZeroThru9Q»; вручную введите следующий синтаксис:
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(selecttop1"0"as espr1 from MSysObjects
unionallselecttop1"1"as espr2 from MSysObjects
unionallselecttop1"2"as espr3 from MSysObjects
unionallselecttop1"3"as espr4 from MSysObjects
unionallselecttop1"4"as espr5 from MSysObjects
unionallselecttop1"5"as espr6 from MSysObjects
unionallselecttop1"6"as espr7 from MSysObjects
unionallselecttop1"7"as espr8 from MSysObjects
unionallselecttop1"8"as espr9 from MSysObjects
unionallselecttop1"9"as espr9 from MSysObjects
)as a,(selecttop1"0"as espr1 from MSysObjects
unionallselecttop1"1"as espr2 from MSysObjects
unionallselecttop1"2"as espr3 from MSysObjects
unionallselecttop1"3"as espr4 from MSysObjects
unionallselecttop1"4"as espr5 from MSysObjects
unionallselecttop1"5"as espr6 from MSysObjects
unionallselecttop1"6"as espr7 from MSysObjects
unionallselecttop1"7"as espr8 from MSysObjects
unionallselecttop1"8"as espr9 from MSysObjects
unionallselecttop1"9"as espr9 from MSysObjects
)as b,(selecttop1"0"as espr1 from MSysObjects
unionallselecttop1"1"as espr2 from MSysObjects
unionallselecttop1"2"as espr3 from MSysObjects
unionallselecttop1"3"as espr4 from MSysObjects
unionallselecttop1"4"as espr5 from MSysObjects
unionallselecttop1"5"as espr6 from MSysObjects
unionallselecttop1"6"as espr7 from MSysObjects
unionallselecttop1"7"as espr8 from MSysObjects
unionallselecttop1"8"as espr9 from MSysObjects
unionallselecttop1"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 записью.
Как указывалось (или, по крайней мере, упоминалось) во многих замечательных ответах, которые уже даны, эта проблема легко решается, когда у вас есть набор чисел для работы.
Примечание: ниже приведен T-SQL, но это просто моя конкретная реализация общих концепций, уже упомянутых здесь и в Интернете в целом. Преобразование кода на ваш диалект должен быть относительно простым.
Как? Рассмотрим этот запрос:
SELECT DATEADD(d, N,'0001-01-22')FROM Numbers -- A table containing the numbers 0 through NWHERE N <=5;
Выше приведен диапазон дат 1/22/0001 - 1/27/0001 и является чрезвычайно тривиальным. Есть 2 ключевых элементов информации в приведенном выше запросе: дата начала из 0001-01-22и смещения в 5. Если мы объединим эти две части информации, то у нас, очевидно, будет дата окончания. Таким образом, с учетом двух дат генерация диапазона может быть разбита следующим образом:
Найти разницу между двумя данными датами (смещение) легко:
Использование 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(ORDERBY(SELECTNULL))-1FROM(SELECT'A'AS S UNIONALLSELECT'A'UNIONALLSELECT'A') S
) Numbers
WHERE N <= ABS(DATEDIFF(d,@date1,@date2));
Приведенный выше пример - ужасный код, но демонстрирует, как все объединяется.
Смешнее
Мне нужно много заниматься такими вещами, поэтому я инкапсулировал логику в два TVF. Первый генерирует диапазон чисел, а второй использует эту функцию для генерации диапазона дат. Математика состоит в том, чтобы гарантировать, что порядок ввода не имеет значения, и потому что я хотел использовать полный диапазон чисел, доступных в GenerateRangeSmallInt.
Следующая функция занимает ~ 16 мс процессорного времени, чтобы вернуть максимальный диапазон 65536 дат.
CREATEFUNCTION dbo.GenerateRangeDate (@date1 DATE,@date2 DATE
)
RETURNS TABLEWITH SCHEMABINDING
ASRETURN(SELECT D = DATEADD(d, N +32768,CASEWHEN@date1 <=@date2 THEN@date1 ELSE@date2 END)FROM dbo.GenerateRangeSmallInt(-32768, ABS(DATEDIFF(d,@date1,@date2))-32768));
GO
CREATEFUNCTION dbo.GenerateRangeSmallInt (@num1 SMALLINT =-32768,@num2 SMALLINT =32767)
RETURNS TABLEWITH SCHEMABINDING
ASRETURN(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))SELECTTOP(ABS(CAST(@num1 AS INT)- CAST(@num2 AS INT))+1)
N = ROW_NUMBER()OVER(ORDERBY(SELECTNULL))+CASEWHEN@num1 <=@num2 THEN@num1 ELSE@num2 END-1FROM Numbers A
, Numbers B
);
WITH CTE AS(SELECTDISTINCTconvert(varchar(10),StartTime,101)AS StartTime,
datediff(dd,StartTime, endTime)AS diff
FROM dbo.testdate
UNIONALLSELECT StartTime,
diff -1AS diff
FROM CTE
WHERE diff<>0)SELECTDISTINCT 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
Объяснение: первый столбец - это «начальная дата», второй столбец - это разница между начальной и конечной датой в днях, и он будет рассматриваться как «другой» столбец.
Вторая часть запроса:
UNION ALL SELECT StartTime, diff-1 AS diff FROM CTE WHERE diff<>0
Объяснение: Объединение всех будет наследовать результат вышеупомянутого запроса до тех пор, пока результат не станет нулевым, поэтому результат «StartTime» наследуется от сгенерированного запроса CTE, а от diff, уменьшается - 1, поэтому он выглядит как 3, 2 и 1 до 0
STARTDATE Specification
10/24/2012--> From Record 110/27/2012--> From Record 210/27/2012--> From Record 210/27/2012--> From Record 210/30/2012--> From Record 3
3-я часть запроса
SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime FROM CTE
Это добавит день «diff» в «startdate», поэтому результат должен быть таким, как показано ниже
(SELECT TRIM('2016-01-05'+ INTERVAL a + b DAY) date
FROM(SELECT0 a UNIONSELECT1 a UNIONSELECT2UNIONSELECT3UNIONSELECT4UNIONSELECT5UNIONSELECT6UNIONSELECT7UNIONSELECT8UNIONSELECT9) d,(SELECT0 b UNIONSELECT10UNIONSELECT20UNIONSELECT30UNIONSELECT40) m
WHERE'2016-01-05'+ INTERVAL a + b DAY <='2016-01-21')
Для тех, кто хочет это как сохраненное представление (MySQL не поддерживает вложенные операторы select в представлениях):
createview zero_to_nine asselect0as n unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9;createview date_range asselect curdate()- INTERVAL (a.n +(10* b.n)+(100* c.n)) DAY as date
from zero_to_nine as a
crossjoin zero_to_nine as b
crossjoin zero_to_nine as c;
Вы можете сделать
select*from date_range
получить
date
---2017-06-062017-06-052017-06-042017-06-032017-06-02...
Элегантное решение с использованием новой рекурсивной (Common Table Expressions) функциональности в MariaDB> = 10.3 и MySQL> = 8.0.
WITH RECURSIVE t as(select'2019-01-01'as dt
UNIONSELECT DATE_ADD(t.dt, INTERVAL 1 DAY)FROM t WHERE DATE_ADD(t.dt, INTERVAL 1 DAY)<='2019-04-30')select*FROM t;
Выше приведена таблица дат между «2019-01-01» и «2019-04-30». Это также прилично быстро. Возвращение дат на 1000 лет (~ 365 000 дней) занимает около 400 мс на моей машине.
Это хорошая идея с генерацией этих дат на лету. Тем не менее, я не чувствую себя комфортно делать это с довольно большим диапазоном, поэтому я получил следующее решение:
Создана таблица «DatesNumbers», в которой будут храниться числа, используемые для расчета дат:
CREATETABLE DatesNumbers (
i MEDIUMINT NOTNULL,PRIMARYKEY(i))
COMMENT='Used by Dates view';
Заполните таблицу, используя вышеуказанные методы, числами от -59999 до 40000. Этот диапазон даст мне даты от 59999 дней (~ 164 года) до 40000 дней (109 лет):
INSERTINTO DatesNumbers
SELECT
a.i +(10* b.i)+(100* c.i)+(1000* d.i)+(10000* e.i)-59999AS i
FROM(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9)AS a,(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9)AS b,(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9)AS c,(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9)AS d,(SELECT0AS i UNIONALLSELECT1UNIONALLSELECT2UNIONALLSELECT3UNIONALLSELECT4UNIONALLSELECT5UNIONALLSELECT6UNIONALLSELECT7UNIONALLSELECT8UNIONALLSELECT9)AS e
;
Создан вид "Даты":
SELECT
i,CURRENT_DATE()+ INTERVAL i DAY AS Date
FROM
DatesNumbers
Вот и все.
(+) Легко читаемые запросы
(+) Нет на лету чисел поколений
(+) Дает даты в прошлом и будущем, и нет никакого СОЮЗА для этого, как в этом посте .
(+) Даты «Только в прошлом» или «Только в будущем» могут быть отфильтрованы с помощью WHERE i < 0или WHERE i > 0(PK)
(-) используется временная таблица и представление
Используйте это, скажем, для создания временной таблицы, а затем выполните выбор * для временной таблицы. Или выводите результаты по одному. То, что вы говорите, вы хотите сделать, нельзя сделать с помощью инструкции SELECT , но это может быть осуществимо с вещами, специфичными для MySQL.
Опять же, может быть, вам нужны курсоры: http://dev.mysql.com/doc/refman/5.0/en/cursors.html
set language 'SPANISH'DECLARE@tabletable(fechaDesde datetime , fechaHasta datetime )INSERT@tableVALUES('20151231','20161231');WITH x AS(SELECT DATEADD( m ,1,fechaDesde )as fecha FROM@tableUNIONALLSELECT DATEADD( m ,1,fecha )FROM@table t INNERJOIN x ON DATEADD( m ,1,x.fecha )<= t.fechaHasta
)SELECTLEFT(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)
select d.Date
from(select
date(julianday('2010-01-20')+(a.a +(10* b.a)+(100* c.a)))as Date
from(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as a
crossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as b
crossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as c
) d
where
d.Date between'2010-01-20'and'2010-01-24'orderby d.Date
WITH
Digits AS(SELECT0 D UNIONSELECT1UNIONSELECT2UNIONSELECT3UNIONSELECT4UNIONSELECT5UNIONSELECT6UNIONSELECT7UNIONSELECT8UNIONSELECT9),
Dates AS(SELECT adddate('1970-01-01',t4.d*10000+ t3.d*1000+ t2.d*100+ t1.d*10+t0.d)AS date FROM Digits AS t0, Digits AS t1, Digits AS t2, Digits AS t3, Digits AS t4)SELECT*FROM Dates WHERE date BETWEEN'2017-01-01'AND'2017-12-31'
select datetable.Date
from(select date_format(adddate(now(),-(a.a +(10* b.a)+(100* c.a))),'%Y-%m-%d')AS Date
from(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as a
crossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as b
crossjoin(select0as a unionallselect1unionallselect2unionallselect3unionallselect4unionallselect5unionallselect6unionallselect7unionallselect8unionallselect9)as c
) datetable
where datetable.Date between now()- INTERVAL 14 Day and Now()orderby datetable.Date DESC
insert into table select ... as days date between '' and ''
Ответы:
В этом решении не используются циклы, процедуры или временные таблицы . Подзапрос генерирует даты за последние 10 000 дней и может быть расширен, чтобы перейти так далеко назад или вперед, как вы хотите.
Вывод:
Примечания по производительности
Тестирование здесь показывает , что производительность на удивление хорошая: приведенный выше запрос занимает 0,0009 сек.
Если мы расширим подзапрос для генерации ок. 100 000 чисел (и, следовательно, около 274 лет дат), это работает в 0,0458 сек.
Кстати, это очень переносимый метод, который работает с большинством баз данных с небольшими изменениями.
Пример SQL Fiddle, возвращающий 1000 дней
источник
UNION
наUNION ALL
- это тратит время на проверку дубликатов для удаления, которые не существуют. Хотя это слишком сложный IMO - если вы собираетесь создавать набор результатов с помощью UNION, почему бы просто не указать дату и покончить с этим?Вот еще один вариант с использованием представлений:
И тогда вы можете просто сделать (посмотреть, как это элегантно?):
Обновить
Стоит отметить, что вы сможете генерировать только прошлые даты, начиная с текущей даты . Если вы хотите сгенерировать любой диапазон дат (прошлые, будущие и промежуточные), вам придется использовать это представление:
источник
dates
упомянутом выше, вычисляются даты, начиная с текущей даты, поэтому вы не сможете получить даты, установленные в будущем. Ответ от @RedFilter страдает тем же недостатком дизайна. Я добавил обходной путь в моем ответе все же.UNION
предложения выглядят странно в одном операторе SQL.Принятый ответ не работает для PostgreSQL (синтаксическая ошибка в или около "a").
Вы делаете это в PostgreSQL с помощью
generate_series
функции, то есть:источник
Используя рекурсивное выражение общих таблиц (CTE), вы можете создать список дат, а затем выбрать его. Очевидно, что вы обычно не хотите создавать три миллиона дат, так что это просто иллюстрирует возможности. Вы можете просто ограничить диапазон дат внутри CTE и опустить предложение where в операторе select, используя CTE.
На Microsoft SQL Server 2005 создание списка CTE всех возможных дат заняло 1:08. Генерация ста лет заняла меньше секунды.
источник
MSSQL Query
Вывод
источник
Старое школьное решение для этого без цикла / курсора состоит в том, чтобы создать
NUMBERS
таблицу, которая имеет единственный столбец Integer со значениями, начинающимися с 1.Вам нужно заполнить таблицу достаточным количеством записей, чтобы удовлетворить ваши потребности:
Когда у вас есть
NUMBERS
таблица, вы можете использовать:Абсолютное низкотехнологичное решение будет:
Что бы вы использовали для этого?
Сформировать списки дат или чисел для того, чтобы присоединиться к. Вы должны сделать это, чтобы увидеть, где есть пропуски в данных, потому что вы ЛЕВЫЕ ПРИСОЕДИНЯЕТЕСЬ к списку последовательных данных - нулевые значения сделают очевидным, где существуют пропуски.
источник
DUAL
Таблица поддерживается Oracle и MySQL для использования в качестве дублера в таблице вFROM
п. Он не существует, выбор значений из него вернет любое значение. Идея состояла в том, чтобы иметь замену, потому что запрос SELECT требуетFROM
предложения, определяющего по крайней мере одну таблицу.Для Access 2010 - требуется несколько шагов; Я следовал той же схеме, что и выше, но думал, что смогу помочь кому-то в Access. Отлично сработало для меня, мне не нужно было хранить таблицу с датами.
Создайте таблицу с именем DUAL (аналогично тому, как работает таблица Oracle DUAL)
Создайте запрос с именем «ZeroThru9Q»; вручную введите следующий синтаксис:
Создайте запрос с именем «TodayMinus1KQ» (для дат до сегодняшнего дня); вручную введите следующий синтаксис:
Создайте запрос с именем «TodayPlus1KQ» (для дат после сегодняшнего дня); вручную введите следующий синтаксис:
Создайте объединенный запрос с именем «TodayPlusMinus1KQ» (для дат +/- 1000 дней):
Теперь вы можете использовать запрос:
источник
Процедура + временная таблица:
источник
Thx Pentium10 - вы заставили меня присоединиться к stackoverflow :) - это мой перенос на msaccess - думаю, он будет работать на любой версии:
ссылающиеся на MSysObjects просто «потому что для доступа нужен счетчик таблиц» как минимум в 1 записи, в предложении from - подойдет любая таблица с хотя бы 1 записью.
источник
Как указывалось (или, по крайней мере, упоминалось) во многих замечательных ответах, которые уже даны, эта проблема легко решается, когда у вас есть набор чисел для работы.
Примечание: ниже приведен T-SQL, но это просто моя конкретная реализация общих концепций, уже упомянутых здесь и в Интернете в целом. Преобразование кода на ваш диалект должен быть относительно простым.
Как? Рассмотрим этот запрос:
Выше приведен диапазон дат 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, другие вместо этого используют таблицу чисел, вы понимаете. Я выступаю за использование наиболее эффективного решения, которое вы также понимаете.Объединение этих двух методов решит нашу проблему:
Приведенный выше пример - ужасный код, но демонстрирует, как все объединяется.
Смешнее
Мне нужно много заниматься такими вещами, поэтому я инкапсулировал логику в два TVF. Первый генерирует диапазон чисел, а второй использует эту функцию для генерации диапазона дат. Математика состоит в том, чтобы гарантировать, что порядок ввода не имеет значения, и потому что я хотел использовать полный диапазон чисел, доступных в
GenerateRangeSmallInt
.Следующая функция занимает ~ 16 мс процессорного времени, чтобы вернуть максимальный диапазон 65536 дат.
источник
попробуй это.
источник
Вы хотели бы получить диапазон дат.
В вашем примере вы хотели бы получить даты между '2010-01-20' и '2010-01-24'
возможное решение:
объяснение
MySQL имеет функцию date_add, так
дам тебе
Функция datediff позволит вам часто знать, что вам придется повторить это
который возвращается
Получение списка дат в диапазоне дат сводится к созданию последовательности целых чисел, см. Генерацию последовательности целых чисел в MySQL.
В качестве ответа с наибольшим количеством голосов в качестве основы использовался подход, аналогичный https://stackoverflow.com/a/2652051/1497139 :
что приведет к
Строки теперь можно использовать для создания списка дат с заданной начальной датой. Чтобы включить дату начала, мы начинаем со строки -1;
источник
если вам понадобится больше, чем пара дней, вам нужен стол.
Создать диапазон дат в MySQL
затем,
источник
Создать даты между двумя полями даты
Если вы знакомы с запросом SQL CTE, то это решение поможет вам решить ваш вопрос.
Вот пример
У нас есть даты в одной таблице
Название таблицы: «дата теста»
Требовать результат:
Решение:
Объяснение: CTE Рекурсивное объяснение запроса
Первая часть запроса:
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
Объяснение: Объединение всех будет наследовать результат вышеупомянутого запроса до тех пор, пока результат не станет нулевым, поэтому результат «StartTime» наследуется от сгенерированного запроса CTE, а от diff, уменьшается - 1, поэтому он выглядит как 3, 2 и 1 до 0
Например
Спецификация результата
3-я часть запроса
SELECT DISTINCT DateAdd(dd,diff, StartTime) AS StartTime FROM CTE
Это добавит день «diff» в «startdate», поэтому результат должен быть таким, как показано ниже
результат
источник
Короче, чем принятый ответ, та же идея:
источник
Для тех, кто хочет это как сохраненное представление (MySQL не поддерживает вложенные операторы select в представлениях):
Вы можете сделать
получить
источник
Элегантное решение с использованием новой рекурсивной (Common Table Expressions) функциональности в MariaDB> = 10.3 и MySQL> = 8.0.
Выше приведена таблица дат между «2019-01-01» и «2019-04-30». Это также прилично быстро. Возвращение дат на 1000 лет (~ 365 000 дней) занимает около 400 мс на моей машине.
источник
Это хорошая идея с генерацией этих дат на лету. Тем не менее, я не чувствую себя комфортно делать это с довольно большим диапазоном, поэтому я получил следующее решение:
Вот и все.
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 , но это может быть осуществимо с вещами, специфичными для MySQL.
Опять же, может быть, вам нужны курсоры: http://dev.mysql.com/doc/refman/5.0/en/cursors.html
источник
Для Oracle мое решение:
Системная дата может быть изменена на определенную дату, а номер уровня может быть изменен, чтобы дать больше дат.
источник
если вы хотите список дат между двумя датами:
* скрипка здесь: http://sqlfiddle.com/#!6/9eecb/3469
источник
источник
источник
SQLite версия решения RedFilters
источник
улучшено с выходным днем присоединение к пользовательскому праздничному столу Microsoft MSSQL 2012 для таблицы дат powerpivot https://gist.github.com/josy1024/cb1487d66d9e0ccbd420bc4a23b6e90e
источник
источник
Можно также создать процедуру для создания таблицы календаря с графиком времени, отличным от дня. Если вы хотите таблицу на каждый квартал
например
ты можешь использовать
а затем манипулировать через
которые дают вам также тс
отсюда вы можете начать добавлять другую информацию, такую как
или создать реальную таблицу с помощью оператора создания таблицы
источник
Более общий ответ, который работает в AWS MySQL.
источник
Еще одно решение для mysql 8.0.1 и mariadb 10.2.2 с использованием рекурсивных общих табличных выражений:
источник