选项(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"

  1. 在 msdb 数据库上创建一个存储过程,允许在没有系统管理员权限的情况下设置所需的跟踪标志。
    (当然,系统管理员负责设置允许的跟踪标志值列表)。

  2. 通过调用此存储过程包装任何有问题的语句(动态 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 的默认设置。