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

13

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 département de chaque ticket pour chaque jour d'ouverture pour lequel il y a un changement dans le département (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, c'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

On dirait que cela pourrait être 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é a 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?

Mark Freeman
la source

Réponses:

14

Utilisez LEAD()pour obtenir la ligne suivante dans la partition TicketId. Rejoignez ensuite une table 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 ...

Rob Farley
la source
4

C'est un moyen rapide de le faire (je n'ai pas testé les performances ou l'évolutivité)

- créer une table 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

entrez la description de l'image ici

Kin Shah
la source
Merci pour cela! Le plan d'exécution estimé affiche un ensemble de résultats de 25 milliards de lignes, nous allons donc renégocier l'exigence de rapport (qui doit actuellement être rapportée chaque jour pour chaque ticket de l'année écoulée). J'espère que nous pourrons montrer le dernier DepartmentId pour chaque ticket et montrer le détail de DepartmentId par jour pour un seul ticket sélectionné sur demande.
Mark Freeman