Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Một lý do khác để tránh sp_updatestats

Trước đây tôi đã viết blog về lý do tại sao tôi không yêu sp_updatestats. Gần đây tôi đã tìm ra một lý do khác rằng đó không phải là bạn của tôi. TL; DR:Nó không cập nhật thống kê về các chế độ xem được lập chỉ mục. Bây giờ, tài liệu không khẳng định rằng nó có, vì vậy không có lỗi ở đây. Tài liệu MSDN nêu rõ:

Chạy THỐNG KÊ CẬP NHẬT dựa trên tất cả các bảng nội bộ và do người dùng xác định trong cơ sở dữ liệu hiện tại.

Nhưng… có bao nhiêu người trong số các bạn nghĩ về các lượt xem được lập chỉ mục của mình và tự hỏi liệu những lượt xem đó đã được cập nhật chưa? Tôi thừa nhận là tôi đã không. Tôi quên mất các chế độ xem được lập chỉ mục, điều này thật đáng tiếc vì chúng có thể thực sự mạnh mẽ khi được sử dụng một cách thích hợp. Chúng cũng có thể là một cơn ác mộng để làm sáng tỏ khi bạn đang khắc phục sự cố, nhưng tôi sẽ không tranh luận về việc sử dụng chúng hôm nay. Tôi chỉ muốn bạn biết rằng chúng không được cập nhật bởi sp_updatestats và hãy xem bạn có những tùy chọn nào.

Thiết lập

Vì Giải Thế giới vừa kết thúc, chúng tôi sẽ sử dụng cơ sở dữ liệu Bóng chày để thử nghiệm. Bạn có thể tải xuống từ trang Tài nguyên SQLskills. Sau khi khôi phục, chúng tôi sẽ tạo một bản sao của bảng dbo.Players, có tên là dbo.PlayerInfo, tải một vài nghìn hàng vào đó, sau đó tạo một dạng xem được lập chỉ mục kết hợp bảng mới của chúng tôi với bảng PitchingPost:

 SỬ DỤNG [BaseballData]; ĐI TẠO BẢNG [dbo]. [PlayerInfo] ([lahmanID] [int] NOT NULL, [playerID] [varchar] (10) NULL DEFAULT (NULL), [managerID] [varchar] ( 10) NULL DEFAULT (NULL), [hofID] [varchar] (10) NULL DEFAULT (NULL), [BirthYear] [int] NULL DEFAULT (NULL), [BirthMonth] [int] NULL DEFAULT (NULL), [BirthDay] [int] NULL DEFAULT (NULL), [BirthCountry] [varchar] (50) NULL DEFAULT (NULL), [BirthState] [varchar] (2) NULL DEFAULT (NULL), [BirthCity] [varchar] (50) NULL DEFAULT (NULL), [deathYear] [int] NULL DEFAULT (NULL), [deathMonth] [int] NULL DEFAULT (NULL), [deathDay] [int] NULL DEFAULT (NULL), [deathCountry] [varchar] (50) NULL DEFAULT (NULL), [deathState] [varchar] (2) NULL DEFAULT (NULL), [deathCity] [varchar] (50) NULL DEFAULT (NULL), [nameFirst] [varchar] (50) NULL DEFAULT (NULL), [nameLast] [varchar] (50) NULL DEFAULT (NULL), [nameNote] [varchar] (255) NULL DEFAULT (NULL), [nameGiven] [varchar] (255) NULL DEFAULT (NULL), [nameNick] [varchar ] (255) NULL DEFAULT (NULL), [weight] [int] NULL DEFAULT (NULL), [height] [int] NULL, [bats] [varchar] (1) NULL DEFAULT (NULL), [throws] [varchar] (1) NULL DEFAULT (NULL), [debut] [varchar] ( 10) NULL DEFAULT (NULL), [finalGame] [varchar] (10) NULL DEFAULT (NULL), [college] [varchar] (50) NULL DEFAULT (NULL), [lahman40ID] [varchar] (9) NULL DEFAULT ( NULL), [lahman45ID] [varchar] (9) NULL DEFAULT (NULL), [retroID] [varchar] (9) NULL DEFAULT (NULL), [holtzID] [varchar] (9) NULL DEFAULT (NULL), [bbrefID ] [varchar] (9) KHÔNG CÓ ĐỊNH NGHĨA (NULL), KHÓA CHÍNH ĐÃ ĐIỀU CHỈNH ([lahmanID] ASC) BẬT [CHÍNH]) BẬT [CHÍNH]; ĐI CHÈN VÀO [dbo]. [PlayerInfo] ([lahmanID], [playerID] , [managerID], [hofID], [BirthYear], [BirthMonth], [BirthDay], [BirthCountry], [BirthState], [BirthCity], [deathYear], [deathMonth], [deathDay], [deathCountry], [ deathState], [deathCity], [nameFirst], [nameLast] , [nameNote], [nameGiven], [nameNick], [weight], [height], [bats], [throws], [debut], [finalGame], [college], [lahman40ID], [lahman45ID], [ retroID], [holtzID], [bbrefID]) CHỌN [lahmanID], [playerID], [managerID], [hofID], [BirthYear], [BirthMonth], [BirthDay], [BirthCountry], [BirthState], [BirthCity ], [deathYear], [deathMonth], [deathDay], [deathCountry], [deathState], [deathCity], [nameFirst], [nameLast], [nameNote], [nameGiven], [nameNick], [weight], [height], [bats], [throws], [debut], [finalGame], [college], [lahman40ID], [lahman45ID], [retroI D], [holtzID], [bbrefID] FROM [dbo]. [Người chơi] WHERE [lahmanID] <=10000; TẠO CHẾ ĐỘ XEM [PlayerPostSeason] VỚI SCHEMABINDINGAS SELECT [p]. [LahmanID], [p]. [NameFirst], [p]. [NameLast], [p]. [Debut], [p]. [FinalGame], [pp ]. [yearID], [pp]. [round], [pp]. [teamID], [pp]. [w], [pp]. [l], [pp]. [g] FROM [dbo]. [PlayerInfo] [p] JOIN [dbo]. [PitchingPost] [pp] ON [p]. [PlayerID] =[pp]. [PlayerID]; TẠO CHỈ SỐ ĐƯỢC ĐIỀU CHỈNH DUY NHẤT [CI_PlayerPostSeason] ON [PlayerPostSeason] ([lahmanID], [yearID], [round]); TẠO CHỈ SỐ KHÔNG ĐƯỢC ĐIỀU CHỈNH [NCI_PlayerPostSeason_Name] BẬT [PlayerPostSeason] ([nameFirst], [nameLast]); 

Nếu chúng tôi kiểm tra số liệu thống kê cho các chỉ mục nhóm và không gộp, chúng tôi thấy chúng tồn tại:

 DBCC SHOW_STATISTICS ('PlayerPostSeason', CI_PlayerPostSeason) VỚI STAT_HEADER; GODBCC SHOW_STATISTICS ('PlayerPostSeason', NCI_PlayerPostSeason_Name) VỚI STAT_HEADER; ĐI 

Thống kê lượt xem chỉ mục sau khi tạo ban đầu

Bây giờ chúng ta sẽ chèn nhiều hàng hơn vào PlayerInfo:

 CHÈN VÀO [dbo]. [PlayerInfo] ([lahmanID], [playerID], [managerID], [hofID], [BirthYear], [BirthMonth], [BirthDay], [BirthCountry], [BirthState], [ BornCity], [deathYear], [deathMonth], [deathDay], [deathCountry], [deathState], [deathCity], [nameFirst], [nameLast], [nameNote], [nameGiven], [nameNick], [weight] , [height], [bats], [throws], [debut], [finalGame], [college], [lahman40ID], [lahman45ID], [retroID], [holtzID], [bbrefID]) CHỌN [lahmanID], [playerID], [managerID], [hofID], [BirthYear], [BirthMonth], [BirthDay], [BirthCountry], [BirthState], [BirthCity] , [deathYear], [deathMonth], [deathDay], [deathCountry], [deathState], [deathCity], [nameFirst], [nameLast], [nameNote], [nameGiven], [nameNick], [weight], [ height], [bats], [throws], [debut], [finalGame], [college], [lahman40ID], [lahman45ID], [retroID], [holtzID], [bbrefID] FROM [dbo]. [Người chơi] WHERE [lahmanID]> 10000; 

Và nếu chúng tôi kiểm tra sys.dm_db_stats_properties, chúng tôi có thể thấy các sửa đổi hàng:

 SELECT [sch]. [name] AS [Schema], [so]. [name] AS [ObjectName], [so]. [type] AS [ObjectType], [ss]. [name] AS [Statistic ], [sp]. [last_updated] AS [StatsLastUpdated], [sp]. [lines] AS [RowsInTable], [sp]. [rows_sampled] AS [RowsSampled], [sp]. [modification_counter] AS [RowModification] FROM [sys]. [objects] [so] JOIN [sys]. [stats] [ss] ON [so]. [object_id] =[ss]. [object_id] JOIN [sys]. [schemas] [sch] ON [ so]. [schema_id] =[sch]. [schema_id] NGOÀI RA ÁP DỤNG [sys]. [dm_db_stats_properties] ([so]. [object_id], [ss]. [stats_id]) spWHERE [so]. [name] =' PlayerPostSeason '; 

Các hàng được sửa đổi trong chế độ xem được lập chỉ mục, thông qua sys.dm_db_stats_properties

Và vui thôi, nếu chúng tôi kiểm tra sys.sysindexes, chúng tôi cũng có thể thấy các sửa đổi ở đó:

 SELECT [so]. [name], [si]. [name], [si]. [rowcnt], [si]. [rowmodctr] FROM [sys]. [sysindexes] [si] JOIN [sys] . [object] [so] ON [si]. [id] =[so]. [object_id] WHERE [so]. [name] ='PlayerPostSeason'; 

Các hàng được sửa đổi trong chế độ xem được lập chỉ mục, thông qua sys.sysindexes

Bây giờ sys.sysindexes không được dùng nữa, nhưng nếu bạn nhớ từ bài viết trước của tôi, đó là những gì sp_updatestats sử dụng để xem những gì đã được sửa đổi. Nhưng… danh sách đối tượng cho sys.indexes được điều khiển bởi truy vấn chống lại sys.objects, nếu bạn nhớ, sẽ lọc trên bảng người dùng ('U') và bảng nội bộ ('CNTT'). Nó không bao gồm các chế độ xem ('V') trong bộ lọc đó. Do đó, khi chúng tôi chạy sp_updatestats và kiểm tra kết quả (không bao gồm cho ngắn gọn), sẽ không có đề cập đến chế độ xem PlayerPostSeason của chúng tôi.

Do đó, nếu bạn có các lượt xem được lập chỉ mục và bạn đang dựa vào sp_updatestats để cập nhật thống kê của mình, thì số liệu thống kê về lượt xem của bạn sẽ không được cập nhật. Tuy nhiên, tôi đoán rằng hầu hết các bạn đã bật tùy chọn Thống kê Tự động Cập nhật cho cơ sở dữ liệu của mình. Điều này là tốt, bởi vì với tùy chọn này, thống kê chế độ xem sẽ cập nhật nếu chúng đã bị vô hiệu. Chúng tôi biết rằng chúng tôi đã thực hiện hơn 2000 sửa đổi đối với các chỉ mục trên PlayerPostSeason. Nếu chúng tôi truy vấn theo tên có chọn lọc, thì kế hoạch truy vấn của chúng tôi nên sử dụng chỉ mục NCI_PlayerPostSeason_Name và bởi vì thống kê đã lỗi thời, chúng sẽ được cập nhật. Hãy kiểm tra:

 CHỌN * TỪ [PlayerPostSeason] WHERE [nameFirst] ='Madison'; ĐI 

Kế hoạch truy vấn từ SELECT so với chỉ mục không phân phối

Chúng tôi có thể thấy trong kế hoạch rằng chỉ mục không có trong NCI_PlayerPostSeason_Name đã được sử dụng và nếu chúng tôi kiểm tra thống kê:

Thống kê sau khi cập nhật tự động

Chắc chắn, số liệu thống kê cho chỉ mục không phân biệt đã được cập nhật. Nhưng tất nhiên chúng tôi không muốn dựa vào cập nhật tự động để quản lý số liệu thống kê, chúng tôi muốn chủ động. Chúng tôi có hai lựa chọn:

  • Nhiệm vụ Bảo trì
  • Tập lệnh tùy chỉnh

Nhiệm vụ bảo trì thống kê cập nhật hiện cập nhật số liệu thống kê lượt xem. Điều này không được gọi cụ thể ở bất kỳ đâu trong giao diện người dùng, nhưng nếu chúng tôi tạo kế hoạch bảo trì với tác vụ thống kê cập nhật và chạy nó, thống kê cho chế độ xem được lập chỉ mục sẽ được cập nhật. Hạn chế của nhiệm vụ bảo trì số liệu thống kê cập nhật là đó là một cách tiếp cận búa tạ. Nó cập nhật tất cả thống kê, bất kể nó có cần thiết hay không (nó gần như tệ như sp_updatestats). Tôi thích một tập lệnh tùy chỉnh, trong đó SQL Server chỉ cập nhật những gì đã được sửa đổi. Nếu bạn không thích biên tập kịch bản của riêng mình, bạn có thể sử dụng tập lệnh của Ola Hallengren. Việc cập nhật số liệu thống kê là một phần của việc xây dựng lại và sắp xếp lại chỉ mục của bạn. Ví dụ:với tập lệnh của Ola trong công việc SQL Agent, bạn sẽ có:

sqlcmd -E -S $ (ESCAPE_SQUOTE (SRVR)) -d master -Q "EXECUTE [dbo]. [IndexOptimize] @Databases ='BaseballData', @FragmentationLow =NULL, @FragmentationMedium ='INDEX_REORGANIZE', @Fragmentation ', @ FragmentationLevel1 =5, @ FragmentationLevel2 =30, @UpdateStosystem =' ALL ', @OnlyModifiedStainstage =' Y ', @LogToTable =' Y '"–b

Với tùy chọn này, nếu số liệu thống kê đã được sửa đổi, chúng sẽ được cập nhật và nếu chúng tôi kiểm tra quy trình được lưu trữ [dbo]. [IndexOptimize], chúng tôi có thể thấy nơi Ola kiểm tra các sửa đổi:

 - Dữ liệu trong thống kê có được sửa đổi kể từ khi thống kê được cập nhật lần cuối không? IF @CurrentStatisticsID KHÔNG ĐẦY ĐỦ VÀ @UpdateStatistics KHÔNG ĐẦY ĐỦ VÀ @OnlyModifiedStained ='Y' BEGIN SET @ CurrentCommand10 ='' NẾU @LockTimeout KHÔNG ĐẦY ĐỦ @ CurrentCommand10 ='SET LOCK_TIMEOUT' + CAST (@LockTimeout * 1000 AS nvarchar) + '; 'IF (@Version> =10.504000 AND @Version <11) OR @Version> =11.03000 BEGIN SET @ CurrentCommand10 =@ CurrentCommand10 +' USE '+ QUOTENAME (@CurrentDatabaseName) +'; IF EXISTS (SELECT * FROM sys.dm_db_stats_properties (@ParamObjectID, @ParamStosystemID) WHERE modification_counter> 0) BEGIN SET @ParamSt StatisticsModified =1 END 'END ELSE BEGIN SETAM @ CurrentCommand10 =@ CurrentCommand10 +' IF EXISTS (SELECT @CurrentDatabaseName) + '.sys.sysindexes sysindexes WHERE sysindexes. [Id] =@ParamObjectID AND sysindexes. [Indid] =@ParamStosystemID AND sysindexes. [Rowmodctr] pre> 0) BEGIN SET @ParamSt Statistics> 

Đối với các phiên bản hỗ trợ DMF sys.dm_db_stats_properties, Ola sẽ kiểm tra nó để tìm bất kỳ thống kê nào đã được sửa đổi và đối với các phiên bản không hỗ trợ DMF sys.dm_db_stats_properties mới, bảng hệ thống sys.sysindexes sẽ được kiểm tra. Khiếu nại duy nhất của tôi ở đây là tập lệnh hoạt động giống như sp_updatestats:nếu ít nhất một hàng đã được sửa đổi, thống kê sẽ được cập nhật.

Nếu bạn không thích viết mã của riêng mình để quản lý số liệu thống kê, thì tôi khuyên bạn nên gắn bó với tập lệnh của Ola. Nhưng nếu bạn muốn nhắm mục tiêu các bản cập nhật của mình nhiều hơn một chút, thì tôi khuyên bạn nên sử dụng sys.dm_db_stats_properties. DMF này chỉ khả dụng cho SQL Server 2008R2 SP2 trở lên và SQL Server 2012 SP1 trở lên, vì vậy nếu bạn đang sử dụng phiên bản thấp hơn, bạn sẽ cần sử dụng sys.indexes. Nhưng đối với những người bạn có quyền truy cập vào sys.dm_db_stats_properties, đây là một truy vấn để giúp bạn bắt đầu:

 SELECT [sch]. [name] AS [Schema], [so]. [name] AS [ObjectName], [so]. [type] AS [ObjectType], [ss]. [name] AS [Statistic ], [sp]. [last_updated] AS [StatsLastUpdated], [sp]. [rows] AS [RowsInTable], [sp]. [rows_sampled] AS [RowsSampled], CAST (100 * [sp]. [row_sampled] / [sp]. [row] AS DECIMAL (18, 2)) AS [PercentSampled], [sp]. [modification_counter] AS [RowModilities], CAST (100 * [sp]. [modification_counter] / [sp]. [lines ] AS DECIMAL (18, 2)) AS [PercentChange] FROM [sys]. [Objects] AS [so] INNER JOIN [sys]. [Stats] AS [ss] ON [so]. [Object_id] =[ss] . [object_id] INNER JOIN [sys]. [schemas] AS [sch] ON [so]. [schema_id] =[sch]. [schema_id] NGOÀI RA ÁP DỤNG [sys]. [dm_db_stats_properties] ([so]. [object_id] , [ss]. [stats_id]) AS [sp] WHERE [so]. [type] IN ('U', 'V') AND ((CAST (100 * [sp]. [modification_counter] / [sp]. [row] AS DECIMAL (18,2))> =10,0)) ĐẶT HÀNG THEO ĐÚC (100 * [sp]. [modification_counter] / [sp]. [lines] AS DECIMAL (18, 2)) DESC; 

Lưu ý rằng với sys.objects, chúng tôi lọc trên các bảng và dạng xem; bạn có thể thay đổi điều này để bao gồm các bảng hệ thống. Sau đó, bạn có thể sửa đổi vị từ để chỉ truy xuất các hàng dựa trên tỷ lệ phần trăm hàng được sửa đổi hoặc có thể là sự kết hợp giữa tỷ lệ phần trăm sửa đổi và số hàng (đối với bảng có hàng triệu hoặc hàng tỷ hàng, tỷ lệ phần trăm đó có thể thấp hơn đối với bảng nhỏ).

Tóm tắt

Thông báo mang về nhà ở đây khá rõ ràng:Tôi không khuyên bạn nên sử dụng sp_updatestats để quản lý thống kê. Thống kê được cập nhật khi một hoặc nhiều hàng đã thay đổi (đây là ngưỡng cực kỳ thấp để cập nhật số liệu thống kê) và số liệu thống kê cho các chế độ xem được lập chỉ mục không đã cập nhật. Đây không phải là một phương pháp toàn diện và hiệu quả để quản lý số liệu thống kê… và nhiệm vụ thống kê cập nhật trong Kế hoạch bảo trì cũng không tốt hơn nhiều. Nó cập nhật thống kê lượt xem được lập chỉ mục, nhưng nó cập nhật mọi thống kê, bất kể sửa đổi. Một tập lệnh tùy chỉnh thực sự là một cách tốt nhất, nhưng hãy hiểu rằng tập lệnh của Ola Hallengren, nếu bạn đang cập nhật dựa trên sửa đổi, cũng cập nhật khi chỉ hàng đã được sửa đổi (nhưng ít nhất nó cũng có được các chế độ xem được lập chỉ mục). Cuối cùng, để kiểm soát tốt nhất, hãy tìm tập lệnh của riêng bạn để quản lý số liệu thống kê. Tôi đã cung cấp cho bạn truy vấn cơ sở để bắt đầu. Nếu bạn có thể dành một vài giờ để thực hành viết T-SQL của mình và sau đó kiểm tra nó, bạn sẽ có một tập lệnh tùy chỉnh hoạt động sẵn sàng cho cơ sở dữ liệu của mình trước khi kỳ nghỉ đến.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bắt đầu với Shareplex trên Windows trên AWS, Phần 1

  2. SQL CHỌN AVG

  3. Xác định và Khắc phục Sự cố Hiệu suất Hồ sơ Chuyển tiếp

  4. 12 Toán tử SQL thường được sử dụng

  5. Vấn đề Halloween - Phần 3