Тази заявка също върши работа. Производителността му е много добра (докато планът за изпълнение не изглежда толкова страхотен, действителният процесор и IO превъзхождат много други заявки).
Вижте как работи в Sql Fiddle .
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
;
С групиран индекс на WorkerID, JobStart, JobEnd, JobID
, и с примерните 7 реда от горната цигулка шаблон за нови данни за работник/работа, повторен достатъчно пъти, за да се получи таблица с 14 336 реда, ето резултатите от производителността. Включих другите работещи/правилни отговори на страницата (досега):
Author CPU Elapsed Reads Scans
------ --- ------- ------ -----
Erik 157 166 122 2
Gordon 375 378 106964 53251
Направих по-изчерпателен тест от различен (по-бавен) сървър (където всяка заявка беше изпълнена 25 пъти, най-добрите и най-лошите стойности за всеки показател бяха изхвърлени, а останалите 23 стойности бяха осреднени) и получих следното:
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
Алтернативната техника, за която смятах, че със сигурност ще подобри нещата. Е, спести 6 четения, но струва много повече процесор (което има смисъл). Вместо да пренасяте статистическите данни за начало/край на всеки времеви отрязък до края, най-добре е просто да преизчислите кои отрязъци да запазите с EXISTS
срещу оригиналните данни. Възможно е различен профил на няколко работници с много работни места да промени статистическите данни за ефективността за различни заявки.
В случай, че някой иска да опита, използвайте CREATE TABLE
и INSERT
изявления от моята цигулка и след това изпълнете това 11 пъти:
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
;
Създадох две други решения на тази заявка, но най-доброто с около двойно по-висока производителност имаше фатален недостатък (неправилно обработване на напълно затворени времеви диапазони). Другият имаше много висока/лоша статистика (което знаех, но трябваше да опитам).
Обяснение
Използвайки всички крайни времена от всеки ред, изградете отделен списък от всички възможни времеви диапазони, които представляват интерес, като дублирате всяко време на крайната точка и след това групирате по такъв начин, че да сдвоите всяко време със следващото възможно време. Сумирайте изминалите минути от тези диапазони, където съвпадат с действителното работно време на всеки работник.