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

Partycjonowanie skutkuje uruchomieniem zapytania podsumowującego

Jeśli nie musisz PRZECHOWYWAĆ danych (czego nie powinieneś, ponieważ musisz aktualizować sumy bieżące za każdym razem, gdy jakikolwiek wiersz zostanie zmieniony, dodany lub usunięty), i jeśli nie ufasz dziwacznej aktualizacji (którą nie powinno, ponieważ nie ma gwarancji, że będzie działać, a jego zachowanie może ulec zmianie po wprowadzeniu poprawki, dodatku Service Pack, uaktualnienia, a nawet podstawowego indeksu lub zmiany statystyk), można wypróbować ten typ zapytania w czasie wykonywania. Jest to metoda, którą inny MVP, Hugo Kornelis, wymyślił „iteracja oparta na zbiorach” (opublikował coś podobnego w jednym ze swoich rozdziałów Głębokie nurkowania SQL Server MVP ). Ponieważ uruchamianie sumowania zazwyczaj wymaga najechania kursorem na cały zestaw, dziwacznej aktualizacji całego zestawu lub pojedynczego nieliniowego łączenia, które staje się coraz droższe wraz ze wzrostem liczby wierszy, sztuczka polega na tym, aby przejść przez pewną skończoną liczbę wierszy. element w zestawie (w tym przypadku "ranga" każdego wiersza w ujęciu miesięcznym, dla każdego użytkownika - a każdą pozycję przetwarzasz tylko raz dla wszystkich kombinacji użytkownik/miesiąc na tej pozycji, więc zamiast przeskakiwać przez 200 000 wierszy, zapętlisz do 24 razy).

DECLARE @t TABLE
(
  [user_id] INT, 
  [month] TINYINT,
  total DECIMAL(10,1), 
  RunningTotal DECIMAL(10,1), 
  Rnk INT
);

INSERT @t SELECT [user_id], [month], total, total, 
  RANK() OVER (PARTITION BY [user_id] ORDER BY [month]) 
  FROM dbo.my_table;

DECLARE @rnk INT = 1, @rc INT = 1;

WHILE @rc > 0
BEGIN
  SET @rnk += 1;

  UPDATE c SET RunningTotal = p.RunningTotal + c.total
    FROM @t AS c INNER JOIN @t AS p
    ON c.[user_id] = p.[user_id]
    AND p.rnk = @rnk - 1
    AND c.rnk = @rnk;

  SET @rc = @@ROWCOUNT;
END

SELECT [user_id], [month], total, RunningTotal
FROM @t
ORDER BY [user_id], rnk;

Wyniki:

user_id  month   total   RunningTotal
-------  -----   -----   ------------
1        1       2.0     2.0
1        2       1.0     3.0
1        3       3.5     6.5 -- I think your calculation is off
2        1       0.5     0.5
2        2       1.5     2.0
2        3       2.0     4.0

Oczywiście możesz zaktualizować tabelę bazową z tej zmiennej tabeli, ale po co się męczyć, skoro te przechowywane wartości są dobre tylko do następnego razu, gdy tabela zostanie dotknięta przez jakiekolwiek polecenie DML?

UPDATE mt
  SET cumulative_total = t.RunningTotal
  FROM dbo.my_table AS mt
  INNER JOIN @t AS t
  ON mt.[user_id] = t.[user_id]
  AND mt.[month] = t.[month];

Ponieważ nie polegamy na jakiejkolwiek niejawnej kolejności, jest to w 100% obsługiwane i zasługuje na porównanie wydajności w stosunku do nieobsługiwanej dziwacznej aktualizacji. Nawet jeśli nie pobije go, ale jest blisko, powinieneś rozważyć użycie go mimo to IMHO.

Jeśli chodzi o rozwiązanie SQL Server 2012, Matt wspomina o RANGE ale ponieważ ta metoda wykorzystuje buforowanie na dysku, powinieneś również przetestować z ROWS zamiast po prostu biegać z RANGE . Oto krótki przykład Twojej sprawy:

SELECT
  [user_id],
  [month],
  total,
  RunningTotal = SUM(total) OVER 
  (
    PARTITION BY [user_id] 
    ORDER BY [month] ROWS UNBOUNDED PRECEDING
  )
FROM dbo.my_table
ORDER BY [user_id], [month];

Porównaj to z RANGE UNBOUNDED PRECEDING lub nie ROWS\RANGE w ogóle (co również użyje RANGE na dysku). Powyższe będzie miało krótszy ogólny czas trwania i sposób mniej I/O, mimo że plan wygląda na nieco bardziej złożony (dodatkowy operator sekwencji projektu).

Niedawno opublikowałem post na blogu, w którym przedstawiłem pewne różnice w wydajności, które zaobserwowałem w przypadku konkretnego scenariusza sumowania bieżącego:

http://www.sqlperformance.com/2012/07 /t-sql-queries/running-totals



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak wybrać określoną liczbę znaków z lewej lub prawej strony ciągu w SQL Server?

  2. Jak konwertować między formatami dat w SQL Server za pomocą CAST()

  3. Jak dodać ograniczenie klucza obcego do istniejącej tabeli w SQL Server (T-SQL)

  4. Czy klucz obcy automatycznie tworzy indeks?

  5. Jak mogę usunąć zduplikowane wiersze?