Denne forespørgsel gør også jobbet. Dens ydeevne er meget god (mens udførelsesplanen ikke ser så god ud, slår den faktiske CPU og IO mange andre forespørgsler).
Se, hvordan det fungerer i en Sql-violin .
WITH Times AS (
SELECT DISTINCT
H.WorkerID,
T.Boundary
FROM
dbo.JobHistory H
CROSS APPLY (VALUES (H.JobStart), (H.JobEnd)) T (Boundary)
), Groups AS (
SELECT
WorkerID,
T.Boundary,
Grp = Row_Number() OVER (PARTITION BY T.WorkerID ORDER BY T.Boundary) / 2
FROM
Times T
CROSS JOIN (VALUES (1), (1)) X (Dup)
), Boundaries AS (
SELECT
G.WorkerID,
TimeStart = Min(Boundary),
TimeEnd = Max(Boundary)
FROM
Groups G
GROUP BY
G.WorkerID,
G.Grp
HAVING
Count(*) = 2
)
SELECT
B.WorkerID,
WorkedMinutes = Sum(DateDiff(minute, 0, B.TimeEnd - B.TimeStart))
FROM
Boundaries B
WHERE
EXISTS (
SELECT *
FROM dbo.JobHistory H
WHERE
B.WorkerID = H.WorkerID
AND B.TimeStart < H.JobEnd
AND B.TimeEnd > H.JobStart
)
GROUP BY
WorkerID
;
Med et klynget indeks på WorkerID, JobStart, JobEnd, JobID
, og med prøven på 7 rækker fra ovenstående violin, en skabelon for nye arbejder/job-data, der gentages nok gange til at give en tabel med 14.336 rækker, her er præstationsresultaterne. Jeg har inkluderet de andre fungerende/korrekte svar på siden (indtil videre):
Author CPU Elapsed Reads Scans
------ --- ------- ------ -----
Erik 157 166 122 2
Gordon 375 378 106964 53251
Jeg lavede en mere udtømmende test fra en anden (langsommere) server (hvor hver forespørgsel blev kørt 25 gange, de bedste og værste værdier for hver metrik blev smidt ud, og de resterende 23 værdier blev beregnet til gennemsnittet) og fik følgende:
Query CPU Duration Reads Notes
-------- ---- -------- ------ ----------------------------------
Erik 1 215 231 122 query as above
Erik 2 326 379 116 alternate technique with no EXISTS
Gordon 1 578 682 106847 from j
Gordon 2 584 673 106847 from dbo.JobHistory
Den alternative teknik troede jeg var sikker på at forbedre tingene. Nå, det sparede 6 læsninger, men kostede meget mere CPU (hvilket giver mening). I stedet for at føre start/slut-statistikken for hvert tidsudsnit igennem til slutningen, er det bedst bare at genberegne, hvilke udsnit der skal beholdes med EXISTS
mod de originale data. Det kan være, at en anden profil af få arbejdere med mange job kan ændre præstationsstatistikken for forskellige forespørgsler.
Hvis nogen vil prøve det, så brug CREATE TABLE
og INSERT
udsagn fra min violin og derefter køre dette 11 gange:
INSERT dbo.JobHistory
SELECT
H.JobID + A.MaxJobID,
H.WorkerID + A.WorkerCount,
DateAdd(minute, Elapsed + 45, JobStart),
DateAdd(minute, Elapsed + 45, JobEnd)
FROM
dbo.JobHistory H
CROSS JOIN (
SELECT
MaxJobID = Max(JobID),
WorkerCount = Max(WorkerID) - Min(WorkerID) + 1,
Elapsed = DateDiff(minute, Min(JobStart), Min(JobEnd))
FROM dbo.JobHistory
) A
;
Jeg byggede to andre løsninger til denne forespørgsel, men den bedste med omtrent dobbelt så god ydeevne havde en fatal fejl (ikke håndterede fuldstændigt lukkede tidsintervaller korrekt). Den anden havde meget høj/dårlig statistik (som jeg vidste, men måtte prøve).
Forklaring
Brug alle endepunktstiderne fra hver række til at opbygge en særskilt liste over alle mulige tidsintervaller af interesse ved at duplikere hvert endepunktstidspunkt og derefter gruppere på en sådan måde, at hver gang parres med det næste mulige tidspunkt. Sum de forløbne minutter af disse intervaller, uanset hvor de falder sammen med enhver faktisk arbejders arbejdstid.