sql >> Databasteknik >  >> RDS >> Sqlserver

Hur man uppdaterar SQL Server-statistik för stora tabeller

I min tidigare artikel har jag kortfattat tagit upp databasstatistik, dess betydelse och varför statistik bör uppdateras. Dessutom har jag demonstrerat en steg-för-steg-process för att skapa en SQL Server-underhållsplan för att uppdatera statistik. I den här artikeln kommer följande problem att förklaras:1. Hur man uppdaterar statistik med T-SQL Command. 2. Hur man identifierar de ofta uppdaterade tabellerna med T-SQL och även hur man uppdaterar statistiken för tabeller med ofta infogade/uppdaterade/raderade data.

Uppdatera statistik med T-SQL

Du kan uppdatera statistik med T-SQL-skriptet. Om du vill uppdatera statistik med T-SQL eller SQL Server Management Studio behöver du ALTER databas behörighet på databasen. Se T-SQL-kodexemplet för att uppdatera statistiken för en specifik tabell:

UPDATE STATISTICS <schema_name>.<table_name>.

Låt oss överväga exemplet med att uppdatera statistiken för OrderLines tabellen för WideWorldImporters databas. Följande skript kommer att göra det.

UPDATE STATISTICS [Sales].[OrderLines]

Om du vill uppdatera statistiken för ett specifikt index kan du använda följande skript:

UPDATE STATISTICS <schema_name>.<table_name> <index_name>

Om du vill uppdatera statistiken för IX_Sales_OrderLines_Perf_20160301_02 index för OrderLines tabell kan du köra följande skript:

UPDATE STATISTICS [Sales].[OrderLines] [IX_Sales_OrderLines_Perf_20160301_02]

Du kan också uppdatera statistiken för hela databasen. Om du har en mycket liten databas med få tabeller och liten mängd data kan du uppdatera statistiken för alla tabeller i en databas. Se följande skript:

USE wideworldimporters 
go 
EXEC Sp_updatestats

Uppdatera statistik för tabeller med ofta infogade / uppdaterade / raderade data

På stora databaser blir det komplicerat att schemalägga statistikjobbet, särskilt när du bara har några timmar på dig att utföra indexunderhåll, uppdatera statistik och utföra andra underhållsuppgifter. Med en stor databas menar jag en databas som innehåller tusentals tabeller och varje tabell innehåller tusentals rader. Till exempel har vi en databas som heter X. Den har hundratals tabeller och varje tabell har miljontals rader. Och bara ett fåtal tabeller uppdateras ofta. Andra tabeller ändras sällan och har mycket få transaktioner utförda på dem. Som jag nämnde tidigare, för att hålla databasens prestanda upp till märket, måste tabellstatistiken vara uppdaterad. Så vi skapar en SQL-underhållsplan för att uppdatera statistiken för alla tabeller i X-databasen. När SQL-servern uppdaterar statistiken för en tabell använder den en betydande mängd resurser vilket kan leda till prestandaproblem. Så det tar lång tid att uppdatera statistiken för hundratals stora tabeller och medan statistiken uppdateras minskar databasens prestanda avsevärt. Under sådana omständigheter är det alltid tillrådligt att uppdatera statistiken endast för de tabeller som uppdateras ofta. Du kan hålla reda på förändringar i datavolym eller antal rader över tiden genom att använda följande dynamiska hanteringsvyer:1. sys.partitions ger information om det totala antalet rader i en tabell. 2. sys.dm_db_partition_stats ger information om radantal och sidantal per partition. 3. sys.dm_db_index_physical_stats ger information om antalet rader och sidor, plus information om indexfragmentering med mera. Detaljerna om datavolymen är viktiga, men de gör inte bilden av databasaktivitet komplett. Till exempel kan en mellanställningstabell som har nästan samma antal poster tas bort från tabellen eller infogas i en tabell varje dag. På grund av det skulle en ögonblicksbild av antalet rader tyda på att tabellen är statisk. Det kan vara möjligt att de poster som läggs till och raderas har väldigt olika värden som förändrar datafördelningen kraftigt. I det här fallet gör automatisk uppdatering av statistik i SQL Server statistik meningslös. Därför är det mycket användbart att spåra antalet ändringar av en tabell. Detta kan göras på följande sätt:1. rowmodctr kolumn i sys.sysindexes 2. modified_count kolumn i sys.system_internals_partition_columns 3. modification_counter kolumn i sys.dm_db_stats_properties Således, som jag förklarade tidigare, om du har begränsad tid för databasunderhåll, är det alltid lämpligt att uppdatera statistiken endast för tabellerna med en högre frekvens av dataändring (infoga / uppdatera / ta bort). För att göra det effektivt har jag skapat ett skript som uppdaterar statistiken för de "aktiva" tabellerna. Skriptet utför följande uppgifter:• Deklarerar de nödvändiga parametrarna • Skapar en tillfällig tabell med namnet #tempstatistics för att lagra tabellnamn, schemanamn och databasnamn • Skapar en annan tabell med namnet #tempdatabase för att lagra databasnamnet. Kör först följande skript för att skapa två tabeller:

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

Skriv sedan en while-loop för att skapa en dynamisk SQL-fråga som itererar genom alla databaser och infogar en lista med tabeller som har en modifieringsräknare som är större än 200 i #tempstatistics tabell. För att få information om dataändringar använder jag sys.dm_db_stats_properties . Studera följande kodexempel:

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

Skapa nu den andra slingan inom den första slingan. Det kommer att generera en dynamisk SQL-fråga som uppdaterar statistiken med full scan. Se kodexemplet nedan:

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

När skriptkörningen är klar kommer alla tillfälliga tabeller att tas bort.

SELECT * 
    FROM   #tempstatistics 
    DROP TABLE #tempdatabases 
    DROP TABLE #tempstatistics

Hela skriptet kommer att se ut enligt följande:

--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

Du kan också automatisera det här skriptet genom att skapa ett SQL Server Agent-jobb som kör det vid en schemalagd tidpunkt. En steg-för-steg-instruktion för att automatisera detta jobb ges nedan.

Skapa ett SQL-jobb

Låt oss först skapa ett SQL Job för att automatisera processen. För att göra det, öppna SSMS, anslut till önskad server och expandera SQL Server Agent, högerklicka på Jobb och välj Nytt jobb . I Nya jobbet i dialogrutan, skriv önskat namn i Namn fält. Klicka nu på Steg menyalternativet på den vänstra panelen i Nytt jobb dialogrutan och klicka sedan på Ny i stegen fönster. I steget Nytt jobb dialogrutan som öppnas, ange önskat namn i Stegnamn fält. Välj sedan Transact-SQL-skript (T-SQL) i Typ rullgardinsmenyn. Välj sedan DBATools i databasen rullgardinsmenyn och skriv följande fråga i kommandotextrutan:

EXEC Statistics_maintenance

För att konfigurera schemat för jobbet klickar du på Schedules menyalternativet i Nytt jobb dialog ruta. Nytt jobbschema dialogrutan öppnas. I Namn fältet, ange önskat schemanamn. I vårt exempel vill vi att det här jobbet ska utföras varje kväll klockan 01.00, därför i Uppstår rullgardinsmenyn i Frekvens väljer du Dagligen . I avsnittet Förekommer en gång på i fältet Daglig frekvens sektion, ange 01:00:00. Klicka på OK för att stänga Nytt jobbschema fönstret och klicka sedan på OK igen i Nytt jobb dialogrutan för att stänga den. Låt oss nu testa det här jobbet. Under SQL Server Agent högerklickar du på Update_Statistics_Daily . Om jobbet har utförts framgångsrikt kommer du att se följande fönster.

Sammanfattning

I den här artikeln har följande frågor behandlats:1. Hur man uppdaterar statistiken för tabeller med T-SQL-skript. 2. Hur man får information om förändringar i datavolym och frekvens av dataändringar. 3. Hur man skapar skriptet som uppdaterar statistik på aktiva tabeller. 4. Hur man skapar ett SQL Server Agent Job för att exekvera skriptet vid den schemalagda tiden.


  1. Oracle Sequence nextval hoppar nummer fram och tillbaka

  2. PostgreSQL inaktivera mer utdata

  3. Hur man bestämmer det totala antalet öppna/aktiva anslutningar i ms sql server 2005

  4. Hur dekrypterar man ett lösenord från SQL-servern?