Sql-Server

UPDATE STATISTICS 選項在 Amazon RDS SQL Server 中失敗

  • July 18, 2021

在 Amazon RDS SQL Server(任何版本/版本)中,創建一個臨時表並更新其統計資訊。這工作正常:

IF OBJECT_ID('tempdb..#t') IS NOT NULL
   DROP TABLE #t;
GO
CREATE TABLE #t (id INT NOT NULL);
GO
UPDATE STATISTICS #t 
GO

但嘗試在統計資訊中指定行數:

UPDATE STATISTICS #t 
 WITH ROWCOUNT = 100000000;
GO

你得到一個錯誤:

消息 1088,級別 16,狀態 12,第 1 行找不到對象“#t”,因為它不存在或您沒有權限。

STATS_STREAM、ROWCOUNT 和 PAGECOUNT 選項已記錄在案,但不受官方支持。是否有適用於 AWS RDS SQL Server 的解決方法以使其正常工作?

確實,每個 SQL 程式碼都需要 sysadmin 或 db_owner 權限才能使用這些選項。我們知道我們沒有系統管理員權限。對於內部表,需要係統管理員權限。由於臨時表不是內部的,因此這不適用。所以這留下了一種可能性——我們必須擁有 db_owner 權限。我們要不要?在 SQL 程式碼中,不是在目前 DB 上下文中評估它,而是在我們嘗試更改的對象的數據庫中評估它:TempDB。

現在我們知道了失敗的原因,我們可以評估可能的解決方法。1. 如果 Amazon RDS 授予修改 tempdb 數據庫角色的權限,則將使用者添加到 db_owner 即可。我沒有對此進行測試,但根據這篇文章相信它可能是可行的。這將是最簡單,也許是最優雅的解決方案。2. 使用與您的目標統計配置文件匹配的垃圾填充臨時表,使用 NORECOMPUTE 更新統計資訊,然後以最初預期的方式截斷並填充/使用它。

證明:使用系統管理員登錄;核實

SELECT SUSER_SNAME() [CurrentLogin], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()) GO

創建測試數據庫並使用它

CREATE DATABASE [Test] GO USE [Test] GO

確認系統管理員沒有發生此問題

IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t; GO CREATE TABLE #t (id INT NOT NULL; GO UPDATE STATISTICS #t WITH ROWCOUNT = 100000000; GO

創建一個沒有系統管理員權限的使用者並使用它

CREATE LOGIN [UpdateStatsLogin] WITH PASSWORD = 'DontPeek!' 
CREATE USER [UpdateStatsUsr] FROM LOGIN [UpdateStatsLogin]
SETUSER 'UpdateStatsUsr' GO SELECT SUSER_SNAME() [CurrentLogin], USER_NAME() [CurrentUser], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()), IS_ROLEMEMBER('db_owner', USER_NAME())

重現錯誤

IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t GO CREATE TABLE #t (id INT NOT NULL); GO UPDATE STATISTICS #t WITH ROWCOUNT = 100000000; GO

證明目前數據庫的db_owner不起作用

SETUSER GO SELECT SUSER_SNAME() [CurrentLogin], USER_NAME() [CurrentUser], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()), IS_ROLEMEMBER('db_owner', USER_NAME()) GO ALTER ROLE [db_owner] ADD MEMBER [updatestatsusr] GO SETUSER 'updatestatsusr' GO SELECT SUSER_SNAME() [CurrentLogin], USER_NAME() [CurrentUser], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()), IS_ROLEMEMBER('db_owner', USER_NAME()) GO

重現錯誤

IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t GO CREATE TABLE #t (id INT NOT NULL); GO UPDATE STATISTICS #t WITH ROWCOUNT = 100000000; GO 

現在添加到 tempdb 中的 db_owner 切換回 sysadmin 並驗證

SETUSER GO SELECT SUSER_SNAME() [CurrentLogin], USER_NAME() [CurrentUser], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()), IS_ROLEMEMBER('db_owner', USER_NAME()) GO USE TEMPDB GO CREATE USER [updatestatsusr] FROM LOGIN [updatestatslogin] GO ALTER ROLE [db_owner] ADD MEMBER [updatestatsusr] GO SELECT SUSER_SNAME() [CurrentLogin], USER_NAME() [CurrentUser], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()), IS_ROLEMEMBER('db_owner', USER_NAME()) GO

並觀察錯誤消失

USE [Test] GO SETUSER 'updatestatsusr' GO SELECT SUSER_SNAME() [CurrentLogin], USER_NAME() [CurrentUser], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()), IS_ROLEMEMBER('db_owner', USER_NAME()) GO IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t GO CREATE TABLE #t (id INT NOT NULL); GO UPDATE STATISTICS #t WITH ROWCOUNT = 100000000; GO 

從 Test 中的 db_owner 中刪除,以表明 tempdb 很重要

SETUSER GO SELECT SUSER_SNAME() [CurrentLogin], USER_NAME() [CurrentUser], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()), IS_ROLEMEMBER('db_owner', USER_NAME()) GO ALTER ROLE [db_owner] DROP MEMBER [UpdateStatsUsr] GO SELECT SUSER_SNAME() [CurrentLogin], USER_NAME() [CurrentUser], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()), IS_ROLEMEMBER('db_owner', USER_NAME()) GO IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t GO CREATE TABLE #t (id INT NOT NULL); GO UPDATE STATISTICS #t WITH ROWCOUNT = 100000000; GO 

如果您將命令包裝在指定“EXECUTE AS OWNER”的儲存過程中,似乎可以工作:-

/* RDS SQL Server Express Edition 14.00.3281.6.v1 */

IF OBJECT_ID('tempdb..#uspMyProc') IS NOT NULL
   DROP PROC #uspMyProc ;
GO

CREATE PROC #uspMyProc
  WITH EXECUTE AS OWNER
AS 
  BEGIN
     UPDATE STATISTICS #t 
        WITH ROWCOUNT = 100000000 ;
  END ;
GO

EXEC #uspMyProc ;
GO

引用自:https://dba.stackexchange.com/questions/236790