与针对单个数据库运行相比,SQL Server 2014 sp_msforeachdb提供的结果不同

时间:2018-09-19 21:24:27

标签: sql-server sql-server-2014

我的办公室正在更改我们的链接服务器。结果,我需要从实例上每个数据库获取指向当前链接服务器的每个视图的列表,以便我们知道需要替换什么。

在进行了一些在线研究之后,我想到了此解决方案,以获取所有引用链接服务器的视图的列表:

  1. 创建一个临时表:

    CREATE TABLE [dbo].[#TMP]
    (
        [DBNAME] NVARCHAR(256) NULL,
        [NAME]   NVARCHAR(256) NOT NULL,
        [DESC]   NVARCHAR(MAX) NOT NULL
    );
    
  2. 然后,我可以利用sp_msforeachdb过程来遍历每个数据库,并将此信息存储在临时表中:

    DECLARE @command varchar(1000)
    
    SELECT @command = 'INSERT INTO #TMP SELECT ''?'' as DBName, OBJECT_NAME(object_id), definition FROM sys.sql_modules WHERE definition LIKE ''%linkedservername%'''
    EXEC sp_msforeachdb @command
    

当我做一个SELECT * from #TMP时,我看到一些可疑的地方……对每个数据库都重复相同的5个视图。好像它是在具有链接服务器名称的数据库中获取了前5个视图,然后只是为每个数据库复制了它!

如果我通过将sys.sql_modules更改为[?].sys.sql_modules来修改选择命令,事情会变得更加奇怪。在这种情况下,我没有得到565个结果,而只有17个!!!

现在,如果我删除命令的INSERT INTO #TMP"部分,然后运行以下命令:

DECLARE @command varchar(1000)

SELECT @command = 'SELECT ''?'' as DBName, OBJECT_NAME(object_id), definition FROM sys.sql_modules WHERE definition LIKE ''%linkedservername%'''

EXEC sp_msforeachdb @command

结果变得更奇怪!在我的一个名为“ DB_Jobs”的数据库中,在用于视图的列中(没有列名),4个结果中的3个返回NULL,最后一个结果返回“ SCHTYPEMismatch”。奇怪的是,在“定义”列中,它返回准确的结果!!!

然后,如果我进入数据库并运行此命令:

SELECT OBJECT_NAME(object_id), definition 
FROM [DB_Jobs].[sys].[sql_modules] 
WHERE definition LIKE '%linkedservername%'

它完美地返回了结果!

这是怎么回事?更重要的是,我该如何使用原始的@command来利用sp_msforeachdb并正确返回想要的结果(并为每个结果包括数据库名称)?

顺便说一句,我正在使用SQL Server 2014。

1 个答案:

答案 0 :(得分:1)

Sp_msforeachdb本质上是一个全局游标,它使您可以通过引用[?]依次访问每个数据库。默认情况下,它不会在每个数据库上执行您的命令。如果您运行简单的

EXEC sp_msforeachdb 'select db_name()'

对于第一个示例,您将获得相同的视图,因为每次都针对相同的数据库运行命令。当您切换到[?]。sys.sql_modules时,您开始查询由[?]引用的数据库中的sys.sql_modules。

可以通过运行以下命令来查看NULL问题:

SELECT OBJECT_NAME(object_id), definition FROM [msdb].[sys].[sql_modules] WHERE definition LIKE '%name%'

在MSDB中运行它,您将获得一个充满对象名称的列名和一个包含定义的列。在Master中运行它,即使您具有定义,对象名称现在也为NULL。 OBJECT_NAME()在当前数据库的上下文中运行,因此除非您碰巧具有一个匹配的object_id,否则您将获得NULL,但随后显示的对象名称错误。定义直接引用所需的数据库,因此您仍然可以获取它们。

要使您的查询按您希望的方式工作,您只需要USE [?](我正在寻找%name%之类的定义,因为我知道它将在这里进行测试)

CREATE TABLE [dbo].[#TMP](
[DBNAME] NVARCHAR(256) NULL,
[NAME]   NVARCHAR(256) NOT NULL,
[DESC]   NVARCHAR(MAX) NOT NULL);

DECLARE @command varchar(1000)
SELECT @command = 'USE [?]; INSERT INTO #TMP SELECT ''?'' as DBName, OBJECT_NAME(object_id), definition FROM sys.sql_modules WHERE definition LIKE ''%name%'''
EXEC sp_msforeachdb @command


SELECT * FROM #TMP