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 是否有解决方法以便它们工作?
确实,根据 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)