sql >> Database >  >> RDS >> Sqlserver

Vind de totale tijd die is gewerkt met meerdere taken / bestellingen met overlappende / overlappende tijden voor elke werknemer en taak / bestelling

Deze query doet het werk ook. De prestaties zijn erg goed (hoewel het uitvoeringsplan er niet zo geweldig uitziet, verslaan de eigenlijke CPU en IO vele andere vragen).

Zie hoe het werkt in een 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
;

Met een geclusterde index op WorkerID, JobStart, JobEnd, JobID , en met het voorbeeld van 7 rijen uit de bovenstaande viool, een sjabloon voor nieuwe werknemer-/taakgegevens die vaak genoeg worden herhaald om een ​​tabel met 14.336 rijen op te leveren, zijn hier de prestatieresultaten. Ik heb de andere werkende/goede antwoorden op de pagina toegevoegd (tot nu toe):

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

Ik deed een uitgebreidere test vanaf een andere (langzamere) server (waar elke query 25 keer werd uitgevoerd, de beste en slechtste waarden voor elke statistiek werden weggegooid en de resterende 23 waarden werden gemiddeld) en kreeg het volgende:

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

De alternatieve techniek dacht ik zeker om dingen te verbeteren. Nou, het heeft 6 keer gelezen, maar kost veel meer CPU (wat logisch is). In plaats van de begin-/eindstatistieken van elke tijdslice tot het einde door te voeren, kunt u het beste gewoon opnieuw berekenen welke segmenten u wilt behouden met de EXISTS tegen de originele gegevens. Het kan zijn dat een ander profiel van enkele werknemers met veel banen de prestatiestatistieken voor verschillende zoekopdrachten kan veranderen.

Als iemand het wil proberen, gebruik dan de CREATE TABLE en INSERT uitspraken van mijn viool en voer dit dan 11 keer uit:

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
;

Ik heb twee andere oplossingen voor deze vraag gebouwd, maar de beste met ongeveer het dubbele van de prestaties had een fatale fout (niet correct omgaan met volledig ingesloten tijdbereiken). De andere had zeer hoge/slechte statistieken (wat ik wist maar moest proberen).

Uitleg

Gebruik alle eindpunttijden van elke rij en bouw een duidelijke lijst op van alle mogelijke tijdbereiken van belang door elke eindpunttijd te dupliceren en vervolgens zo te groeperen dat elke keer wordt gekoppeld aan de volgende mogelijke tijd. Tel de verstreken minuten van deze bereiken bij elkaar op, waar ze ook samenvallen met de werkelijke werktijd van een werknemer.



  1. Tijdstempels groeperen in MySQL met PHP

  2. java.math.BigInteger kan niet worden gecast naar java.lang.Integer

  3. Hoe Log() werkt in PostgreSQL

  4. Escape-citaat in HQL-query