Image des tickets d'assistance qui sont transférés entre les départements. Nous voulons savoir quel est le département en fin de journée pour chaque ticket pour chaque jour d'ouverture du ticket. Le tableau contient le dernier rayon de chaque ticket pour chaque jour d'ouverture pour lequel il y a un changement dans le rayon (y compris une ligne pour la date d'ouverture initiale du ticket et la date de fermeture). Le tableau de données ressemble à ceci:
CREATE TABLE TicketAssigment (
TicketId INT NOT NULL,
AssignedDate DATE NOT NULL,
DepartmentId INT NOT NULL);
Ce dont j'ai besoin est de remplir toutes les dates manquantes pour chaque TicketId, en utilisant le DepartmentId de la ligne TicketAssigment précédente commandée par Date.
Si j'ai des lignes TicketAssigment comme ceci:
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
Je veux cette sortie:
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
Il semble que cela soit proche de ce dont j'ai besoin, mais je n'ai pas eu la patience de le laisser finir, et le coût du plan estimé comporte 6 chiffres:
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;
Je soupçonne qu'il existe un moyen de le faire en utilisant LAG et un cadre de fenêtre, mais je ne l'ai pas tout à fait compris. Quelle est la manière la plus efficace de répondre à l'exigence?
Utilisez LEAD()
pour obtenir la ligne suivante dans la partition TicketId. Rejoignez ensuite un tableau Calendrier pour obtenir toutes les dates entre.
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)
;
Toutes sortes de façons d'obtenir une table de calendrier ...
C'est une façon rapide de faire (je n'ai pas testé les performances ou l'évolutivité)
- créer un tableau de calendrier
-- 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;
--- créez votre table de test
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 )
--- Requête pour obtenir la sortie souhaitée
;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