このクエリも同様に機能します。そのパフォーマンスは非常に優れています (実行計画はそれほど良くないように見えますが、実際の CPU と IO は他の多くのクエリを上回っています)。
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 回の読み取りを節約できましたが、CPU のコストが大幅に増加しました (これは理にかなっています)。各タイムスライスの開始/終了統計を最後まで実行する代わりに、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
;
このクエリに対して他に 2 つのソリューションを作成しましたが、パフォーマンスが約 2 倍の最良のソリューションには致命的な欠陥がありました (完全に囲まれた時間範囲を正しく処理していません)。もう 1 つは非常に高い/悪い統計値でした (わかっていましたが、試してみる必要がありました)。
説明
各行のすべての終点時刻を使用して、各終点時刻を複製し、次に可能な時刻と毎回ペアになるようにグループ化することにより、対象となる可能性のあるすべての時刻範囲の個別のリストを作成します。労働者の実際の作業時間と一致する場合はいつでも、これらの範囲の経過分を合計します。