UPDATE STATISTICS 选项在 Amazon RDS SQL Server 中失败

Bre*_*zar 12 sql-server statistics temporary-tables amazon-rds

在 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
Run Code Online (Sandbox Code Playgroud)

但是尝试在统计信息上指定行数:

UPDATE STATISTICS #t 
  WITH ROWCOUNT = 100000000;
GO
Run Code Online (Sandbox Code Playgroud)

你会得到一个错误:

消息 1088,级别 16,状态 12,第 1 行 找不到对象“#t”,因为它不存在或您没有权限。

STATS_STREAM、ROWCOUNT 和 PAGECOUNT 选项已记录在案,但未得到官方支持。AWS RDS SQL Server 是否有解决方法以便它们工作?

sta*_*ray 1

确实,根据 SQL 代码,需要 sysadmin 或 db_owner 权限才能使用这些选项。我们知道我们没有系统管理员权限。对于内部表,需要系统管理员权限。由于临时表不是内部的,因此这不适用。所以这就留下了一种可能性——我们必须拥有 db_owner 权限。我们要不要?在 SQL 代码中,它不是在当前数据库上下文中进行评估,而是在我们尝试更改的对象的数据库:TempDB 中进行评估。

现在我们知道了失败的原因,我们可以评估可能的解决方法。1. 如果 Amazon RDS 授予修改 tempdb 数据库角色的权限,则将用户添加到 db_owner 即可。我还没有测试过这一点,但根据这篇文章相信它可能是可行的。这将是最简单、也许也是最优雅的解决方案。2. 使用与您要定位的统计信息配置文件匹配的垃圾填充临时表,使用 NORECOMPUTE 更新统计信息,然后按照最初预期的方式截断并填充/使用它。

证明:使用 sysadmin 登录;核实

SELECT SUSER_SNAME() [CurrentLogin], IS_SRVROLEMEMBER('SYSADMIN', SUSER_SNAME()) GO
Run Code Online (Sandbox Code Playgroud)

创建测试数据库并使用它

CREATE DATABASE [Test] GO USE [Test] GO
Run Code Online (Sandbox Code Playgroud)

确认 sysadmin 不会出现此问题

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
Run Code Online (Sandbox Code Playgroud)

创建一个没有sysadmin权限的用户并使用它

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())
Run Code Online (Sandbox Code Playgroud)

重现错误

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
Run Code Online (Sandbox Code Playgroud)

证明当前数据库的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
Run Code Online (Sandbox Code Playgroud)

重现错误

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 
Run Code Online (Sandbox Code Playgroud)

现在添加到 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
Run Code Online (Sandbox Code Playgroud)

并观察错误消失

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 
Run Code Online (Sandbox Code Playgroud)

从测试中的 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 
Run Code Online (Sandbox Code Playgroud)