W moim poprzednim artykule pokrótce omówiłem statystyki bazy danych, ich znaczenie i dlaczego statystyki powinny być aktualizowane. Ponadto przedstawiłem krok po kroku proces tworzenia planu konserwacji SQL Server w celu aktualizacji statystyk. W tym artykule wyjaśnione zostaną następujące zagadnienia:1. Jak zaktualizować statystyki za pomocą polecenia T-SQL. 2. Jak zidentyfikować często aktualizowane tabele za pomocą T-SQL, a także jak zaktualizować statystyki tabel z często wstawianymi/aktualizowanymi/usuwanymi danymi.
Aktualizacja statystyk za pomocą T-SQL
Możesz aktualizować statystyki za pomocą skryptu T-SQL. Jeśli chcesz aktualizować statystyki za pomocą T-SQL lub SQL Server Management Studio, potrzebujesz bazy danych ALTER uprawnienia do bazy danych. Zobacz przykładowy kod T-SQL, aby zaktualizować statystyki określonej tabeli:
UPDATE STATISTICS <schema_name>.<table_name>.
Rozważmy przykład aktualizacji statystyk OrderLines tabela WideWorldImporters Baza danych. Poniższy skrypt to zrobi.
UPDATE STATISTICS [Sales].[OrderLines]
Jeśli chcesz zaktualizować statystyki określonego indeksu, możesz użyć następującego skryptu:
UPDATE STATISTICS <schema_name>.<table_name> <index_name>
Jeśli chcesz zaktualizować statystyki IX_Sales_OrderLines_Perf_20160301_02 indeks OrderLines tabeli, możesz wykonać następujący skrypt:
UPDATE STATISTICS [Sales].[OrderLines] [IX_Sales_OrderLines_Perf_20160301_02]
Możesz także aktualizować statystyki całej bazy danych. Jeśli masz bardzo małą bazę danych z kilkoma tabelami i niewielką ilością danych, możesz zaktualizować statystyki wszystkich tabel w bazie danych. Zobacz następujący skrypt:
USE wideworldimporters go EXEC Sp_updatestats
Aktualizowanie statystyk tabel z często wstawianymi/aktualizowanymi/usuwanymi danymi
W przypadku dużych baz danych planowanie zadania statystyki staje się skomplikowane, zwłaszcza gdy masz tylko kilka godzin na konserwację indeksu, aktualizację statystyk i wykonanie innych zadań konserwacyjnych. Przez dużą bazę danych mam na myśli bazę, która zawiera tysiące tabel, a każda tabela zawiera tysiące wierszy. Na przykład mamy bazę danych o nazwie X. Zawiera setki tabel, a każda tabela ma miliony wierszy. I tylko kilka tabel jest często aktualizowanych. Inne tabele są rzadko zmieniane i zawierają niewiele transakcji. Jak wspomniałem wcześniej, aby utrzymać wydajność bazy danych na najwyższym poziomie, statystyki tabel muszą być aktualne. Dlatego tworzymy plan konserwacji SQL, aby aktualizować statystyki wszystkich tabel w bazie danych X. Kiedy serwer SQL aktualizuje statystyki tabeli, zużywa znaczną ilość zasobów, co może prowadzić do problemów z wydajnością. Tak więc aktualizacja statystyk setek dużych tabel zajmuje dużo czasu, a gdy statystyki są aktualizowane, wydajność bazy danych znacznie się zmniejsza. W takich okolicznościach zawsze zaleca się aktualizowanie statystyk tylko dla tabel, które są często aktualizowane. Możesz śledzić zmiany ilości danych lub liczby wierszy w czasie, korzystając z następujących dynamicznych widoków zarządzania:1. sys.partitions dostarcza informacji o całkowitej liczbie wierszy w tabeli. 2. sys.dm_db_partition_stats zawiera informacje o liczbie wierszy i liczbie stron na partycję. 3. sys.dm_db_index_physical_stats zawiera informacje o liczbie wierszy i stron, a także informacje o fragmentacji indeksu i nie tylko. Szczegóły dotyczące ilości danych są ważne, ale nie uzupełniają obrazu aktywności bazy danych. Na przykład tabela pomostowa, która ma prawie taką samą liczbę rekordów, może być codziennie usuwana z tabeli lub wstawiana do tabeli. Z tego powodu zrzut liczby wierszy sugerowałby, że tabela jest statyczna. Możliwe, że dodawane i usuwane rekordy mają bardzo różne wartości, które znacznie zmieniają rozkład danych. W takim przypadku automatyczne aktualizowanie statystyk w programie SQL Server sprawia, że statystyki stają się bezsensowne. Dlatego bardzo przydatne jest śledzenie liczby modyfikacji w tabeli. Można to zrobić w następujący sposób:1. rowmodctr kolumna w sys.sysindexes 2. modified_count kolumna w sys.system_internals_partition_columns 3. modification_counter kolumna w sys.dm_db_stats_properties Tak więc, jak wyjaśniłem wcześniej, jeśli masz ograniczony czas na konserwację bazy danych, zawsze zaleca się aktualizowanie statystyk tylko dla tabel z większą częstotliwością zmian danych (wstaw/aktualizuj/usuń). Aby to zrobić sprawnie, stworzyłem skrypt, który aktualizuje statystyki dla „aktywnych” tabel. Skrypt wykonuje następujące zadania:• Deklaruje wymagane parametry • Tworzy tymczasową tabelę o nazwie #tempstatistics do przechowywania nazwy tabeli, nazwy schematu i nazwy bazy danych • Tworzy kolejną tabelę o nazwie #tempdatabase do przechowywania nazwy bazy danych. Najpierw wykonaj następujący skrypt, aby utworzyć dwie tabele:
DECLARE @databasename VARCHAR(500) DECLARE @i INT=0 DECLARE @DBCOunt INT DECLARE @SQLCOmmand NVARCHAR(max) DECLARE @StatsUpdateCOmmand NVARCHAR(max) CREATE TABLE #tempstatistics ( databasename VARCHAR(max), tablename VARCHAR(max), schemaname VARCHAR(max) ) CREATE TABLE #tempdatabases ( databasename VARCHAR(max) ) INSERT INTO #tempdatabases (databasename) SELECT NAME FROM sys.databases WHERE database_id > 4 ORDER BY NAME
Następnie napisz pętlę while, aby utworzyć dynamiczne zapytanie SQL, które przechodzi przez wszystkie bazy danych i wstawia listę tabel, które mają licznik modyfikacji większy niż 200 do #tempstatistics stół. Aby uzyskać informacje o zmianach danych, używam sys.dm_db_stats_properties . Przestudiuj następujący przykład kodu:
SET @DBCOunt=(SELECT Count(*) FROM #tempdatabases) WHILE ( @i < @DBCOunt ) BEGIN DECLARE @DBName VARCHAR(max) SET @DBName=(SELECT TOP 1 databasename FROM #tempdatabases) SET @SQLCOmmand= ' use [' + @DBName + ']; select distinct ''' + @DBName+ ''', a.TableName,a.SchemaName from (SELECT obj.name as TableName, b.name as SchemaName,obj.object_id, stat.name, stat.stats_id, last_updated, modification_counter FROM [' + @DBName+ '].sys.objects AS obj inner join ['+ @DBName + '].sys.schemas b on obj.schema_id=b.schema_id INNER JOIN [' + @DBName+ '].sys.stats AS stat ON stat.object_id = obj.object_id CROSS APPLY [' + @DBName+'].sys.dm_db_stats_properties(stat.object_id, stat.stats_id) AS sp WHERE modification_counter > 200 and obj.name not like ''sys%''and b.name not like ''sys%'')a' INSERT INTO #tempstatistics (databasename, tablename, schemaname) EXEC Sp_executesql @SQLCOmmand
Teraz utwórz drugą pętlę w pierwszej pętli. Wygeneruje dynamiczne zapytanie SQL, które zaktualizuje statystyki przy pełnym skanowaniu. Zobacz poniższy przykład kodu:
DECLARE @j INT=0 DECLARE @StatCount INT SET @StatCount =(SELECT Count(*) FROM #tempstatistics) WHILE @J < @StatCount BEGIN DECLARE @DatabaseName_Stats VARCHAR(max) DECLARE @Table_Stats VARCHAR(max) DECLARE @Schema_Stats VARCHAR(max) DECLARE @StatUpdateCommand NVARCHAR(max) SET @DatabaseName_Stats=(SELECT TOP 1 databasename FROM #tempstatistics) SET @Table_Stats=(SELECT TOP 1 tablename FROM #tempstatistics) SET @Schema_Stats=(SELECT TOP 1 schemaname FROM #tempstatistics) SET @StatUpdateCommand='Update Statistics [' + @DatabaseName_Stats + '].[' + @Schema_Stats + '].[' + @Table_Stats + '] with fullscan' EXEC Sp_executesql @StatUpdateCommand SET @[email protected] + 1 DELETE FROM #tempstatistics WHERE databasename = @DatabaseName_Stats AND tablename = @Table_Stats AND schemaname = @Schema_Stats END SET @[email protected] + 1 DELETE FROM #tempdatabases WHERE databasename = @DBName END
Po zakończeniu wykonywania skryptu usunie wszystkie tabele tymczasowe.
SELECT * FROM #tempstatistics DROP TABLE #tempdatabases DROP TABLE #tempstatistics
Cały skrypt będzie wyglądał następująco:
--set count on CREATE PROCEDURE Statistics_maintenance AS BEGIN DECLARE @databasename VARCHAR(500) DECLARE @i INT=0 DECLARE @DBCOunt INT DECLARE @SQLCOmmand NVARCHAR(max) DECLARE @StatsUpdateCOmmand NVARCHAR(max) CREATE TABLE #tempstatistics ( databasename VARCHAR(max), tablename VARCHAR(max), schemaname VARCHAR(max) ) CREATE TABLE #tempdatabases ( databasename VARCHAR(max) ) INSERT INTO #tempdatabases (databasename) SELECT NAME FROM sys.databases WHERE database_id > 4 ORDER BY NAME SET @DBCOunt=(SELECT Count(*) FROM #tempdatabases) WHILE ( @i < @DBCOunt ) BEGIN DECLARE @DBName VARCHAR(max) SET @DBName=(SELECT TOP 1 databasename FROM #tempdatabases) SET @SQLCOmmand= ' use [' + @DBName + ']; select distinct ''' + @DBName+ ''', a.TableName,a.SchemaName from (SELECT obj.name as TableName, b.name as SchemaName,obj.object_id, stat.name, stat.stats_id, last_updated, modification_counter FROM [' + @DBName+ '].sys.objects AS obj inner join ['+ @DBName + '].sys.schemas b on obj.schema_id=b.schema_id INNER JOIN [' + @DBName+ '].sys.stats AS stat ON stat.object_id = obj.object_id CROSS APPLY [' + @DBName+'].sys.dm_db_stats_properties(stat.object_id, stat.stats_id) AS sp WHERE modification_counter > 200 and obj.name not like ''sys%''and b.name not like ''sys%'')a' INSERT INTO #tempstatistics (databasename, tablename, schemaname) EXEC Sp_executesql @SQLCOmmand DECLARE @j INT=0 DECLARE @StatCount INT SET @StatCount =(SELECT Count(*) FROM #tempstatistics) WHILE @J < @StatCount BEGIN DECLARE @DatabaseName_Stats VARCHAR(max) DECLARE @Table_Stats VARCHAR(max) DECLARE @Schema_Stats VARCHAR(max) DECLARE @StatUpdateCommand NVARCHAR(max) SET @DatabaseName_Stats=(SELECT TOP 1 databasename FROM #tempstatistics) SET @Table_Stats=(SELECT TOP 1 tablename FROM #tempstatistics) SET @Schema_Stats=(SELECT TOP 1 schemaname FROM #tempstatistics) SET @StatUpdateCommand='Update Statistics [' + @DatabaseName_Stats + '].[' + @Schema_Stats + '].[' + @Table_Stats + '] with fullscan' EXEC Sp_executesql @StatUpdateCommand SET @[email protected] + 1 DELETE FROM #tempstatistics WHERE databasename = @DatabaseName_Stats AND tablename = @Table_Stats AND schemaname = @Schema_Stats END SET @[email protected] + 1 DELETE FROM #tempdatabases WHERE databasename = @DBName END SELECT * FROM #tempstatistics DROP TABLE #tempdatabases DROP TABLE #tempstatistics END
Możesz również zautomatyzować ten skrypt, tworząc zadanie SQL Server Agent, które wykona je w zaplanowanym czasie. Instrukcja krok po kroku dotycząca automatyzacji tej pracy jest podana poniżej.
Tworzenie zadania SQL
Najpierw utwórzmy zadanie SQL, aby zautomatyzować proces. Aby to zrobić, otwórz SSMS, połącz się z żądanym serwerem i rozwiń SQL Server Agent, kliknij prawym przyciskiem myszy Praca i wybierz Nowa oferta pracy . W Nowej pracy w oknie dialogowym wpisz żądaną nazwę w polu Nazwa pole. Teraz kliknij Kroki opcja menu w lewym panelu Nowa oferta w oknie dialogowym, a następnie kliknij Nowy w Krokach okno. W kroku Nowa praca w wyświetlonym oknie dialogowym podaj żądaną nazwę w Nazwa kroku pole. Następnie wybierz Skrypt Transact-SQL (T-SQL) w Typie upuścić pudło. Następnie wybierz DBATools w Bazie danych rozwijanej listy i wpisz następujące zapytanie w polu tekstowym polecenia:
EXEC Statistics_maintenance
Aby skonfigurować harmonogram zadania, kliknij Harmonogramy opcja menu w Nowej pracy Okno dialogowe. Nowy harmonogram prac otworzy się okno dialogowe. W Nazwie wprowadź żądaną nazwę harmonogramu. W naszym przykładzie chcemy, aby to zadanie było wykonywane każdej nocy o 1 w nocy, stąd w Występuje menu rozwijane w Częstotliwości sekcji, wybierz Codziennie . W Występuje raz o pole w Częstotliwości dziennej sekcji, wprowadź 01:00:00. Kliknij OK aby zamknąć Nowy harmonogram pracy okno, a następnie kliknij OK ponownie w Nowej pracy okno dialogowe, aby je zamknąć. Teraz przetestujmy tę pracę. W obszarze Agent serwera SQL kliknij prawym przyciskiem myszy Aktualizuj_statystyki_dziennie . Jeśli zadanie zostało wykonane pomyślnie, zobaczysz następujące okno.
Podsumowanie
W tym artykule omówiono następujące zagadnienia:1. Jak zaktualizować statystyki tabel przy użyciu skryptu T-SQL. 2. Jak uzyskać informacje o zmianach ilości danych i częstotliwości zmian danych. 3. Jak stworzyć skrypt aktualizujący statystyki aktywnych tabel. 4. Jak utworzyć zadanie agenta SQL Server, aby wykonać skrypt w zaplanowanym czasie.