sql >> Databasteknik >  >> RDS >> Sqlserver

Hitta total tid arbetad med flera jobb/order med överlappande/överlappande tider på varje arbetare och jobb/order

Den här frågan gör jobbet också. Dess prestanda är mycket bra (medan exekveringsplanen inte ser så bra ut, slår den faktiska CPU:n och IO många andra frågor).

Se hur det fungerar i en Sql-fiol .

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
;

Med ett klustrat index på WorkerID, JobStart, JobEnd, JobID , och med provet 7 rader från ovanstående fiol en mall för nya arbetar-/jobbdata som upprepas tillräckligt många gånger för att ge en tabell med 14 336 rader, här är resultatresultaten. Jag har inkluderat de andra fungerande/rätta svaren på sidan (hittills):

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

Jag gjorde ett mer uttömmande test från en annan (långsammare) server (där varje fråga kördes 25 gånger, de bästa och sämsta värdena för varje mätvärde kastades ut och de återstående 23 värdena beräknades) och fick följande:

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

Den alternativa tekniken tänkte jag vara säker på att förbättra saker och ting. Tja, det sparade 6 läsningar, men kostade mycket mer CPU (vilket är vettigt). Istället för att föra igenom start-/slutstatistiken för varje tidssegment till slutet, är det bäst att bara räkna om vilka segment som ska behållas med EXISTS mot de ursprungliga uppgifterna. Det kan vara så att en annan profil av få arbetare med många jobb kan förändra prestationsstatistiken för olika frågor.

Om någon vill prova, använd CREATE TABLE och INSERT uttalanden från min fiol och kör sedan detta 11 gånger:

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
;

Jag byggde två andra lösningar för den här frågan, men den bästa med ungefär dubbelt så hög prestanda hade ett fatalt fel (hanterade inte helt slutna tidsintervall korrekt). Den andra hade väldigt hög/dålig statistik (vilket jag visste men var tvungen att prova).

Förklaring

Använd alla slutpunktstider från varje rad, bygg upp en distinkt lista över alla möjliga tidsintervall av intresse genom att duplicera varje slutpunktstid och sedan gruppera på ett sådant sätt att varje gång paras ihop med nästa möjliga tid. Summera de förflutna minuterna av dessa intervall var de än sammanfaller med en faktisk arbetares arbetstid.



  1. ladda flera csv till en tabell med SQLLDR

  2. Använd php/MySql medan loop i javascript-objekt

  3. Varför visas bara ett resultat från min fråga?

  4. Rails Migration:Bigint på PostgreSQL verkar misslyckas?