私には3台のサーバーがあり、そのうちの1台は他を指すリンクサーバーが構成されています。これをサーバーAと呼びます。サーバーAには、さまざまな目的で100を超えるユーザーデータベースがあります。サーバーBは、私たちが排除しようとしているSQL 2005を実行しています。サーバーCには、サーバーBの一部のデータベースのコピーがあり、アプリケーションをサーバーBのデータベースコピーからサーバーCに移行しています。
サーバーBにいるとき、サーバーAから特定のデータベースへの接続を確認できますが、サーバーBへのリンクサーバー接続を使用しているサーバーAからの手順、タスク、またはジョブを確認する方法がわかりません。
サーバーBのデータベースを廃止するには、サーバーAの接続をサーバーCのデータベースに再ポイントする必要があります。しかし、そのためには、サーバーAのどの手順、タスク、またはジョブがその接続を使用しているかを知る必要があるため、それらを更新できます。
リンクサーバーを無効にせずにリンクサーバーの依存関係を確認して、何が失敗し始めるかを確認する方法はありますか?
さて、リンクされたサーバーの名前を探す手順(暗号化されていない限り)とサーバーAのジョブを解析できますが、これらの要求は、アドホックからアプリを通じて渡される可能性があることに注意してくださいSQL、動的に構築されたものなどなので、これはすべてをキャッチしません。リンクサーバー名がコードやコメントで自然に見つかる可能性のある一般的な用語である場合、これは誤検知を引き起こす可能性もあります。
DECLARE @sql NVARCHAR(MAX) = N'',
@p NVARCHAR(MAX),
@linked_server SYSNAME = N'%linked_server_name%';
SET @p = N' UNION ALL SELECT N''$db$'', s.name,
o.name FROM $db$.sys.sql_modules AS p
INNER JOIN $db$.sys.objects AS o
ON p.[object_id] = o.[object_id]
INNER JOIN $db$.sys.schemas AS s
ON o.[schema_id] = s.[schema_id]
WHERE p.definition LIKE @lsn';
SELECT @sql = @sql + REPLACE(@p, N'$db$', QUOTENAME(name))
FROM sys.databases; -- may want to filter out system dbs, offline, etc
SET @sql = STUFF(@sql, 1, 11, N'') + N';';
EXEC sys.sp_executesql @sql, N'@lsn SYSNAME', @linked_server;
SELECT j.name FROM msdb.dbo.sysjobs AS j
INNER JOIN msdb.dbo.sysjobsteps AS s
ON j.job_id = s.job_id
WHERE s.command LIKE @linked_server;
もちろん、最初に同義語を探す必要があるかもしれません:
DECLARE @sql NVARCHAR(MAX) = N'',
@p NVARCHAR(MAX),
@synonym_prefix SYSNAME = N'[linked_server_name].%';
SET @p = N' UNION ALL SELECT N''$db$'', s.name, o.name
FROM $db$.sys.synonyms AS o
INNER JOIN $db$.sys.schemas AS s
ON o.[schema_id] = s.[schema_id]
WHERE s.base_object_name LIKE @spre';
SELECT @sql = @sql + REPLACE(@p, N'$db$', QUOTENAME(name))
FROM sys.databases; -- may want to filter out system dbs, offline, etc
SET @sql = STUFF(@sql, 1, 11, N'') + N';';
EXEC sys.sp_executesql @sql, N'@spre SYSNAME', @synonym_prefix;
次に、上記の'linked_server_name'
の代わりにそれらの同義語名を使用します。
リンクサーバーの参照を検索するために開発したこのスクリプトを使用しますが、複数のオブジェクトで任意のテキスト文字列を検索するために使用できます。
/*********************************
Name: Text Searcher
Author: Jonathan Fite
Created: 1/16/2015
Purpose: To enable one to more easily search for objects by an indicated string.
Usage:
- Update the "Where To Search" section to turn on or off various sections.
- Craft an insert statement to populate "@ObjectsToSearchFor" with the text you
want to look for.
- Execute.
NOTE: This works recursively (in a fashion). The first execution gets just what you are
looking for. But if you execute it again without explicitly dropping the "#SearchResults" table
then it will run again, but this time looking for references to the objects it found in the first pass.
This is useful when you are trying to find out what is using a linked server. So you get the name of the
linked server (and it's Host name) and run a search on that. It tells you a handful of objects. You then run
it again, but this time looking for those objects (say, in other stored procedures or in SSIS/Jobs).
CAUTION: If you want to use the recursive functionality, you will need to save off the results FIRST.
DROP TABLE #SearchResults
-Linked Server Query (finds all references to linked servers as configured, as well as all variations
that would allow for accessing a remote server without using a linked server.
INSERT INTO @ObjectsToSearchFor
(TextString)
SELECT srvname FROM sys.sysservers WHERE srvname <> @@SERVERNAME
UNION
SELECT datasource FROM sys.sysservers WHERE srvname <> @@SERVERNAME
UNION
SELECT srvnetname FROM sys.sysservers WHERE srvnetname <> @@SERVERNAME
UNION
SELECT 'OPENROWSET'
UNION
SELECT 'OPENQUERY'
UNION
SELECT 'OPENDATASOURCE'
**********************************/
--To hide row counts and make the print statements easier to see.
SET NOCOUNT ON
/** Where To Search **/
DECLARE @SearchSSIS BIT = 1
DECLARE @SearchObjectNames BIT = 1
DECLARE @SearchObjectDefinition BIT = 1
DECLARE @SearchAgentJobs BIT = 1
/** What are you looking for? **/
DECLARE @ObjectsToSearchFor TABLE
(
TextString VARCHAR(200) NULL
)
INSERT INTO @ObjectsToSearchFor
(TextString)
SELECT srvname FROM sys.sysservers WHERE srvname <> @@SERVERNAME
UNION
SELECT datasource FROM sys.sysservers WHERE srvname <> @@SERVERNAME
UNION
SELECT srvnetname FROM sys.sysservers WHERE srvnetname <> @@SERVERNAME
UNION
SELECT 'OPENROWSET'
UNION
SELECT 'OPENQUERY'
UNION
SELECT 'OPENDATASOURCE'
/*************************************************************************************************************************
Shouldn't need to mess with anything below this line.
**************************************************************************************************************************/
/** Handle Recursion on #SearchResults.
Remember to save your data first!
If you don't want recursion, explitly drop #SearchResults between executions.
**/
IF OBJECT_ID('tempdb..#SearchResults') IS NOT NULL
BEGIN
DELETE FROM @ObjectsToSearchFor
--This allows for successive iterations to search for objects which reference objects which reference....
INSERT INTO @ObjectsToSearchFor
(TextString)
SELECT DISTINCT ObjectName
FROM #SearchResults
WHERE LocationFound IN ('sys.sql_modules')
AND ObjectName IS NOT NULL
SET @SearchObjectNames = 0
DROP TABLE #SearchResults
END
CREATE TABLE #SearchResults
(
DatabaseName SYSNAME NULL
, ObjectName SYSNAME NULL
, LocationFound VARCHAR(100) NULL
, MatchedOn VARCHAR(200) NULL
, Misc VARCHAR(100) NULL
)
/** Various declarations to hold results. **/
DECLARE @SearchTerm VARCHAR(200)
DECLARE @DatabaseName SYSNAME
DECLARE @Query NVARCHAR(4000)
-- Loop through search terms.
DECLARE curSearch CURSOR LOCAL STATIC FORWARD_ONLY
FOR SELECT O.TextString
FROM @ObjectsToSearchFor O
OPEN curSearch
FETCH NEXT FROM curSearch
INTO @SearchTerm
WHILE @@FETCH_STATUS = 0
BEGIN
--Begin Searching.
PRINT ('Looking for: ' + @SearchTerm)
/** This will loop over every database, only do it if you need it. **/
IF(@SearchObjectDefinition = 1 OR @SearchObjectNames = 1)
BEGIN
DECLARE curDatabases CURSOR LOCAL STATIC FORWARD_ONLY
FOR SELECT D.name
FROM sys.sysdatabases D
ORDER BY D.name
OPEN curDatabases
FETCH NEXT FROM curDatabases
INTO @DatabaseName
WHILE @@FETCH_STATUS = 0
BEGIN
--Look for object name matching...
IF(@SearchObjectNames = 1)
BEGIN
SET @Query = 'USE ' + QUOTENAME(@DatabaseName) + ';'
+ 'INSERT INTO #SearchResults'
+ ' SELECT DB_NAME()'
+ ', O.name'
+ ', ' + QUOTENAME('sys.objects', '''')
+ ', ' + QUOTENAME(@SearchTerm, '''')
+ ', O.type_desc'
+ ' FROM sys.objects O (NOLOCK)'
+ ' WHERE LOWER(O.name) LIKE ' + QUOTENAME('%' + LOWER(@SearchTerm) + '%', '''')
EXEC sp_executesql @Query
SET @Query = 'USE ' + QUOTENAME(@DatabaseName) + ';'
+ 'INSERT INTO #SearchResults'
+ ' SELECT DB_NAME()'
+ ', C.name'
+ ', ' + QUOTENAME('sys.columns', '''')
+ ', ' + QUOTENAME(@SearchTerm, '''')
+ ', O.name + O.type_desc'
+ ' FROM sys.columns C (NOLOCK) INNER JOIN sys.objects O ON O.[object_id] = C.[object_id]'
+ ' WHERE LOWER(C.name) LIKE ' + QUOTENAME('%' + LOWER(@SearchTerm) + '%', '''')
EXEC sp_executesql @Query
END --Looking for object names.
--Look for definitions.
--Find objects that have the string in question listed, but ignore where the name is what is being searched for.
IF(@SearchObjectDefinition = 1)
BEGIN
SET @Query = 'USE ' + QUOTENAME(@DatabaseName) + ';'
+ 'INSERT INTO #SearchResults'
+ ' SELECT DB_NAME()'
+ ', O.name'
+ ', ' + QUOTENAME('sys.sql_modules', '''')
+ ', ' + QUOTENAME(@SearchTerm, '''')
+ ', O.type_desc'
+ ' FROM sys.sql_modules M (NOLOCK) INNER JOIN sys.objects O ON O.[object_id] = M.[object_id]'
+ ' WHERE LOWER(M.[definition]) LIKE ' + QUOTENAME('%' + LOWER(@SearchTerm) + '%', '''')
+ ' AND NOT(LOWER(O.name) = LOWER(' + QUOTENAME(@SearchTerm, '''') + '))'
EXEC sp_executesql @Query
END --Looking for object definitions.
--Get next database
FETCH NEXT FROM curDatabases
INTO @DatabaseName
END
CLOSE curDatabases
DEALLOCATE curDatabases
END
--Look in SSIS packages.
IF(@SearchSSIS = 1)
BEGIN
;WITH SSISFolders AS
(
SELECT pf.folderid
, pf.parentfolderid
, CAST(pf.foldername AS VARCHAR(MAX)) AS foldername
FROM msdb.dbo.sysssispackagefolders pf (NOLOCK)
WHERE pf.parentfolderid IS NULL
UNION ALL
SELECT pf.folderid
, pf.parentfolderid
, Parent.foldername + '\' + ISNULL(CAST(pf.foldername AS VARCHAR(MAX)), '') AS foldername
FROM msdb.dbo.sysssispackagefolders pf (NOLOCK)
INNER JOIN SSISFolders Parent ON Parent.folderid = pf.parentfolderid
)
INSERT INTO #SearchResults
SELECT NULL
, ISNULL(F.foldername, '') + '\' + CAST(ISNULL(S.name, '') AS VARCHAR(MAX))
, 'msdb.dbo.syssispackages'
, @SearchTerm
, NULL
FROM msdb.dbo.sysssispackages S (NOLOCK)
LEFT OUTER JOIN SSISFolders F (NOLOCK) ON F.folderid = S.folderid
WHERE LOWER(CAST(CAST(CAST(CAST(packagedata AS VARBINARY(MAX)) AS VARCHAR(MAX)) AS XML) AS VARCHAR(MAX))) LIKE ('%' + @SearchTerm + '%')
END
--Look in SQL Agent Jobs
IF(@SearchAgentJobs = 1)
BEGIN
--Job Name
INSERT INTO #SearchResults
SELECT NULL
, J.name
, 'msdb.dbo.sysjobs'
, @SearchTerm
, 'Job Name'
FROM msdb.dbo.sysjobs J (NOLOCK)
WHERE LOWER(J.name) LIKE ('%' + @SearchTerm + '%')
--Job Description
INSERT INTO #SearchResults
SELECT NULL
, J.name
, 'msdb.dbo.sysjobs'
, @SearchTerm
, 'Job Description'
FROM msdb.dbo.sysjobs J (NOLOCK)
WHERE LOWER(J.[description]) LIKE ('%' + @SearchTerm + '%')
--Step Name.
INSERT INTO #SearchResults
SELECT NULL
, (ISNULL(J.name, '') + ' - ' + ISNULL(S.step_name, '') + ' (' + CAST(S.step_id AS VARCHAR(10)) + ')')
, 'msdb.dbo.sysjobsteps'
, @SearchTerm
, 'Step Name'
FROM msdb.dbo.sysjobsteps S (NOLOCK)
INNER JOIN msdb.dbo.sysjobs J (NOLOCK) ON J.job_id = S.job_id
WHERE LOWER(S.step_name) LIKE ('%' + @SearchTerm + '%')
--Step Code.
INSERT INTO #SearchResults
SELECT NULL
, (ISNULL(J.name, '') + ' - ' + ISNULL(S.step_name, '') + ' (' + CAST(S.step_id AS VARCHAR(10)) + ')')
, 'msdb.dbo.sysjobsteps'
, @SearchTerm
, 'Step Code'
FROM msdb.dbo.sysjobsteps S (NOLOCK)
INNER JOIN msdb.dbo.sysjobs J (NOLOCK) ON J.job_id = S.job_id
WHERE LOWER(S.command) LIKE ('%' + @SearchTerm + '%')
END --End Looking at Jobs.
--Get next search term.
FETCH NEXT FROM curSearch
INTO @SearchTerm
END
--Cleanup.
CLOSE curSearch
DEALLOCATE curSearch
/***************** Display Output **********************************/
--Display search terms.
SELECT O.TextString
, COUNT(S.MatchedOn) AS MatchCount
FROM @ObjectsToSearchFor O
LEFT OUTER JOIN #SearchResults S ON S.MatchedOn = O.TextString
GROUP BY O.TextString
--Display search results.
SELECT DISTINCT * FROM #SearchResults