IF OBJECT_ID('dbo.RunCheckUntrustedForeignKeys', 'P') IS NULL
EXECUTE ('CREATE PROCEDURE dbo.RunCheckUntrustedForeignKeys AS SELECT 1');
GO


ALTER PROCEDURE dbo.RunCheckUntrustedForeignKeys(
    @DbName         VARCHAR(256) = NULL 
)
AS
/*
Author: Jefferson Elias
Original link: http://www.sqlshack.com/managing-untrusted-foreign-keys/
Depends: usp_GetUntrustedForeignKeys
Exapmle: exec dbo.RunCheckUntrustedForeignKeys
*/
BEGIN

    SET NOCOUNT ON;
    
    -- general purpose variables
    DECLARE @tsql                           nvarchar(max);            -- text to execute via dynamic SQL
	DECLARE @LineFeed                       CHAR(2);
    DECLARE @DbLoggerExists                 BIT;
    DECLARE @ErrorLogMsg                    VARCHAR(MAX);
	DECLARE @ProcDbId				        INT;
    DECLARE @CorrectedCount                 INT;
    DECLARE @FailedCount                    INT;
    DECLARE @ErrorNumber	                INT             = ERROR_NUMBER();
    DECLARE @ErrorLine		                INT             = ERROR_LINE();
    DECLARE @ErrorMessage	                NVARCHAR(4000)  = ERROR_MESSAGE();
    DECLARE @ErrorSeverity	                INT             = ERROR_SEVERITY();
    DECLARE @ErrorState		                INT             = ERROR_STATE();    
    
    -- procedure-sepecific variables
    DECLARE @CurrentDb		                VARCHAR(256);
    DECLARE @CurrentSchemaName		        VARCHAR(256);
    DECLARE @CurrentTableName		        VARCHAR(256);
    DECLARE @CurrentForeignKeyName		    VARCHAR(256);
    
    -- initial settings
	SELECT 
        @ProcDbId = DB_ID(), 
        @LineFeed = CHAR(13) + CHAR(10) ,
        @CorrectedCount = 0,
        @FailedCount = 0
    ;

    -- Logging execution
    SELECT 
        @DbLoggerExists = 1 
    FROM sys.objects 
    WHERE object_id = OBJECT_ID(N'[Common].[LogMessage]') AND type in (N'P') ;
    
    if(@DbLoggerExists is not null)
    BEGIN
        SET @ErrorLogMsg = 'Procedure started';
        SET @tsql = 'exec [Common].[LogMessage] @ApplicationName = ''$(SolutionName)'',@LogLevel = ''INFO'',@DatabaseID = @DbID,@ObjectID = @ObjID,@LogMessage = @ErrorLogMsg' ;
        exec sp_executesql @tsql, N'@ObjID INT,@ErrorLogMsg VARCHAR(max), @DbID INT',@ObjID=@@PROCID,@ErrorLogMsg =@ErrorLogMsg ,@DbID = @ProcDbId;
    END;
    ELSE
    BEGIN
        PRINT 'Starting procedure ' + OBJECT_NAME(@@PROCID);
    END;
    
    IF(OBJECT_ID('tempdb..##UntrustedForeignKeysData') IS NOT NULL)
    BEGIN
        EXEC sp_executesql N'DROP TABLE ##UntrustedForeignKeysData;';
    END;
    
    BEGIN TRY 
        
        -- building the list of untrusted foreign key based on parameters
        
        exec dbo.GetUntrustedForeignKeys @DbName = @DbName, @_NoTempTableDropAtTheEnd = 1 , @_NoSelectOnTempTable = 1;
        
        -- Now let's loop and try to resolve untrusted foreign keys.
        
        DECLARE GetFKs CURSOR LOCAL FOR 
            SELECT 
                DbName,SchemaName,TableName,ForeignKeyName,DDL2Resolve
            FROM ##UntrustedForeignKeysData
        ;
        
        OPEN GetFKs;
        
        FETCH NEXT FROM GetFKs INTO @CurrentDb,@CurrentSchemaName,@CurrentTableName,@CurrentForeignKeyName,@tsql;
        
        WHILE @@FETCH_STATUS = 0
        BEGIN
            BEGIN TRY
                
                -- run the corrective query
                exec sp_executesql @tsql;
                
                -- log its success 
                SET @ErrorLogMsg =  'Foreign key ' + QUOTENAME(@CurrentForeignKeyName) + ' on table ' + QUOTENAME(@CurrentDb) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentTableName) + ' checked successfully.';
                
                if(@DbLoggerExists = 1)
                BEGIN
                    SET @tsql = 'exec [Common].[LogMessage] @ApplicationName = ''$(SolutionName)'',@LogLevel = ''INFO'',@DatabaseID = @DbID,@ObjectID = @ObjID,@LogMessage = @ErrorLogMsg' ;
                    exec sp_executesql @tsql, N'@ObjID INT,@ErrorLogMsg VARCHAR(max),@DbID INT',@ObjID=@@PROCID,@ErrorLogMsg =@ErrorLogMsg,@DbID = @ProcDbId ;
                END;
                ELSE 
                BEGIN
                    PRINT @ErrorLogMsg ;
                END;
                
                -- increment for summary
                SET @CorrectedCount = @CorrectedCount + 1 ;
                
            END TRY 
            BEGIN CATCH 
                -- an error occurred during the corrective action
                IF @@TRANCOUNT > 0
                BEGIN 
                    -- just to be sure 
                    ROLLBACK TRANSACTION;        
                END;
                    
                -- getting back error 
                SET @ErrorNumber    = ERROR_NUMBER();
                SET @ErrorLine      = ERROR_LINE();
                SET @ErrorMessage   = ERROR_MESSAGE();
                SET @ErrorSeverity  = ERROR_SEVERITY();
                SET @ErrorState     = ERROR_STATE(); 
                
                SET @ErrorLogMsg =  'Unable to check foreign key ' + QUOTENAME(@CurrentForeignKeyName) + ' on table ' + QUOTENAME(@CurrentDb) + '.' + QUOTENAME(@CurrentSchemaName) + '.' + QUOTENAME(@CurrentTableName) + @LineFeed +
                                    'Query used : ' + @LineFeed+
                                    @tsql + @LineFeed +
                                    'Message ID ' + CONVERT(VARCHAR,@ErrorNumber) + ' caught at Line ' + CONVERT(VARCHAR,@ErrorLine) + @LineFeed +
                                    'With Severity ' + CONVERT(VARCHAR,@ErrorSeverity) + ' and State ' + CONVERT(VARCHAR,@ErrorState) + @LineFeed +
                                    'Full Message = ' + @LineFeed +
                                    @ErrorMessage ;
                
                -- increment for summary
                SET @FailedCount = @FailedCount + 1;
                
                -- Log the error 
                if(@DbLoggerExists = 1)
                BEGIN
                    SET @tsql = 'exec [Common].[LogMessage] @ApplicationName = ''$(SolutionName)'',@LogLevel = ''ERROR'',@DatabaseID = @DbID,@ObjectID = @ObjID,@LogMessage = @ErrorLogMsg' ;
                    exec sp_executesql @tsql, N'@ObjID INT,@ErrorLogMsg VARCHAR(max),@DbID INT',@ObjID=@@PROCID,@ErrorLogMsg =@ErrorLogMsg,@DbID = @ProcDbId ;
                    RAISERROR(@ErrorLogMsg,10,1);
                END;
                ELSE 
                BEGIN
                    PRINT @ErrorLogMsg ;
                END;
            END CATCH;
            
            FETCH NEXT FROM GetFKs INTO @CurrentDb,@CurrentSchemaName,@CurrentTableName,@CurrentForeignKeyName,@tsql;
        END;
        
    END TRY 
    BEGIN CATCH
        IF @@TRANCOUNT > 0
        BEGIN 
            ROLLBACK TRANSACTION;
        END;
            
        SET @ErrorNumber    = ERROR_NUMBER();
        SET @ErrorLine      = ERROR_LINE();
        SET @ErrorMessage   = ERROR_MESSAGE();
        SET @ErrorSeverity  = ERROR_SEVERITY();
        SET @ErrorState     = ERROR_STATE(); 
            
        SET @ErrorLogMsg =  'Message ID ' + CONVERT(VARCHAR,@ErrorNumber) + ' caught at Line ' + CONVERT(VARCHAR,@ErrorLine) + CHAR(13) + CHAR(10) +
                            'With Severity ' + CONVERT(VARCHAR,@ErrorSeverity) + ' and State ' + CONVERT(VARCHAR,@ErrorState) + CHAR(13) + CHAR(10) +
                            'Full Message = ' + CHAR(13) + CHAR(10) +
                            @ErrorMessage ;

        if(EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N'[Common].[LogMessage]') AND type in (N'P')))
        BEGIN
            SET @tsql = 'exec [Common].[LogMessage] @ApplicationName = ''$(SolutionName)'',@LogLevel = ''ERROR'',@DatabaseID = @DbID,@ObjectID = @ObjID,@LogMessage = @ErrorLogMsg' ;
            exec sp_executesql @tsql, N'@ObjID INT,@ErrorLogMsg VARCHAR(max),@DbID INT',@ObjID=@@PROCID,@ErrorLogMsg =@ErrorLogMsg,@DbID = @ProcDbId ;
            RAISERROR(@ErrorLogMsg,10,1);
        END;
        ELSE
            THROW ;
    END CATCH;
    
    -- Cleanups 
        
    IF(OBJECT_ID('tempdb..##UntrustedForeignKeysData') IS NOT NULL)
    BEGIN
        EXEC sp_executesql N'DROP TABLE ##UntrustedForeignKeysData;';
    END;
         
    IF (CURSOR_STATUS('local','GetFKs') > -1)
    BEGIN
        CLOSE GetFKs;
    END;         
    IF (CURSOR_STATUS('local','GetFKs') =-1)
    BEGIN
        DEALLOCATE GetFKs;
    END;
    
    -- logging the end of execution
	if(@DbLoggerExists is not null)
    BEGIN
        SET @ErrorLogMsg =  'Procedure completed.' + @LineFeed + 
                            'Summary: ' + @LineFeed + 
                            '    ' + CONVERT(VARCHAR,@CorrectedCount) + ' successful FK check(s).' + @LineFeed + 
                            '    ' + CONVERT(VARCHAR,@FailedCount)    + ' failed FK check(s).'
                          ;
        SET @tsql = 'exec [Common].[LogMessage] @ApplicationName = ''$(SolutionName)'',@LogLevel = ''INFO'',@DatabaseID = @DbID,@ObjectID = @ObjID,@LogMessage = @ErrorLogMsg' ;
        exec sp_executesql @tsql, N'@ObjID INT,@ErrorLogMsg VARCHAR(max),@DbID INT',@ObjID=@@PROCID,@ErrorLogMsg =@ErrorLogMsg,@DbID = @ProcDbId ;
    END;
END
GO
