选项(QUERYTRACEON 9481),动态 SQL & DBCC TRACEON 错误
OPTION(QUERYTRACEON 9481), Dynamic SQL & DBCC TRACEON error
我的情况有点奇怪...a) 我们正在将所有 SQL 服务器实例升级到 2017。b) 多个受影响的数据库正在使用旧版兼容性 Levels/Cardinality 估算器。 c) 我们希望所有数据库都在当前 (2017) CL 上并使用最新的 CE。
计划在单个语句级别使用 "OPTION(QUERYTRACEON 9481)" 来处理不适合新 CE 的查询。不是作为永久修复,而是作为克服困难的一种方法(重写每个受影响的过程完全超出了当前项目的范围)。
切入正题...除了当存储过程使用动态 SQL 并且由非 SA 用户执行时,这似乎按预期工作。当然,这确实有道理。只要在过程范围内执行 "OPTION(QUERYTRACEON 9481)",所有权链接就会提供执行基础 DBCC TRACEON 命令所需的权限。然而,动态 SQL 的使用本质上将代码视为 AD-HOC SQL 并在用户自己的安全上下文下执行...这不是 SA,因此无权执行DBCC TRACEON;
简短或重写查询或赋予应用程序服务帐户 SA 角色,有人有可用的解决方案吗?
提前谢谢你,
贾森
问题:
...The use of dynamic SQL, however, essentially treats the code as AD-HOC SQL and is executed under the users own security context... Which is not a SA and therefore does not have rights to execute DBCC TRACEON;
解法:
金伯利·特里普 (Kimberly Tripp) 在她的 post 中对 sqlskills.com 进行了精彩的解释:
"Setting CE TraceFlags on a query-by-query (or session) basis"
在 msdb 数据库上创建一个存储过程,允许在没有系统管理员权限的情况下设置所需的跟踪标志。
(当然,系统管理员负责设置允许的跟踪标志值列表)。
通过调用此存储过程包装任何有问题的语句(动态 sql、临时查询或过程调用)并更改会话跟踪标志以执行。
这允许具有较低权限的用户更改 Cardinality Estimator 以执行有问题的语句。
用法示例:
EXEC msdb.dbo.msdbSetTraceFlag 9481, 1;
GO
Problematic STATEMENT or PROCEDURE
EXEC msdb.dbo.msdbSetTraceFlag 9481, 0; -- don't remember to turn it back off!
GO
存储过程代码:
USE msdb;
GO
CREATE PROCEDURE msdbSetTraceFlag
(@TraceFlag int,
@OnOff bit = 0)
WITH EXECUTE AS OWNER
AS
DECLARE @OnOffStr char(1) = @OnOff;
-- Sysadmins can add supported trace flags and then use this
-- from their applications
IF @TraceFlag NOT IN (
9481 -- LegacyCE if database is compat mode 120 or higher
, 2312 -- NewCE if database compat mode 110 or lower
)
BEGIN
RAISERROR('The Trace Flag supplied is not supported. Please contact your system administrator to determine inclusion of this trace flag: %i.', 16, 1, @TraceFlag);
RETURN
END
ELSE
BEGIN
DECLARE @ExecStr nvarchar(100);
IF @OnOff = 1
SELECT @ExecStr = N'DBCC TRACEON(' + CONVERT(nvarchar(4), @TraceFlag) + N')';
ELSE
SELECT @ExecStr = N'DBCC TRACEOFF(' + CONVERT(nvarchar(4), @TraceFlag) + N')';
-- SELECT (@ExecStr)
EXEC(@ExecStr)
-- RAISERROR (N'TraceFlag: %i has been set to:%s (1 = ON, 0 = OFF).', 10, 1, @TraceFlag, @OnOffStr);
END;
GO
GRANT EXECUTE ON msdbSetTraceFlag TO PUBLIC --or to a specific set of users;
GO
注意: 此存储过程是在 msdb 中创建的,而不是在 master 上创建的,因为 "trustworthy" 先决条件是 msdb 的默认设置。
我的情况有点奇怪...a) 我们正在将所有 SQL 服务器实例升级到 2017。b) 多个受影响的数据库正在使用旧版兼容性 Levels/Cardinality 估算器。 c) 我们希望所有数据库都在当前 (2017) CL 上并使用最新的 CE。
计划在单个语句级别使用 "OPTION(QUERYTRACEON 9481)" 来处理不适合新 CE 的查询。不是作为永久修复,而是作为克服困难的一种方法(重写每个受影响的过程完全超出了当前项目的范围)。
切入正题...除了当存储过程使用动态 SQL 并且由非 SA 用户执行时,这似乎按预期工作。当然,这确实有道理。只要在过程范围内执行 "OPTION(QUERYTRACEON 9481)",所有权链接就会提供执行基础 DBCC TRACEON 命令所需的权限。然而,动态 SQL 的使用本质上将代码视为 AD-HOC SQL 并在用户自己的安全上下文下执行...这不是 SA,因此无权执行DBCC TRACEON;
简短或重写查询或赋予应用程序服务帐户 SA 角色,有人有可用的解决方案吗?
提前谢谢你,
贾森
问题:
...The use of dynamic SQL, however, essentially treats the code as AD-HOC SQL and is executed under the users own security context... Which is not a SA and therefore does not have rights to execute DBCC TRACEON;
解法:
金伯利·特里普 (Kimberly Tripp) 在她的 post 中对 sqlskills.com 进行了精彩的解释:
"Setting CE TraceFlags on a query-by-query (or session) basis"
在 msdb 数据库上创建一个存储过程,允许在没有系统管理员权限的情况下设置所需的跟踪标志。
(当然,系统管理员负责设置允许的跟踪标志值列表)。通过调用此存储过程包装任何有问题的语句(动态 sql、临时查询或过程调用)并更改会话跟踪标志以执行。
这允许具有较低权限的用户更改 Cardinality Estimator 以执行有问题的语句。
用法示例:
EXEC msdb.dbo.msdbSetTraceFlag 9481, 1;
GO
Problematic STATEMENT or PROCEDURE
EXEC msdb.dbo.msdbSetTraceFlag 9481, 0; -- don't remember to turn it back off!
GO
存储过程代码:
USE msdb;
GO
CREATE PROCEDURE msdbSetTraceFlag
(@TraceFlag int,
@OnOff bit = 0)
WITH EXECUTE AS OWNER
AS
DECLARE @OnOffStr char(1) = @OnOff;
-- Sysadmins can add supported trace flags and then use this
-- from their applications
IF @TraceFlag NOT IN (
9481 -- LegacyCE if database is compat mode 120 or higher
, 2312 -- NewCE if database compat mode 110 or lower
)
BEGIN
RAISERROR('The Trace Flag supplied is not supported. Please contact your system administrator to determine inclusion of this trace flag: %i.', 16, 1, @TraceFlag);
RETURN
END
ELSE
BEGIN
DECLARE @ExecStr nvarchar(100);
IF @OnOff = 1
SELECT @ExecStr = N'DBCC TRACEON(' + CONVERT(nvarchar(4), @TraceFlag) + N')';
ELSE
SELECT @ExecStr = N'DBCC TRACEOFF(' + CONVERT(nvarchar(4), @TraceFlag) + N')';
-- SELECT (@ExecStr)
EXEC(@ExecStr)
-- RAISERROR (N'TraceFlag: %i has been set to:%s (1 = ON, 0 = OFF).', 10, 1, @TraceFlag, @OnOffStr);
END;
GO
GRANT EXECUTE ON msdbSetTraceFlag TO PUBLIC --or to a specific set of users;
GO
注意: 此存储过程是在 msdb 中创建的,而不是在 master 上创建的,因为 "trustworthy" 先决条件是 msdb 的默认设置。