验证所有存储过程是否有效

时间:2014-02-27 09:30:57

标签: sql sql-server validation stored-procedures sql-server-2008-r2

背景
我的应用程序由SQL Server(2008 R2)备份,并且有相当多的SP,触发器等。
我的目标是确保程序启动时所有这些对象仍然有效 例如,如果我有一个调用存储过程A的存储过程B,如果有人将B的名称更改为C,我希望收到通知在Debug环境中运行我的应用程序时。

我尝试了什么?
所以,我想根据documentation使用sp_refreshsqlmodule返回0 (success) or a nonzero number (failure)

DECLARE @RESULT int 
exec @RESULT  = sp_refreshsqlmodule N'A' --In this case A is the SP name
SELECT @@ERROR
SELECT @RESULT 

所以我将SP B名称更改为C并运行脚本。 结果在哪里:

  • @@ERROR0
  • @RESULT为0
  • 我收到了一条消息:

      

    模块'A'取决于丢失的对象'B'。该模块将   仍然被创造;但是,它直到对象才能成功运行   存在。

  •   
  我的问题:
  我在这里遗漏了什么,不应该得到一个非零数字,表明出现了问题吗?

6 个答案:

答案 0 :(得分:2)

假设您的所有依赖项至少都是模式限定的,那么您似乎可以使用sys.sql_expression_dependencies。例如,运行此脚本:

create proc dbo.B
as
go
create proc dbo.A
as
exec dbo.B
go
select OBJECT_SCHEMA_NAME(referencing_id),OBJECT_NAME(referencing_id),
   referenced_schema_name,referenced_entity_name,referenced_id
from sys.sql_expression_dependencies
go
sp_rename 'dbo.B','C','OBJECT'
go
select OBJECT_SCHEMA_NAME(referencing_id),OBJECT_NAME(referencing_id),
   referenced_schema_name,referenced_entity_name,referenced_id
from sys.sql_expression_dependencies

sql_expression_dependencies的第一个查询将依赖关系显示为:

(No Column name) (No Column name) referenced_schema_name referenced_entity_name referenced_id
dbo              A                dbo                    B                      367340373

重命名后,第二个查询显示:

(No Column name) (No Column name) referenced_schema_name referenced_entity_name referenced_id
dbo              A                dbo                    B                      NULL

referenced_idNULL


因此,此查询可能会找到所有已损坏的存储过程(或可包含引用的其他对象):

select OBJECT_SCHEMA_NAME(referencing_id),OBJECT_NAME(referencing_id)
from
    sys.sql_expression_dependencies
group by
    referencing_id
having SUM(CASE WHEN referenced_id IS NULL THEN 1 ELSE 0 END) > 0

答案 1 :(得分:2)

你可以试试这个。它可能不是100%的架构(它拥有下面的所有者名称),因为它更多地基于我使用SQL Server 2000时,但我测试它2008,它基本上在所有的procs,函数,视图上运行alter语句。注释掉PRINT @objName +'似乎有效。只看到无效的过程,功能,视图...随意编辑你想要的任何部分!

DECLARE @objId INT
DECLARE @objName NVARCHAR(max)
DECLARE @owner NVARCHAR(255)
DECLARE @Def nvarchar(max)

DECLARE checker CURSOR FAST_FORWARD FOR
    SELECT
        id, name, USER_NAME(o.uid) owner 
    FROM sysobjects o 
    WHERE   o.type IN ('P', 'TR', 'V', 'TF', 'FN', 'IF')
            AND o.name <> 'RecompileSQLCode'

OPEN checker
FETCH FROM checker INTO @objId, @objName, @owner

WHILE @@FETCH_STATUS=0
BEGIN
    SELECT @Def = definition      
    FROM sys.sql_modules 
    WHERE object_id = @objId


       --print @objName
       --print @def
       SET @def = REPLACE(@def, 'create procedure','alter procedure')
       SET @def = REPLACE(@def, 'create PROC','alter PROC')
       SET @def = REPLACE(@def, 'create trigger','alter trigger')
       SET @def = REPLACE(@def, 'create function','alter function')
       SET @def = REPLACE(@def, 'create view','alter view')
    BEGIN TRANSACTION
        BEGIN TRY
            EXEC sp_executesql @def
            PRINT @objName + ' seems valid.'
        END TRY
        BEGIN CATCH
                PRINT 'Error: ' + @objName + ' : ' + CONVERT(nvarchar(10), ERROR_NUMBER()) + ' ' + ERROR_MESSAGE()
        END CATCH
    ROLLBACK

    FETCH NEXT FROM checker INTO @objId, @objName, @owner
END

CLOSE checker
DEALLOCATE checker

答案 2 :(得分:1)

这是一个过程,它将服务器上的所有存储过程编写为具有名称后缀的CREATE过程。该脚本为&#39; TEMP / Test&#39;创建了相应的DROP PROCEDURE。程序。

这不会确认存储过程是否引用了无效的表名,因为正常创建的存储过程不会对此进行验证。

BEGIN TRAN

--Creating temp able with copy of all procedures
 DECLARE @tTempProcedures TABLE
 (
    ProcedureName NVARCHAR(MAX),
    OriginalProcCreateSQL NVARCHAR(MAX),
    CreateNewProcSQL NVARCHAR(MAX),
    DropTestProcedureSQL NVARCHAR(MAX),
    AllInOneSQL NVARCHAR(MAX)
 )

 INSERT INTO @tTempProcedures
 SELECT 
         procedures.name                                AS  ProcedureName         
        ,syscomments.Text                               AS  OriginalProcCreateSQL
        ,REPLACE(syscomments.Text
                ,procedures.name
                ,procedures.name + '_TEST_CREATE')
            + ' GO'                                     AS  CreateNewProcSQL
        ,'DROP PROCEDURE ' 
            + procedures.name 
            + '_TEST_CREATE'                         AS  DropTestProcedureSQL

    ,'EXEC sp_executesql ' +''''''+
        REPLACE(
            REPLACE(syscomments.Text
                    ,procedures.name
                    ,procedures.name + '_TEST_CREATE')
                ,''''
                ,'''''')

        +''''''
        +  CHAR(10) + CHAR(13) 

        +  CHAR(10) + CHAR(13) 
        + 'EXEC sp_executesql ' +''''''+ 'DROP PROCEDURE ' 
        +  procedures.name 
        +  '_TEST_CREATE' +''''''
        +  CHAR(10) + CHAR(13)                  
                                                AS  AllInOneSQL
FROM
    syscomments

    Inner Join sys.procedures
    ON syscomments.id = procedures.OBJECT_ID






DECLARE cur CURSOR FOR 
SELECT AllInOneSQL FROM @tTempProcedures 

OPEN cur
DECLARE @AllInOneSQL NVARCHAR(MAX)
FETCH NEXT FROM cur INTO @AllInOneSQL

WHILE (@@FETCH_STATUS = 0)
BEGIN
    PRINT(@AllInOneSQL)
    EXEC sp_executesql @AllInOneSQL

    FETCH NEXT FROM cur INTO @AllInOneSQL
END

CLOSE cur
DEALLOCATE cur


ROLLBACK

警告:请小心使用任何DROP PROCEDURE语句。

注意:您还可以使用:&#34; SET NOEXEC ON&#34;然后执行该过程。如果该过程无效,您将收到错误。如果该过程有效,则在设置&#34; SET NOEXEC ON&#34;后,不会更新任何记录。然而,这很难自动化,因为您需要使用有效参数调用proc。

答案 3 :(得分:0)

确实很奇怪,我已经尝试过自己,sp_refreshsqlmodule的结果并不一致。甚至奇怪的是,如果出现错误,交易仍然保持打开状态,那就是我添加了ROLLBACK TRAN。这是一个替代方案:

DECLARE
    @is_refresh_ok AS BIT = 0
    , @error_message VARCHAR(MAX)

BEGIN TRY       
    EXEC sp_refreshsqlmodule '<SP name here>'       
    SET @is_refresh_ok = 1
END TRY
BEGIN CATCH
    SET @error_message = ERROR_MESSAGE()

    IF @@TRANCOUNT > 0
    ROLLBACK TRAN
END CATCH   

SELECT @is_refresh_ok, @error_message

如果您需要,这里的脚本是automatically refresh all stored procedures and functions in a database

答案 4 :(得分:0)

执行CREATE PROCEDURE语句时会解析SP的文本,但外部名称解析会延迟到运行时。这允许,例如,对象之间的循环依赖性,并避免必须具有结构的发布脚本。 Here's关于此主题的technet链接。我可以看到sp_refreshsqlmodule如何重新解析SP的文本并成功提取其元数据,报告0,但仍未将其绑定到依赖对象。 This是另一个涉及该主题的SO问题。

在其他情况下,我在SQL解析器(SO问题herehere)方面取得了一些成功。您可以捕获EXEC语句并列出关联的SP名称。

答案 5 :(得分:0)

SELECT
  OBJECT_NAME(referencing_id) AS [this sproc or view...],
  referenced_entity_name AS [... depends on this missing entity name]
FROM sys.sql_expression_dependencies
WHERE is_ambiguous = 0
  AND OBJECT_ID(referenced_entity_name) IS NULL
  AND referenced_entity_name NOT IN
    (SELECT Name FROM sys.types WHERE is_user_defined = 1)
ORDER BY OBJECT_NAME(referencing_id), referenced_entity_name

我希望这会有所帮助。 -Thomas