Перебора дат в SQL
у меня есть таблица данных, которая выглядит немного как это:
Name StartTime FinishTime Work
Bob 2010-08-03 08:00:00 2010-08-03 12:00:00 4
Bob 2010-08-03 13:00:00 2010-08-03 16:00:00 3
Pete 2010-08-04 08:00:00 2010-08-04 12:00:00 4
Mark 2010-08-04 10:00:00 2010-08-04 12:00:00 2
ни один из этих диапазонов дат не должен превышать полуночи.
Я хочу написать SQL, который даст мне следующий вывод, учитывая дату начала ввода 2010-08-02 и дату окончания 2010-08-05
Date Name TotalWork
2010-08-03 Bob 7
2010-08-03 Pete 3
2010-08-04 Pete 4
2010-08-04 Mark 2
я мог бы жить, и на самом деле, возможно, в конечном итоге потребуется, чтобы любые дни, которые не связаны с работой, также были представлены в наборе результатов, возможно, в виде строки это:
2010-08-05 NULL 0
Я не совсем уверен, как перебирать даты в SQL так же, как и с другими языками.
чтобы дать этому некоторый контекст, вывод этого в конечном итоге подключится к элементу управления .NET с накоплением диаграммы.
может кто-нибудь дать мне подсказку, ссылку на учебник или другой помощи? Иначе я буду возиться с этим несколько дней!
спасибо!
Джонатан
2 ответов
попробуйте это:
Select DateAdd(day, 0, DateDiff(day, 0, StartDate)) Date,
Name, Sum (Work) TotalWork
From TableData
Group By Name, DateAdd(day, 0, DateDiff(day, 0, StartDate))
получить недостающие дни сложнее.
Declare @SD DateTime, @ED DateTime -- StartDate and EndDate variables
Select @SD = DateAdd(day, 0, DateDiff(day, 0, Min(StartDate))),
@ED = DateAdd(day, 0, DateDiff(day, 0, Max(StartDate)))
From TableData
Declare @Ds Table (aDate SmallDateTime)
While @SD <= @ED Begin
Insert @Ds(aDate ) Values @SD
Set @SD = @SD + 1
End
-- ----------------------------------------------------
Select DateAdd(day, 0, DateDiff(day, 0, td.StartDate)) Date,
td.Name, Sum (td.Work) TotalWork
From @Ds ds Left Join TableData td
On DateAdd(day, 0, DateDiff(day, 0, tD.StartDate)) = ds.aDate
Group By Name, DateAdd(day, 0, DateDiff(day, 0, tD.StartDate))
EDIT, я возвращаюсь к этому с решением, которое использует общее табличное выражение (CTE). Это не требует использования таблицы dates.
Declare @SD DateTime, @ED DateTime
Declare @count integer = datediff(day, @SD, @ED)
With Ints(i) As
(Select 0 Union All
Select i + 1 From Ints
Where i < @count )
Select DateAdd(day, 0, DateDiff(day, 0, td.StartDate)) Date,
td.Name, Sum (td.Work) TotalWork
From Ints i
Left Join TableData d
On DateDiff(day, @SD, d.StartDate) = i.i
Group By d.Name, DateAdd(day, 0, DateDiff(day, 0, d.StartDate))
способ, которым вы перебираете строки в SQL, заключается в том, что вы этого не делаете. SQL-это язык, основанный на множестве, который требует совершенно другого мышления от других процедурных языков. Если вы собираетесь работать с SQL, вам действительно нужно иметь возможность сделать этот сдвиг в мышлении, чтобы быть успешным.
вот как я бы справился с этим:
SELECT
CONVERT(VARCHAR(10), StartTime, 121) AS [date],
name,
SUM(work)
FROM
My_Table
WHERE
StartTime >= @start_date AND
StartTime < DATEADD(dy, 1, @finish_date)
GROUP BY
CONVERT(VARCHAR(10), StartTime, 121),
name
кроме того, ваш дизайн таблицы выглядит так, как будто он нарушает нормальные стандарты дизайна базы данных. Ваша колонка "работа" на самом деле просто расчет между StartTime и FinishTime. Это делает его дублированием одних и тех же данных, что может вызвать всевозможные проблемы. Например, что вы делаете, когда ваше время начала и окончания 4 часа, но "работа" говорит 5 часов?
чтобы включить даты без связанной работы, вам нужно будет либо обработать это в передней части, либо вам понадобится таблица "календарь". В нем были бы все даты, и вы бы сделали левое соединение с этим со своим столом. Для пример:
SELECT
CONVERT(VARCHAR(10), C.StartTime, 121) AS [date],
MT.name,
SUM(MT.work)
FROM
Calendar C
LEFT JOIN My_Table MT ON
MT.StartDate BETWEEN C.StartTime and C.FinishTime
WHERE
C.StartTime >= @start_date AND
C.StartTime < DATEADD(dy, 1, @finish_date)
GROUP BY
CONVERT(VARCHAR(10), C.StartTime, 121),
MT.name
таблица календаря также позволяет добавлять дополнительную информацию к датам, например, флаг для праздников," сверхурочных " дней (возможно, работа засчитывается как полтора времени по воскресеньям) и т. д.
Примечание: решение Чарльза Бретаны, вероятно, немного чище, так как оно сохраняет типы данных как даты, а не превращает их в строки. Я собираюсь оставить это здесь, хотя для некоторых других комментариев.