UPDATE STATISTICS 選項在 Amazon RDS SQL Server 中失敗
在 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