Kueri ini juga berfungsi. Performanya sangat bagus (sementara rencana eksekusi terlihat tidak terlalu bagus, CPU dan IO sebenarnya mengalahkan banyak kueri lainnya).
Lihat itu bekerja di 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
;
Dengan indeks berkerumun di WorkerID, JobStart, JobEnd, JobID
, dan dengan sampel 7 baris dari biola di atas, templat untuk data pekerja/pekerjaan baru yang diulang cukup lama untuk menghasilkan tabel dengan 14.336 baris, berikut adalah hasil kinerjanya. Saya telah menyertakan jawaban lain yang berfungsi/benar di halaman (sejauh ini):
Author CPU Elapsed Reads Scans
------ --- ------- ------ -----
Erik 157 166 122 2
Gordon 375 378 106964 53251
Saya melakukan pengujian yang lebih lengkap dari server yang berbeda (lebih lambat) (di mana setiap kueri dijalankan 25 kali, nilai terbaik dan terburuk untuk setiap metrik dibuang, dan 23 nilai sisanya dirata-ratakan) dan mendapatkan yang berikut:
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
Teknik alternatif yang saya pikir pasti untuk memperbaiki keadaan. Yah, itu menghemat 6 bacaan, tetapi membutuhkan lebih banyak CPU (yang masuk akal). Daripada melanjutkan statistik awal/akhir setiap timelice sampai akhir, yang terbaik adalah menghitung ulang slice mana yang harus disimpan dengan EXISTS
terhadap data aslinya. Mungkin profil yang berbeda dari beberapa pekerja dengan banyak pekerjaan dapat mengubah statistik kinerja untuk kueri yang berbeda.
Jika ada yang ingin mencobanya, gunakan CREATE TABLE
dan INSERT
pernyataan dari biola saya dan kemudian jalankan ini 11 kali:
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
;
Saya membangun dua solusi lain untuk kueri ini tetapi yang terbaik dengan kinerja sekitar dua kali lipat memiliki kelemahan fatal (tidak menangani rentang waktu yang tertutup sepenuhnya dengan benar). Yang lain memiliki statistik yang sangat tinggi/buruk (yang saya tahu tetapi harus dicoba).
Penjelasan
Dengan menggunakan semua waktu titik akhir dari setiap baris, buat daftar yang berbeda dari semua rentang waktu yang mungkin menarik dengan menduplikasi setiap waktu titik akhir dan kemudian mengelompokkan sedemikian rupa untuk memasangkan setiap waktu dengan waktu berikutnya yang memungkinkan. Jumlahkan menit yang telah berlalu dari rentang ini di mana pun bertepatan dengan waktu kerja pekerja yang sebenarnya.