Задача T-SQL - найти все даты с указанными неделями в двух диапазонах дат - PullRequest
1 голос
/ 20 июля 2011

У меня довольно сложная задача в T-SQL (MSSQL 2005). У меня есть такая таблица:

WeekDay| SlotTime
------------------
|  1   |   07:00
|  3   |   09:00
|  7   |   14:00
|  1   |   15:00
|  4   |   22:00
|  6   |   08:00

, где 1-й столбец - это номер недели, а 2-й - какое-то значение времени.

В качестве параметров для моего запроса у меня есть 2 даты, например:

StartDate = '2011-07-20'
EndDate = '2011-08-17'

Это определение диапазона для моих данных. Я должен сгенерировать для этих диапазонов все даты, когда происходит WeekDay (из таблицы выше), и добавить к ним значение SlotTime. Так, например, для приведенного выше диапазона дат столбец результата должен быть:

2011-07-20 9:00
2011-07-21 22:00
2011-07-23 8:00
2011-07-24 14:00
2011-07-25 7:00
2011-07-25 15:00
2011-07-27 9:00
2011-07-28 22:00
2011-07-30 8:00
etc.
...

Есть идеи, как этого добиться? Какие-нибудь советы? :) Я считаю, что это невозможно без каких-либо огромных (?) Расчетов и дополнительных таблиц ...

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

create function dbo.NthWeekDay(
   @first datetime,   -- First of the month of interest (no time part)
   @nth tinyint,      -- Which of them - 1st, 2nd, etc.
   @dow tinyint       -- Day of week we want
) returns datetime as begin
-- Note: Returns a date in a later month if @nth is too large
  declare @result datetime
  set @result = @first + 7*(@nth-1)
  return @result + (7 + @dow - datepart(weekday,@result))%7
end
go

SET DATEFORMAT ymd
SET DATEFIRST 1

select dbo.NthWeekDay('2011-07-20',1,1) as D

go

drop function NthWeekDay 

Ответы [ 3 ]

1 голос
/ 20 июля 2011

Это сделает трюк

SET DATEFIRST 1 
-- temp table
declare @t table(WeekDay tinyint, SlotTime time)
-- fill table
insert @t values (1, '7:00')
insert @t values (3, '9:00')
insert @t values (7, '14:00')
insert @t values (1, '15:00')
insert @t values (4, '22:00')
insert @t values (6, '8:00')

-- declare interval
declare @startdate datetime
declare @enddate   datetime
set @StartDate = '2011-07-20'
set @EndDate   = '2011-08-17'

;with cte as
(
-- recusive to make timeline
SELECT @StartDate loopday
UNION ALL
SELECT loopday + 1
FROM cte
WHERE loopday  < @EndDate 
), b as
(
-- join timeline with Weekday and add Slottime to timeline
SELECT loopday + t.SlotTime col
FROM cte
JOIN @t t
ON t.WeekDay = datepart(weekday, cte.loopday)
)
SELECT col 
FROM b
ORDER BY 1
OPTION( MAXRECURSION 0)

(Результат выглядит как ваш вывод)

1 голос
/ 20 июля 2011

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

Вот довольно приятный способ создания таблицы чисел в SQL 2008, также может работать в 2005 году: http://archive.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=NumbersTable

В качестве альтернативы вы просто создаете таблицу с идентификатором, а затем вставляете в нее строки TOP x.

Оттуда вы можете вычислить остальные

number
1          DateAdd(dd, '2011/07/20', number)      DatePart(dw, DateAdd(dd, '2011/07/20', number))
2          DateAdd(dd, '2011/07/20', number)      DatePart(dw, DateAdd(dd, '2011/07/20', number))
3          DateAdd(dd, '2011/07/20', number)      DatePart(dw, DateAdd(dd, '2011/07/20', number))
4          DateAdd(dd, '2011/07/20', number)      DatePart(dw, DateAdd(dd, '2011/07/20', number))
5          DateAdd(dd, '2011/07/20', number)      DatePart(dw, DateAdd(dd, '2011/07/20', number))
6          DateAdd(dd, '2011/07/20', number)      DatePart(dw, DateAdd(dd, '2011/07/20', number))
7          DateAdd(dd, '2011/07/20', number)      DatePart(dw, DateAdd(dd, '2011/07/20', number))

Присоединить эту таблицу кисходные результаты, а затем вставьте продукт в финальный стол.

Запрос:

SELECT TOP 5000
    IDENTITY( INT, 0, 1 ) AS N
INTO
    Number
FROM
    sys.objects a,
    sys.objects b,
    sys.objects c

SELECT
    N,
    DATEADD(dd, N, '7/20/2011') AS Date,
    DATEPART(dw, DATEADD(dd, N, '7/20/2011')) AS DayofWeek
FROM
    Number
WHERE
    DATEADD(dd, N, '7/20/2011') BETWEEN '7/20/2011'
                                AND     '8/17/2011'

Результат:

N           Date                    DayofWeek
----------- ----------------------- -----------
0           2011-07-20 00:00:00.000 4
1           2011-07-21 00:00:00.000 5
2           2011-07-22 00:00:00.000 6
3           2011-07-23 00:00:00.000 7
4           2011-07-24 00:00:00.000 1
5           2011-07-25 00:00:00.000 2
6           2011-07-26 00:00:00.000 3
7           2011-07-27 00:00:00.000 4
8           2011-07-28 00:00:00.000 5
9           2011-07-29 00:00:00.000 6
10          2011-07-30 00:00:00.000 7
11          2011-07-31 00:00:00.000 1
12          2011-08-01 00:00:00.000 2
13          2011-08-02 00:00:00.000 3
14          2011-08-03 00:00:00.000 4
15          2011-08-04 00:00:00.000 5
16          2011-08-05 00:00:00.000 6
17          2011-08-06 00:00:00.000 7
18          2011-08-07 00:00:00.000 1
19          2011-08-08 00:00:00.000 2
20          2011-08-09 00:00:00.000 3
21          2011-08-10 00:00:00.000 4
22          2011-08-11 00:00:00.000 5
23          2011-08-12 00:00:00.000 6
24          2011-08-13 00:00:00.000 7
25          2011-08-14 00:00:00.000 1
26          2011-08-15 00:00:00.000 2
27          2011-08-16 00:00:00.000 3
28          2011-08-17 00:00:00.000 4
0 голосов
/ 20 июля 2011

Я согласен с @Lucent Fox, что таблица чисел может быть очень полезна здесь. Тем не менее, вам не нужно создавать его, если запрашиваемые диапазоны никогда не могут охватывать более 5 с половиной лет. Системная таблица с именем master..spt_values или, точнее, ее подмножество, где type = 'P', может использоваться в качестве числовой таблицы в вашем запросе:

WITH datelist AS (
  SELECT
    Date = DATEADD(DAY, number, @StartDate)
  FROM master..spt_values
  WHERE type = 'P'
    AND number BETWEEN 0 AND DATEDIFF(DAY, @StartDate, @EndDate)
)
SELECT
  Timestamp = d.Date + s.SlotTime
FROM datelist d
  INNER JOIN SlotTable s ON s.WeekDay = DATEPART(WEEKDAY, d.Date)
ORDER BY Timestamp
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...