10

I'm trying to test if all synonyms on a database refer to valid objects, using the SQL batch script below, as part of a clean-up effort on our databases. This script just performs a query against the synonym, and printing out any errors it encounters. This works fine on views and tables, but not on sprocs.

SET NOCOUNT ON

DECLARE @currentId INT;
DECLARE @currentSynonym VARCHAR(255);

SELECT @currentId = MIN(id) FROM sysobjects WITH (NOLOCK) WHERE [xtype]='SN';

WHILE @currentId IS NOT NULL
BEGIN
    SELECT TOP(1) @currentSynonym = name FROM sysobjects WITH (NOLOCK) WHERE id = @currentId;

    PRINT '';
    PRINT '------------------------------------------------------------';
    PRINT @currentSynonym;
    PRINT '------------------------------------------------------------';

    BEGIN TRY
    EXEC('SELECT Top(1) NULL FROM ' + @currentSynonym + ' WITH (NOLOCK);');
        PRINT 'Synonym is valid.';
    END TRY
    BEGIN CATCH 
        PRINT ERROR_MESSAGE();
    END CATCH

    SELECT @currentId = MIN(id) FROM sysobjects WITH (NOLOCK) WHERE [xtype]='SN' AND id > @currentId;
END

SET NOCOUNT OFF

Is there's an command that I can perform on a sproc, table, or view that will throw an error if it doesn't exist?

The synonyms can either point to an object on a linked server or an object on the current @@SERVERNAME, so I can't really just query sys.procedures in the catch block to see if it's there. I'd have to know if the synonym points to a linked server, and then I'd have to query that server's sys.procedures view.

I found a similar question, How can I check if the table behind a synonym exists, but it isn't quite what I'm asking. Everything else I found was Oracle specific.


UPDATE: The script below works for my needs. Thank's to @kenny-evitt for the information I needed to form it.

SET NOCOUNT ON

DECLARE -- for looping through sys.synonyms
    @currentSynonym VARCHAR(255), 

    -- for parsing out [ServerName].[DatabaseName].[SchemaName].[ObjectName] from sys.synonyms.base_object_name
    @baseObjectName NVARCHAR(1035),
    @lastDelimiterIndex INT,                
    @lastToken NVARCHAR(255),
    @sServer NVARCHAR (255),
    @sDatabase NVARCHAR(255),
    @sSchema NVARCHAR(255),
    @sObject NVARCHAR(255),                 

    -- for testing if synonym points to an existing object
    @sql NVARCHAR(1035),
    @objectCount INT, 

    -- for output formatting
    @newLine NVARCHAR(2), 
    @tab NVARCHAR(4),
    @validSynonyms NVARCHAR(MAX), 
    @invalidSynonyms NVARCHAR(MAX);

SET @validSynonyms = '';
SET @invalidSynonyms = '';
SET @newLine = CHAR(13) + CHAR(10);
SET @tab = '    ';

/* Loop through this DB's sys.synonyms view */
SELECT @currentSynonym = MIN(name) FROM sys.synonyms WITH (NOLOCK);

WHILE @currentSynonym IS NOT NULL
BEGIN
    SET @sObject = NULL;
    SET @sSchema = NULL;
    SET @sDatabase = NULL;
    SET @sServer = NULL;

    /* Parse out [server].[database].[schema].[object] from sys.synonyms.base_object_name */
    SELECT @baseObjectName = RTRIM(base_object_name) FROM sys.synonyms WITH (NOLOCK) WHERE name = @currentSynonym;              

    WHILE LEN(@baseObjectName) > 0
    BEGIN
        SET @lastToken = NULL;
        SET @lastDelimiterIndex = CHARINDEX('.', @baseObjectName, 1) + 1;

        -- Find the last token in @manipulated string, 
        -- Do this Right-to-Left, as the database and/or server may not be in sys.synonyms.base_object_name
        WHILE (CHARINDEX('.', @baseObjectName, @lastDelimiterIndex) > 0)
        BEGIN
            SET @lastDelimiterIndex = CHARINDEX('.', @baseObjectName, @lastDelimiterIndex) + 1;
        END
        SET @lastToken = SUBSTRING(@baseObjectName, @lastDelimiterIndex - 1, LEN(@baseObjectName) - @lastDelimiterIndex + 3);                       

        -- Kind of kludgy, but I put the $ character at the end of the string and @lastToken,
        -- so that if 2 of the values match (i.e. object and database, object and schema, whatever) only the last one
        -- is replaced.
        SET @lastToken = @lastToken + '$';
        SET @baseObjectName = @baseObjectName + '$';
        SET @baseObjectName = REPLACE(@baseObjectName, @lastToken, '');

        SET @lastToken = REPLACE(@lastToken, '.', '');
        SET @lastToken = REPLACE(@lastToken, '[', '');
        SET @lastToken = REPLACE(@lastToken, ']', '');
        SET @lastToken = REPLACE(@lastToken, '$', '');              

        IF @sObject IS NULL
            SET @sObject = @lastToken;
        ELSE IF @sSchema IS NULL
            SET @sSchema = @lastToken;
        ELSE IF @sDatabase IS NULL
            SET @sDatabase = @lastToken;
        ELSE IF @sServer IS NULL
            SET @sServer = @lastToken;
    END

    IF @sDatabase IS NULL
        SET @sDatabase = DB_NAME();
    IF @sServer IS NULL
        SET @sServer = @@SERVERNAME;

    /* End of token sys.synonyms.base_object_name parsing */            

    /* Query for the existence of the object on the database the synonym's object should be on. */          
    BEGIN TRY
        SET @sql = N'SELECT @count = Count(1) FROM [' + @sServer + '].[' + @sDatabase + '].sys.sysobjects WITH (NOLOCK) WHERE [name] = ''' + @sObject + ''';';

        EXECUTE sp_executesql @sql,
             N'@count INT OUTPUT',
             @count = @objectCount OUTPUT;

            If @objectCount > 0
                SET @validSynonyms = @validSynonyms + @tab + N'* ' +  @currentSynonym + @newLine;                       
            ELSE                
                SET @invalidSynonyms = @invalidSynonyms + @tab + N'* ' + @currentSynonym + @newLine;
    END TRY
    BEGIN CATCH
        SET @invalidSynonyms = @invalidSynonyms + @tab + N'* ' + @currentSynonym + ' =>' + @newLine;
        SET @invalidSynonyms = @invalidSynonyms + @tab + @tab + ERROR_MESSAGE() + @newLine;
    END CATCH


    SELECT @currentSynonym = MIN(name) FROM sys.synonyms WITH (NOLOCK) WHERE name > @currentSynonym;
END
/*End of sys.synonym Loop*/

PRINT 'Invalid Synonyms:' + @newLine + @newLine;
PRINT @invalidSynonyms;
PRINT @newLine + 'Valid Synonyms:' + @newLine + @newline;
PRINT @validSynonyms;


SET NOCOUNT OFF
Community
  • 1
  • 1
JustinP8
  • 1,353
  • 1
  • 14
  • 33
  • Just to clarify, you want to check if the *object*, e.g. stored procedure, view, *or table*, **to which a synonym points** exists or not, right? I thought you were asking about checking everything that might be *using* a synonym because your title says "... performed **against** a sproc, table, or view" – it seemed like you were checking stored procedures, tables, and views. – Kenny Evitt May 15 '14 at 21:14
  • Sorry for the confusion, your clarification is correct. Please feel free to edit into a better title. I'm honestly not sure how to phrase it clearly. – JustinP8 May 15 '14 at 21:58

2 Answers2

16

That looks like an awfully long script for such a simple check; what's wrong with just:

if exists (select * from sys.synonyms where name = @currentSynonym and object_id(base_object_name) is not null)
begin
  --Add logic here
end
Andy Moignard
  • 161
  • 1
  • 3
  • It seems like OP wanted to validate all synonyms but that could be done with a query similar to the subquery passed to `exists` in your example. – Kenny Evitt Oct 24 '19 at 19:02
  • 1
    @KennyEvitt sure, they could just go with something like: select name, case when object_id(base_object_name) is null then 'Invalid' else 'Valid' [status] from sys.synonyms. – Andy Moignard Oct 31 '19 at 15:00
8

You can use OBJECT_ID with the base_object_name column of sys.synonyms to test whether the base objects exist:

SELECT  [Schema] = sch.name,
        [Name] = syn.name,
        syn.base_object_name,
        [Base object exists?] = CASE WHEN OBJECT_ID(syn.base_object_name) IS NOT NULL THEN 'Yes' ELSE 'No' END
FROM    sys.synonyms syn
        JOIN sys.schemas AS sch ON syn.schema_id = sch.schema_id
ORDER BY [Base object exists?], [Schema], [Name];

The query was adapted from this answer to this same question.

Kenny Evitt
  • 9,291
  • 5
  • 65
  • 93
  • Actually, I'm more interested in seeing if the create procedure has happened, I don't really care if the procedure is broken at this point. I think if I could determine the server and database a synonym points to, I could check check if the object exists via sysobjects view ON that linked server and DB, as our synonyms are usually named sn[ActualObjectName]. – JustinP8 May 15 '14 at 21:02
  • Check `sys.procedures` for procedures that exist i.e. have been created. – Kenny Evitt May 15 '14 at 21:07
  • I'll have to check the sys.procedures on the linked server if the snyonym is pointing to a sproc on a linked server, correct? If so, can you...er...expand a synonym (i.e. expand snSomeSynonym = '[SomeServer].[schema].[SomeObject]'? – JustinP8 May 15 '14 at 21:10
  • Thank you, I'm going to try this technique out. – JustinP8 May 15 '14 at 22:00
  • Thank you for your help. I've updated the question with the script I formed using the information you provided. – JustinP8 May 16 '14 at 19:35
  • Thank for this script ! – SuperPoney Jan 07 '22 at 13:12