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

Jak zaktualizować statystyki programu SQL Server dla dużych tabel

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.


  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 usunąć pierwsze 1000 wierszy z tabeli za pomocą SQL Server 2008?

  2. Jak działa funkcja CHAR() w SQL Server (T-SQL)

  3. Czy w Microsoft SQL Server istnieje typ danych logicznych, taki jak w MySQL?

  4. Utwórz zadanie agenta programu SQL Server w Azure Data Studio

  5. Domyślna kolejność wierszy w zapytaniu SELECT — SQL Server 2008 vs SQL 2012