Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Ermitteln Sie die Gesamtzeit, die mit mehreren Jobs / Aufträgen gearbeitet wurde, mit Überschneidungen / sich überschneidenden Zeiten für jeden Arbeiter und Job / Auftrag

Diese Abfrage erledigt den Job auch. Seine Leistung ist sehr gut (während der Ausführungsplan nicht so toll aussieht, schlagen die tatsächliche CPU und IO viele andere Abfragen).

Sehen Sie, wie es in einer Sql Fiddle funktioniert .

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
;

Mit einem gruppierten Index auf WorkerID, JobStart, JobEnd, JobID , und mit den 7 Beispielzeilen aus der obigen Fiddle eine Vorlage für neue Arbeiter/Job-Daten, die oft genug wiederholt wird, um eine Tabelle mit 14.336 Zeilen zu ergeben, hier sind die Leistungsergebnisse. Ich habe die anderen funktionierenden/korrekten Antworten auf der Seite (bisher) eingefügt:

Author  CPU  Elapsed  Reads   Scans
------  ---  -------  ------  -----
  Erik  157    166      122       2
Gordon  375    378    106964  53251

Ich habe einen umfassenderen Test von einem anderen (langsameren) Server durchgeführt (bei dem jede Abfrage 25 Mal ausgeführt wurde, die besten und schlechtesten Werte für jede Metrik verworfen und die verbleibenden 23 Werte gemittelt wurden) und Folgendes erhalten:

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

Die alternative Technik dachte ich, um sicher zu sein, die Dinge zu verbessern. Nun, es hat 6 Lesevorgänge gespart, aber viel mehr CPU gekostet (was Sinn macht). Anstatt die Start/End-Statistiken jeder Zeitscheibe bis zum Ende durchzuziehen, ist es am besten, einfach neu zu berechnen, welche Scheiben mit EXISTS beibehalten werden sollen gegen die Originaldaten. Es kann sein, dass ein unterschiedliches Profil von wenigen Arbeitern mit vielen Jobs die Leistungsstatistiken für verschiedene Abfragen ändern könnte.

Falls es jemand ausprobieren möchte, benutze CREATE TABLE und INSERT Anweisungen von meiner Geige und führen Sie dann dies 11 Mal aus:

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
;

Ich habe zwei andere Lösungen für diese Abfrage erstellt, aber die beste mit etwa der doppelten Leistung hatte einen fatalen Fehler (nicht korrekt mit vollständig eingeschlossenen Zeitbereichen umgehen). Der andere hatte sehr hohe/schlechte Statistiken (was ich wusste, aber ausprobieren musste).

Erklärung

Erstellen Sie unter Verwendung aller Endpunktzeiten aus jeder Zeile eine eindeutige Liste aller möglichen interessierenden Zeitbereiche, indem Sie jede Endpunktzeit duplizieren und dann so gruppieren, dass jede Zeit mit der nächstmöglichen Zeit gepaart wird. Summieren Sie die verstrichenen Minuten dieser Bereiche, wo immer sie mit der tatsächlichen Arbeitszeit eines Arbeiters zusammenfallen.