Заполните пропущенные даты значением данных из предыдущей заполненной даты для группы

13

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

CREATE TABLE TicketAssigment (
    TicketId     INT NOT NULL,
    AssignedDate DATE NOT NULL,
    DepartmentId INT NOT NULL);

Мне нужно заполнить все пропущенные даты для каждого TicketId, используя DepartmentId из предыдущей строки TicketAssigment, упорядоченной по Date.

Если у меня есть строки TicketAssigment, как это:

1, '1/1/2016', 123 -- Opened
1, '1,4,2016', 456 -- Transferred and closed
2, '1/1/2016', 25  -- Opened
2, '1/2/2016', 52  -- Transferred
2, '1/4/2016', 25  -- Transferred and closed

Я хочу этот вывод:

1, '1/1/2016', 123
1, '1/2/2016', 123
1, '1/3/2016', 123
1, '1/4/2016', 456
2, '1/1/2016', 25
2, '1/2/2016', 52
2, '1/3/2016', 52
2, '1/4/2016', 25

Похоже, что это может быть близко к тому, что мне нужно, но у меня не хватило терпения дать ему закончить, и ориентировочная стоимость плана имеет 6 цифр:

SELECT  l.TicketId, c.Date, MIN(l.DepartmentId)
FROM    dbo.Calendar c 
        OUTER APPLY (SELECT TOP 1 TicketId, DepartmentId FROM TicketAssigment WHERE AssignedDate <= c.Date ORDER BY AssignedDate DESC) l
WHERE   c.Date <= (SELECT MAX(AssignedDate) FROM TicketAssigment)
GROUP   BY l.TicketId, c.Date
ORDER   BY l.TicketId, c.Date;

Я подозреваю, что есть способ сделать это, используя LAG и оконную раму, но я не совсем понял это. Что является более эффективным способом удовлетворения требования?

Марк Фриман
источник

Ответы:

14

Используйте LEAD()для получения следующей строки в разделе TicketId. Затем присоединитесь к таблице календаря, чтобы получить все даты между.

WITH TAwithnext AS
(SELECT *, LEAD(AssignmentDate) OVER (PARTITION BY TicketID ORDER BY AssignmentDate) AS NextAssignmentDate
 FROM TicketAssignment
)
SELECT t.TicketID, c.Date, t.DepartmentID
FROM dbo.Calendar c
JOIN TAwithnext t
    ON c.Date BETWEEN t.AssignmentDate AND ISNULL(DATEADD(day,-1,t.NextAssignmentDate),t.AssignmentDate)
;

Все виды способов получить таблицу календаря ...

Роб Фарли
источник
4

Это быстрый способ сделать (я не проверял на производительность или масштабируемость)

- создать таблицу календаря

-- borrowed from @Aaron's post http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-3 
CREATE TABLE dbo.Calendar(d DATE PRIMARY KEY);

INSERT dbo.Calendar(d) SELECT TOP (365)
 DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY number)-1, '20160101')
 FROM [master].dbo.spt_values
 WHERE [type] = N'P' ORDER BY number;

--- создайте свою тестовую таблицу

CREATE TABLE dbo.TicketAssigment (
    TicketId     INT NOT NULL,
    AssignedDate DATE NOT NULL,
    DepartmentId INT NOT NULL);

--  truncate table dbo.TicketAssigment;

insert into dbo.TicketAssigment values (1   ,   '1-1-2016'  ,   123 )
insert into dbo.TicketAssigment values (1   ,   '1-4-2016'  ,   456 )
insert into dbo.TicketAssigment values (2   ,   '1-1-2016'  ,   25  )
insert into dbo.TicketAssigment values (2   ,   '1-2-2016'  ,   52  )
insert into dbo.TicketAssigment values (2   ,   '1-4-2016'  ,   25  )

--- Запрос, чтобы получить желаемый результат

;with Cte as
(
  select TicketID, 
         min(AssignedDate) minAD, -- This is the min date
         max(AssignedDate) maxAD  -- This is the max date
  from TicketAssigment
  group by TicketID
)
select Cte.TicketID,
       c.d as AssignedDate,

       ( -- Get DeptID
       select top(1) T.departmentID
       from dbo.TicketAssigment as T
       where T.TicketID = cte.TicketID and
             T.AssignedDate <= c.d
       order by T.AssignedDate desc
       ) as DepartmentID
from Cte
  left outer join dbo.Calendar as c
      on c.d between Cte.minAD and Cte.maxAD
    order by Cte.TicketID

введите описание изображения здесь

Кин Шах
источник
Спасибо за это! Предполагаемый план выполнения показывает результирующий набор из 25 миллиардов строк, поэтому мы собираемся пересмотреть требование к отчетности (которое в настоящее время должно составляться каждый день для каждого тикета за прошедший год). Я надеюсь, что мы можем показать последний DepartmentId для каждого билета и показать детали DepartmentId по дням для одного выбранного билета по запросу.
Марк Фриман