Truy vấn này cũng thực hiện công việc. Hiệu suất của nó rất tốt (trong khi kế hoạch thực thi trông không quá tuyệt vời, CPU và IO thực tế đã đánh bại nhiều truy vấn khác).
Xem nó hoạt động trong 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
;
Với chỉ mục được phân nhóm trên WorkerID, JobStart, JobEnd, JobID
và với mẫu 7 hàng ở trên tạo ra một mẫu cho dữ liệu công nhân / công việc mới được lặp lại đủ lần để tạo ra một bảng có 14.336 hàng, đây là kết quả hiệu suất. Tôi đã bao gồm các câu trả lời hiệu quả / đúng đắn khác trên trang (cho đến nay):
Author CPU Elapsed Reads Scans
------ --- ------- ------ -----
Erik 157 166 122 2
Gordon 375 378 106964 53251
Tôi đã thực hiện một bài kiểm tra toàn diện hơn từ một máy chủ khác (chậm hơn) (trong đó mỗi truy vấn được chạy 25 lần, giá trị tốt nhất và kém nhất cho mỗi chỉ số bị loại bỏ và 23 giá trị còn lại được tính trung bình) và nhận được kết quả như sau:
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
Kỹ thuật thay thế mà tôi nghĩ chắc chắn sẽ cải thiện mọi thứ. Chà, nó đã lưu được 6 lần đọc, nhưng tốn nhiều CPU hơn (điều đó có ý nghĩa). Thay vì thực hiện thông qua số liệu thống kê đầu / cuối của mỗi bảng thời gian cho đến cuối, tốt nhất chỉ cần tính toán lại các lát nào cần giữ với EXISTS
so với dữ liệu ban đầu. Có thể là một hồ sơ khác nhau của một vài công nhân với nhiều công việc có thể thay đổi thống kê hiệu suất cho các truy vấn khác nhau.
Trong trường hợp có ai muốn thử, hãy sử dụng CREATE TABLE
và INSERT
câu lệnh từ fiddle của tôi và sau đó chạy điều này 11 lần:
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
;
Tôi đã xây dựng hai giải pháp khác cho truy vấn này nhưng giải pháp tốt nhất có hiệu suất gấp đôi có một lỗ hổng nghiêm trọng (không xử lý chính xác các phạm vi thời gian hoàn toàn kèm theo). Cái kia có số liệu thống kê rất cao / xấu (tôi biết nhưng phải thử).
Giải thích
Sử dụng tất cả thời gian của điểm cuối từ mỗi hàng, tạo một danh sách riêng biệt về tất cả các phạm vi thời gian có thể được quan tâm bằng cách sao chép từng thời gian của điểm cuối và sau đó nhóm theo cách để ghép nối mỗi lần với thời gian có thể tiếp theo. Tính tổng số phút đã trôi qua của các phạm vi này ở bất kỳ nơi nào chúng trùng với thời gian làm việc thực tế của bất kỳ công nhân nào.