SQL-запрос для преобразования диапазонов дат в дневные записи - PullRequest
2 голосов
/ 19 марта 2011

Требования

  1. У меня есть таблица данных, которая сохраняет данные в диапазонах дат.
  2. Каждая запись может перекрываться предыдущими записями (запись имеет столбец CreatedOn datetime).
  3. Новая запись может определять свой собственный диапазон дат, если это необходимо, следовательно, может перекрывать несколько более старых записей.
  4. Каждая новая перекрывающаяся запись переопределяет настройки более старых записей, которые она перекрывает.

Результирующий набор

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

Чтобы преобразовать диапазоны в дни, я думал о таблица чисел / дат и пользовательская функция(UDF), чтобы получить данные за каждый день в диапазоне, но мне интересно, есть ли другой (как в лучше * или даже быстрее ) способ сделать это, так как я используюпоследний SQL Server 2008 R2.

сохраненные данные

Представьте, что мои сохраненные данные выглядят так:

ID | RangeFrom | RangeTo  | Starts | Ends  | CreatedOn (not providing data)
---|-----------|----------|--------|-------|-----------
1  | 20110101  | 20110331 | 07:00  | 15:00
2  | 20110401  | 20110531 | 08:00  | 16:00
3  | 20110301  | 20110430 | 06:00  | 14:00 <- overrides both partially

Результаты

Если я хотел получить данные изИтоговая таблица с 1 января 2011 года по 31 мая 2001 года должна выглядеть следующим образом (пропущены очевидные строки):

DayDate | Starts | Ends
--------|--------|------
20110101| 07:00  | 15:00  <- defined by record ID = 1
20110102| 07:00  | 15:00  <- defined by record ID = 1
...                          many rows omitted for obvious reasons
20110301| 06:00  | 14:00  <- defined by record ID = 3
20110302| 06:00  | 14:00  <- defined by record ID = 3
...                          many rows omitted for obvious reasons
20110501| 08:00  | 16:00  <- defined by record ID = 2
20110502| 08:00  | 16:00  <- defined by record ID = 2
...                          many rows omitted for obvious reasons
20110531| 08:00  | 16:00  <- defined by record ID = 2

Ответы [ 2 ]

6 голосов
/ 19 марта 2011

На самом деле, поскольку вы работаете с датами, будет полезнее таблица Календарь.

Declare @StartDate date
Declare @EndDate date

;With Calendar As
    (
    Select @StartDate As [Date]
    Union All
    Select DateAdd(d,1,[Date])
    From Calendar
    Where [Date] < @EndDate
    )
Select ...
From Calendar
    Left Join MyTable
        On Calendar.[Date] Between MyTable.Start And MyTable.End
Option ( Maxrecursion 0 );

Добавление

Пропущена часть о правиле козырей в исходном сообщении:

Set DateFormat MDY;
Declare @StartDate date = '20110101';
Declare @EndDate date = '20110501';

-- This first CTE is obviously to represent
-- the source table
With SampleData As 
    (
    Select 1 As Id
        , Cast('20110101' As date) As RangeFrom
        , Cast('20110331' As date) As RangeTo
        , Cast('07:00' As time) As Starts
        , Cast('15:00' As time) As Ends
        , CURRENT_TIMESTAMP As CreatedOn
    Union All Select 2, '20110401', '20110531', '08:00', '16:00', DateAdd(s,1,CURRENT_TIMESTAMP )
    Union All Select 3, '20110301', '20110430', '06:00', '14:00', DateAdd(s,2,CURRENT_TIMESTAMP )
    )
    , Calendar As
    (
    Select @StartDate As [Date]
    Union All
    Select DateAdd(d,1,[Date])
    From Calendar
    Where [Date] < @EndDate
    )
    , RankedData As
    (
    Select C.[Date]
        , S.Id
        , S.RangeFrom, S.RangeTo, S.Starts, S.Ends
        , Row_Number() Over( Partition By C.[Date] Order By S.CreatedOn Desc ) As Num
    From Calendar As C
        Join SampleData As S
            On C.[Date] Between S.RangeFrom And S.RangeTo
    )
Select [Date], Id, RangeFrom, RangeTo, Starts, Ends
From RankedData
Where Num = 1   
Option ( Maxrecursion 0 );

Короче говоря, я ранжирую все примеры данных, предпочитая более новые строки, которые перекрывают одну и ту же дату.

1 голос
/ 26 апреля 2011

Зачем делать все это в БД, когда вы можете сделать это лучше в памяти

Это решение (которое я в конечном итоге использовал), которое показалось наиболее разумным с точки зрения передачи данных, скорости и ресурсов.

  1. получить фактические определения диапазона от БД до среднего уровня (меньший объем данных)
  2. создать в календаре памяти определенный диапазон дат (быстрее, чем в БД)
  3. поместить эти БДопределения в (гораздо проще и быстрее, чем в БД)

И это все.Я понял, что усложнять некоторые вещи в БД не стоит, если у вас есть исполняемый код в памяти, который может выполнять те же манипуляции быстрее и эффективнее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...