Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Znajdź całkowity czas przepracowany z wieloma zadaniami / zamówieniami z nakładającymi się / nakładającymi się czasami dla każdego pracownika i zadania / zamówienia

To zapytanie również spełnia swoje zadanie. Jego wydajność jest bardzo dobra (podczas gdy plan wykonania nie wygląda tak dobrze, rzeczywisty procesor i IO pokonają wiele innych zapytań).

Zobacz, jak działa na skrzypcach Sql .

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
;

Z indeksem klastrowym na WorkerID, JobStart, JobEnd, JobID , a przy próbie 7 wierszy z powyższego szablonu dla danych nowego pracownika/zadania powtórzonych wystarczająco wiele razy, aby uzyskać tabelę z 14 336 wierszami, oto wyniki dotyczące wydajności. Na stronie zamieściłem inne działające/poprawne odpowiedzi (jak dotąd):

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

Przeprowadziłem bardziej wyczerpujący test z innego (wolniejszego) serwera (gdzie każde zapytanie zostało uruchomione 25 razy, najlepsze i najgorsze wartości dla każdej metryki zostały odrzucone, a pozostałe 23 wartości zostały uśrednione) i uzyskałem następujące wyniki:

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

Pomyślałem, że alternatywna technika na pewno poprawi sytuację. Cóż, zaoszczędził 6 odczytów, ale kosztował znacznie więcej procesora (co ma sens). Zamiast przenosić statystyki początku/końca każdego kawałka czasu do końca, najlepiej jest po prostu ponownie obliczyć, które wycinki należy zachować z EXISTS w stosunku do oryginalnych danych. Może się zdarzyć, że inny profil kilku pracowników z wieloma zadaniami może zmienić statystyki wydajności dla różnych zapytań.

Jeśli ktoś chce spróbować, użyj CREATE TABLE i INSERT wypowiedzi z moich skrzypiec, a następnie uruchom to 11 razy:

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
;

Zbudowałem dwa inne rozwiązania dla tego zapytania, ale najlepsze z około dwukrotnie większą wydajnością miało fatalną wadę (nie poprawnie obsługuje w pełni zamknięte zakresy czasowe). Drugi miał bardzo wysokie/złe statystyki (które wiedziałem, ale musiałem spróbować).

Wyjaśnienie

Korzystając ze wszystkich czasów punktów końcowych z każdego wiersza, utwórz odrębną listę wszystkich możliwych interesujących zakresów czasowych, powielając każdy czas punktu końcowego, a następnie grupując w taki sposób, aby za każdym razem sparować z następnym możliwym czasem. Zsumuj minuty, które upłynęły z tych zakresów, gdziekolwiek pokrywają się one z rzeczywistym czasem pracy pracownika.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Naucz się projektowania baz danych w SQL Server Management Studio (SSMS) — część 2

  2. Jak napisać foreach w SQL Server?

  3. Używanie sp_help_schedule w SQL Server

  4. Pomiń/ignoruj ​​zduplikowane wiersze podczas wstawiania

  5. Wewnętrzne łączenie trzech stołów