SQL Server 2014 sp_msforeachdb 给出的结果与针对单个数据库的 运行 不同

SQL Server 2014 sp_msforeachdb gives different results than running against individual database

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

在网上做了一些研究后,我想到了这个解决方案来获取引用链接服务器的所有视图的列表:

  1. 创建临时文件table:

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

    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 来修改我的 select 命令,事情会变得更加奇怪;在这种情况下,我没有得到 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 个 returns NULL,最后一个结果 returns "SCHTYPEMismatch"。更奇怪的是,在定义栏中,它 return 的结果是准确的!!!

然后,如果我进入数据库并运行这个:

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

结果return完美!

这是怎么回事?更重要的是,我在原来的 @command 中可以做什么来利用 sp_msforeachdb 并正确 return 我想要的结果(并包括每个结果的数据库名称)?

顺便说一下,我使用的是 SQL Server 2014。

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() 运行s 在当前数据库的上下文中,所以你得到 NULL,除非你碰巧有一个匹配的 object_id,但是你显示了错误的对象名称。定义是直接引用你想要的数据库,所以你仍然可以得到它们。

要让您的查询按照您的意愿工作,您只需要 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