2014-02-11 4 views
1

Как я могу перечислить несколько диапазонов дат в SQL Server 2008? Я знаю, как это сделать, если моя таблица содержит одну записьSQL Server 2008 - Перечислить несколько диапазонов дат

StartDate EndDate 
2014-01-01 2014-01-03 

;WITH DateRange 
AS (
    SELECT @StartDate AS [Date] 
    UNION ALL 

    SELECT DATEADD(d, 1, [Date]) 
    FROM DateRange 
    WHERE [Date] < @EndDate 
    ) 
SELECT * FROM DateRange 

OUTPUT

2014-01-01, 2014-01-02, 2014-01-03 

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

StartDate EndDate 
2014-01-01 2014-01-03 
2014-01-05 2014-01-06 

DESIRED ВЫВОД:

2014-01-01, 2014-01-02, 2014-01-03, 2014-01-05, 2014-01-06 
+1

Если вы отправляете код, XML или образцы данных, ** ПОЖАЛУЙСТА ** выделите эти строки в текстовом редакторе и нажмите кнопку «образцы кода» ('{}') на панели инструментов редактора, чтобы хорошо выделить и выделить синтаксис Это! (и, таким образом, вам не нужны никакие из этих беспорядочных ' ' и '
' тегов!) –

+0

Нечетный ... каждый раз, когда я пытаюсь отправить ответ, я сталкиваюсь с ошибкой из StackOverflow. –

+0

@Jon Что такое «ошибка»? Какая ошибка? –

ответ

0

Хорошо, давайте посмотрим. Определите диапазоны в виде таблицы. Затем создайте полный диапазон дат с первой до последней даты. Наконец, выберите даты, которые находятся в диапазоне:

with dateranges as (
     select cast('2014-01-01' as date) as StartDate, cast('2014-01-03' as date) as EndDate union all 
     select '2014-01-05', '2014-01-06' 
    ), 
    _dates as (
     SELECT min(StartDate) AS [Date], max(EndDate) as enddate 
     FROM dateranges 
     UNION ALL 
     SELECT DATEADD(d, 1, [Date]), enddate 
     FROM _dates 
     WHERE [Date] < enddate 
    ), 
    dates as (
     select [date] 
     from _dates d 
     where exists (select 1 from dateranges dr where d.[date] >= dr.startdate and d.[date] <= dr.enddate) 
    ) 
select * 
from dates 
. . . 

Вы можете увидеть эту работу here.

+0

Ваша идея звуковая, но ваш SQL не запускается. Вы перепутали несколько своих ссылок на ваши CTE (например, даты дат дат, которые появляются после этого, и даты, ссылающиеся на себя, когда они не должны быть рекурсивными). Синтаксис соединения также недействителен в SQL Server, так как отсутствует предложение «ON». –

+0

@ Jon Я согласен, что идея звучит, но она, безусловно, не работает. Заставляет вас задаться вопросом, почему люди принимают ответы, которые они, возможно, даже не пытались. –

+0

@JonSenchyna. , , Идея была в порядке. Синтаксис был абсурдным. Решение теперь работает. Я предполагаю, что это было принято на основе этой идеи. По сути, код был псевдокодом, а не фактическим исполняемым кодом. Теперь это работает. –

0

Вы можете захватить мин и макс дату первым, так как:

SELECT @startDate = MIN(StartDate), @endDate = MAX(EndDate) 
FROM YourTable 
WHERE ... 

А затем передать эти переменные в диапазоне дат, переписчик.

Редактировать ... Огонь, я пропустил важное требование. См. Принятый ответ.

+0

Я мог бы, но тогда 2014-01-04 появлялся бы в таблице, когда он не является частью какого-либо диапазона. Ваш ответ частично корректен, как показано @Gordon Linoff. Отсутствующий фрагмент является окончательным выбором, где я пропускаю даты не в диапазонах. –

+0

@ user1 Моя ошибка, я пропустил это требование. – Manny

0

Как уже упоминалось GordonLinoff, вы должны:

  1. Храните диапазоны в таблице
  2. Сформировать диапазон дат, который охватывает ваши диапазоны
  3. Фильтр вниз только те даты, которые находятся в пределах диапазона

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

-- Create a table of digits (0-9) 
DECLARE @Digits TABLE (digit INT NOT NULL PRIMARY KEY); 
INSERT INTO @Digits(digit) 
VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); 
WITH 
-- Store our ranges in a common table expression 
CTE_DateRanges(StartDate, EndDate) AS (
    SELECT '2014-01-01', '2014-01-03' 
    UNION ALL 
    SELECT '2014-01-05', '2014-01-06' 
) 

SELECT DATEADD(DAY, NUMBERS.num, RANGES.StartDate) AS Date 
FROM 
(
    -- Create the list of all 3-digit numbers (0-999) 
    SELECT D3.digit * 100 + D2.digit * 10 + D1.digit AS num 
    FROM @Digits AS D1 
    CROSS JOIN @Digits AS D2 
    CROSS JOIN @Digits AS D3 
    -- Add more CROSS JOINs to @Digits if your ranges span more than 999 days 
) NUMBERS 
-- Join to our ranges table to generate the dates and filter them 
-- down to those that fall within a range 
INNER JOIN CTE_DateRanges RANGES 
    ON DATEADD(DAY, NUMBERS.num, RANGES.StartDate) <= RANGES.EndDate 
ORDER BY 
     Date 

Создание даты осуществляется путем присоединения нашего списка номеров с нашими диапазоны дат, используя число как количество дней, чтобы добавить к StartDate диапазона. Затем мы отфильтровываем любые результаты, когда сгенерированная дата для данного диапазона выходит за пределы этого диапазона EndDate. Поскольку мы добавляем неотрицательное количество дней в StartDate для генерации даты, мы знаем, что наша дата всегда будет больше или равна StartDate диапазона, поэтому нам не нужно включают StartDate в статье WHERE.

Этот запрос возвращает DATETIME значения. Если вам нужно значение DATE, а не значение DATETIME, вы можете просто указать значение в предложении SELECT.

Кредит составляет Itzik Ben-Gan для таблицы цифр.

+0

@AaronBertrand Спасибо, что редактировали это для меня. Это похоже на случай этой проблемы: http://meta.stackexchange.com/questions/185681/unable-to-post-any-answer-or-comment-with-union-all –

+0

Да, наши комментарии пересекали пути (см. выше). –

Смежные вопросы