database_scoped_configurations中的错误


9

我正在尝试从以下位置插入结果集:

SELECT * FROM sys.database_scoped_configurations

到临时表中,因为我想检查服务器上所有数据库的设置。所以我写了这段代码:

DROP TABLE IF EXISTS #h
CREATE TABLE #h(dbname sysname, configuration_id INT, name sysname,     value SQL_VARIANT,  value_for_secondary SQL_VARIANT)
EXEC sys.sp_MSforeachdb 'USE ?; insert into #h(dbname, configuration_id, name, value,value_for_secondary)  SELECT ''?'' as dbname, * FROM sys.database_scoped_configurations  D'
SELECT * FROM #h H

但随后将只有每个数据库一行,而不是4行,我从在每个数据库中运行一个普通的选择期待。

我知道有比使用sp_MSForEachDB更好的编码方法,我尝试了几种方法。但是我仍然每个数据库只获得一行。我已经在SQL Server 2016 RTM和SP1上都尝试过

这是SQL Server 2016的错误,还是我做错了什么?


错误已得到修复,至少在Microsoft SQL Server 2017(RTM-CU15-GDR)中得到了解决
Henrik Staun Poulsen

Answers:


8

这是SQL Server 2016的错误吗?

是。绝对这不是正确的行为。我在这里报告了该问题,并已在SQL Server 2016 SP2 CU9中修复

正如Mikael Eriksson在评论中所说,sys.database_scoped_configurationssys.dm_exec_sessions以格式的形式实现为视图

SELECT ...  
FROM OpenRowset(TABLE xxxx)  

但是,比较下面的两个计划有明显的不同。

DBCC TRACEON(3604);

DECLARE @database_scoped_configurations TABLE(x INT);

INSERT INTO @database_scoped_configurations
SELECT configuration_id
FROM   sys.database_scoped_configurations
OPTION (QUERYTRACEON 8608, QUERYTRACEON 8615, QUERYTRACEON 8619, QUERYTRACEON 8620 );


DECLARE @dm_exec_sessions TABLE(x INT);

INSERT INTO @dm_exec_sessions
SELECT session_id
FROM   sys.dm_exec_sessions
OPTION (QUERYTRACEON 8608, QUERYTRACEON 8615, QUERYTRACEON 8619, QUERYTRACEON 8620 );

在此处输入图片说明

这两个查询的跟踪标志8619输出显示

应用规则:EnforceHPandAccCard-x0->假脱机或顶部(x0)

SQL Server显然无法确定TVF的来源也不是插入目标,因此需要万圣节保护。

在会话情况下,这被实现为首先捕获所有行的假脱机。在中database_scoped_configurations添加一个TOP 1到计划。本文讨论TOP了万圣节防护的使用。本文还提到了一个未记录的跟踪标志来强制后台打印,而不是按预期方式工作。TOP

DECLARE @database_scoped_configurations TABLE(x INT);

INSERT INTO @database_scoped_configurations
SELECT configuration_id
FROM   sys.database_scoped_configurations
OPTION (QUERYTRACEON 8692)

使用TOP 1而不是线轴的一个明显问题是它将任意限制插入的行数。因此,只有在函数返回的行数小于等于1的情况下,这才是有效的。

初始备忘录如下所示

在此处输入图片说明

将此与查询2的初始备注进行比较

在此处输入图片说明

如果我正确理解上述内容,它认为第一个TVF最多可以返回一行,因此应用了错误的优化。第二个查询的最大值设置为1.34078E+1542^512)。

我不知道这个最大行数是从哪里得到的。也许是DMV的作者提供的元数据?奇怪的是,TOP(50)解决方法没有被重写,TOP(1)因为TOP(50)这不会阻止万圣节问题的发生(尽管会无限期地阻止它继续下去)


6

请停止使用sp_MSForEachDB。它不受支持,没有文档记录,并且存在儿童车-这可能是这里的问题。我的替代品在这里也演示了相同的问题,但总的来说,使用起来更安全。

对于这样的事情,我更喜欢生成动态SQL,而不是将单个命令交给过程执行多次(甚至我的过程,我对此非常信任),这样,我可以简单地打印命令而不是执行它们,并且确保他们都会按照他们说的去做。

通过观察发现,系统视图下的代码实现了TOP (1),我们可以尝试这种方式:

DROP TABLE IF EXISTS #h;

CREATE TABLE #h(dbname sysname, configuration_id INT, name sysname, 
  value SQL_VARIANT,  value_for_secondary SQL_VARIANT);

DECLARE @sql nvarchar(max) = N'', @base nvarchar(max) = N'insert into #h
  (dbname, configuration_id, name, value,value_for_secondary)  SELECT TOP ($c$) 
  $db$ as dbname, * FROM $qdb$.sys.database_scoped_configurations;';

SELECT @sql += REPLACE(REPLACE(REPLACE(@base, N'$qdb$', QUOTENAME(name)), 
  N'$db$', CHAR(39) + name + CHAR(39)), N'$c$', RTRIM(COUNT(*) OVER()))
FROM sys.databases WHERE state = 0;

PRINT @sql;
EXEC sys.sp_executesql @sql;
SELECT * FROM #h;

请注意,我不在USE这里使用,而是在sys目录视图之前添加数据库名称。

我不知道为什么视图以神奇的方式起作用;我不知道您会在这里得到一个好的答案,因为它可能需要Microsoft(或有权访问源代码或愿意启动调试器的任何人)的注释。


那是我尝试的几种方法中的第一种,但是我认为我不能在示例中使用该sproc。
Henrik Staun Poulsen

6

感谢您报告此问题!

这确实是查询优化器为sys.database_scoped_configurations目录视图生成计划的方式中的一个错误。我们将在SQL Server 2016的下一个更新中和Azure SQL数据库中解决此问题。

解决方法是,您可以TOPSELECT插入的部分添加一个子句以获取正确的计划,例如:

DECLARE @database_scoped_configurations TABLE(x INT); 
INSERT INTO @database_scoped_configurations 
SELECT **TOP 100** configuration_id 
FROM sys.database_scoped_configurations 

3

我同意这是非常奇怪的并且可能是一个潜在的错误,但是例如,将TOP(50)添加到您的选择中实际上会返回所有行,因此至少可以使您继续前进。结果似乎来自系统表值函数([DB_SCOPED_CONFIG]),所以我无法真正知道发生了什么。

我将继续关注这个话题,看看“更聪明”的人是否知道为什么会这样。


您是否仅获得每个数据库的MAXDOP行?
Dan Guzman

@DanGuzman-是的select本身可以很好地工作(没有所有的foreach东西,只在一个数据库中)。当您将Insert添加到其中时,就会产生怪异的行为
Scott Hodgin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.