web-dev-qa-db-fra.com

Remplissez les dates manquantes avec la valeur des données de la date remplie précédente pour le groupe

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?

13
Mark Freeman

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 ...

14
Rob Farley

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

enter image description here

4
Kin Shah