查询计划重用需要架构资格吗?

Bor*_*ort 9 sql-server-2005 sql-server-2008

在阅读这篇关于 SQL Server 中的计划缓存的文章时,我发现了一个我不知道的花絮:

...为了重用,批处理引用的对象不需要名称解析。例如,Sales.SalesOrderDetail 不需要名称解析,而 SalesOrderDetail 则需要,因为在多个架构中可能存在名为 SalesOrderDetail 的表。通常,由两部分组成的对象名称(即 schema.object)为计划重用提供了更多机会。

我正在寻找关于使用两部分对象名称的重要性的一些说明,因为“通常,两部分对象名称为计划重用提供了更多机会。”,但它首先说这是必要的。

更具体地说,我处理的大多数存储过程都在 dbo 模式中,并且只引用 dbo 对象,而没有指定 dbo 前缀。即使所有内容都使用默认模式,这些是否无法重用缓存的查询计划?

Mar*_*ith 9

对于要重用的计划,sys.dm_exec_plan_attributeswhere 中的所有属性is_cache_key=1必须相同。这些列表如下。

acceptable_cursor_options
compat_level
date_first
date_format
dbid
dbid_execute
is_replication_specific
language_id
merge_action_type
objectid
optional_clr_trigger_dbid
optional_clr_trigger_objid
optional_spid
required_cursor_options
set_options
status
user_id
Run Code Online (Sandbox Code Playgroud)

使用两个部分名称影响的一个是 user_id

如果您在具有默认架构的用户凭据下尝试以下操作 dbo

DBCC FREEPROCCACHE;

CREATE TABLE dbo.FooBar(X int);

EXEC('SELECT * FROM FooBar');
EXEC('SELECT * FROM FooBar');

EXEC('SELECT * FROM dbo.FooBar');
EXEC('SELECT * FROM dbo.FooBar');
Run Code Online (Sandbox Code Playgroud)

然后执行以下查询

SELECT usecounts,
       text,
       value AS [user_id] 
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
CROSS APPLY sys.dm_exec_plan_attributes(plan_handle) AS epa
WHERE text LIKE 'SELECT * FROM %FooBar'  and attribute='user_id'
Run Code Online (Sandbox Code Playgroud)

你会看到以下结果

usecounts   text                                user_id
----------- ----------------------------------- -------
2           SELECT * FROM dbo.FooBar            -2
2           SELECT * FROM FooBar                1 
Run Code Online (Sandbox Code Playgroud)

这表明当第二次运行相同的语句时,两个计划都得到了重用。sys.dm_exec_plan_attributes的文档解释了user_id

值为 -2 表示提交的批处理不依赖于隐式名称解析,可以在不同用户之间共享。这是首选方法。任何其他值表示在数据库中提交查询的用户的用户 ID。

这似乎是不正确的!从我的测试来看,它user_id在第二种情况下实际使用的值似乎是schema_id执行用户的默认架构的值,而不是该特定用户的标识符。EXEC使用默认模式“dbo”在不同的登录名下再次运行这四个语句。

usecounts   text                                user_id
----------- ----------------------------------- -------
4           SELECT * FROM dbo.FooBar            -2
4           SELECT * FROM FooBar                1 
Run Code Online (Sandbox Code Playgroud)

显示查询的两个版本的计划能够在用户之间重复使用。最后EXEC在第三次登录下再次运行这四个语句,默认模式“guest”给出。

usecounts   text                                user_id
----------- ----------------------------------- -------
6           SELECT * FROM dbo.FooBar            -2
4           SELECT * FROM FooBar                1
2           SELECT * FROM FooBar                2
Run Code Online (Sandbox Code Playgroud)

表明dbo合格查询的计划已在具有不同默认架构的用户之间成功共享,但非架构合格查询需要编译新计划。

如果您没有看到这种共享发生,请确保您正在测试的所有登录都具有相同的set_options, language_id, date_firstdate_format因为它们位于开头列出的缓存键中,并且它们之间的任何差异都将阻止计划在会话之间重复使用。

  • @Bort - 实际上我刚刚意识到它取决于默认模式而不仅仅是“user_id”。在我最初的测试中,我有第二个用户“WITH DEFAULT_SCHEMA=[guest]”。只要两个不同的用户具有相同的默认架构,他们就可以共享彼此的计划。 (2认同)