SQL Server 2014 sp_msforeachdb дает результаты, отличные от работы с отдельной базой данных - PullRequest
0 голосов
/ 20 сентября 2018

Мой офис меняет наши связанные серверы.В результате мне нужно получить список каждого отдельного представления из каждой базы данных в нашем экземпляре, который указывает на текущий связанный сервер, чтобы мы могли знать, что нужно заменить.

Проведя некоторые онлайн-исследования, я пришелс помощью этого решения получить список всех представлений, которые ссылаются на связанный сервер:

  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» в столбце для представлений (имя столбца отсутствует) 3 из 4 результатов возвращают 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 голосов
/ 20 сентября 2018

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 () выполняется в контексте текущей базы данных, поэтому вы получите NULL, если у вас не будет совпадения object_id, но тогда вы выводите неправильное имя объекта.Definitions напрямую ссылается на нужную вам базу данных, поэтому вы все равно получаете их.

Чтобы ваш запрос работал так, как вам нужно, вам просто нужно 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
...