语法检查所有存储过程?

时间:2009-07-24 13:27:14

标签: sql-server stored-procedures maintenance

我想确保所有存储过程在语法上仍然有效。 (如果有人重命名/删除表/列,就会发生这种情况。)

现在我检查所有存储过程语法的解决方案是进入企业管理器,选择列表中的第一个存储过程,并使用以下过程:

  1. 输入
  2. 替代+ C
  3. 逃逸
  4. 逃逸
  5. 向下箭头
  6. 转到1
  7. 它有效,但它非常繁琐。我想要一个名为

    的存储过程

    SyntaxCheckAllStoredProcedures

    就像我写的其他存储过程一样,它对视图做了同样的事情:

    RefreshAllViews


    为了每个人的利益,RefreshAllViews:

    RefreshAllViews.prc

    CREATE PROCEDURE dbo.RefreshAllViews AS
    
    -- This sp will refresh all views in the catalog. 
    --     It enumerates all views, and runs sp_refreshview for each of them
    
    DECLARE abc CURSOR FOR
         SELECT TABLE_NAME AS ViewName
         FROM INFORMATION_SCHEMA.VIEWS
    OPEN abc
    
    DECLARE @ViewName varchar(128)
    
    -- Build select string
    DECLARE @SQLString nvarchar(2048)
    
    FETCH NEXT FROM abc 
    INTO @ViewName
    WHILE @@FETCH_STATUS = 0 
    BEGIN
        SET @SQLString = 'EXECUTE sp_RefreshView '+@ViewName
        PRINT @SQLString
        EXECUTE sp_ExecuteSQL @SQLString
    
        FETCH NEXT FROM abc
        INTO @ViewName
    END
    CLOSE abc
    DEALLOCATE abc
    

    为了每个人的利益,将所有存储过程标记为需要重新编译的存储过程(标记重新编译的存储过程不会告诉您它是否在语法上有效):

    RecompileAllStoredProcedures.prc

    CREATE PROCEDURE dbo.RecompileAllStoredProcedures AS
    
    DECLARE abc CURSOR FOR
         SELECT ROUTINE_NAME
         FROM INFORMATION_SCHEMA.routines
        WHERE ROUTINE_TYPE = 'PROCEDURE'
    OPEN abc
    
    DECLARE @RoutineName varchar(128)
    
    -- Build select string once 
    DECLARE @SQLString nvarchar(2048)
    
    FETCH NEXT FROM abc 
    INTO @RoutineName
    WHILE @@FETCH_STATUS = 0 
    BEGIN
        SET @SQLString = 'EXECUTE sp_recompile '+@RoutineName
        PRINT @SQLString
        EXECUTE sp_ExecuteSQL @SQLString
    
        FETCH NEXT FROM abc
        INTO @RoutineName
    END
    CLOSE abc
    DEALLOCATE abc
    

    为了完整起见, UpdateAllStatistics 过程。这将通过执行完整数据扫描来更新数据库中的所有统计信息:

    RefreshAllStatistics.prc

    CREATE PROCEDURE dbo.RefreshAllStatistics AS
    
    EXECUTE sp_msForEachTable 'UPDATE STATISTICS ? WITH FULLSCAN'
    

9 个答案:

答案 0 :(得分:7)

您也可以“就地”执行此操作 - 无需获取所有创建语句。

除了设置NOEXEC ON之外,您还需要设置自己喜欢的SHOWPLAN_* ON(我使用SHOWPLAN_TEXT)。现在,您可以摆脱步骤2,只执行在步骤1中检索到的每个过程。

以下是使用单个存储过程的示例。你可以把它放到你最喜欢的循环中:

create procedure tests @bob int as 
select * from missing_table_or_view
go 

set showplan_text on; 
go 

set noexec on 

exec tests 

set noexec off 
go 
set showplan_XML off 
go 
drop procedure tests 
go

以上示例应生成以下输出:

  

Msg 208,Level 16,State 1,Procedure tests,Line 2   无效的对象名称'missing_table_or_view'。

答案 1 :(得分:3)

如果您使用的是sql 2008 r2或更低版本,请不要使用

设置NOEXEC ON

它只检查语法,而不检查表或列的存在等潜在错误。 而是使用:

设置FMTONLY

它会在尝试返回存储过程的元数据时进行完全编译。

对于2012年,您将需要使用存储过程: sp_describe_first_result_set

你也可以在Tsql中做一个完整的脚本来检查所有sp和视图,这只是一些工作。

UPDATE 我在tsql中编写了一个完整的解决方案,它通过所有用户定义的存储过程并检查语法。脚本很长,但可以在http://chocosmith.wordpress.com/2012/12/07/tsql-recompile-all-views-and-stored-proceedures-and-check-for-error/

找到

答案 2 :(得分:3)

KenJ建议的检查肯定是最好的,因为重新创建/更改方法没有找到所有错误。例如。

  • 由于查询提示而无法执行计划
  • 我甚至有一个SP引用了一个没有检测到错误的不存在的表。

请找到我的版本,使用下面的KenJ方法一次检查所有现有的SP。 AFAIK,它将检测将阻止SP执行的每个错误。

--Forces the creation of execution-plans for all sps.
--To achieve this, a temporary SP is created that calls all existing SPs.
--It seems like the simulation of the parameters is not necessary. That makes things a lot easier.
DECLARE @stmt NVARCHAR(MAX) = 'CREATE PROCEDURE pTempCompileTest AS ' + CHAR(13) + CHAR(10)
SELECT @stmt = @stmt + 'EXEC [' + schemas.name + '].[' + procedures.name + '];'
    FROM sys.procedures
        INNER JOIN sys.schemas ON schemas.schema_id = procedures.schema_id
    WHERE schemas.name = 'dbo'
    ORDER BY procedures.name

EXEC sp_executesql @stmt
GO

--Here, the real magic happens.
--In order to display as many errors as possible, XACT_ABORT is turned off.
--Unfortunately, for some errors, the execution stops anyway.
SET XACT_ABORT OFF
GO
--Showplan disables the actual execution, but forces t-sql to create execution-plans for every statement.
--This is the core of the whole thing!
SET SHOWPLAN_ALL ON
GO
--You cannot use dynamic SQL in here, since sp_executesql will not be executed, but only show the string passed in in the execution-plan
EXEC pTempCompileTest
GO
SET SHOWPLAN_ALL OFF
GO
SET XACT_ABORT ON
GO
--drop temp sp again
DROP PROCEDURE pTempCompileTest
--If you have any errors in the messages-window now, you should fix these...

答案 3 :(得分:1)

此外,您可能需要考虑使用Visual Studio Team System 2008 Database Edition,其中包括对构建项目中的所有存储过程进行静态验证,从而确保所有存储过程与当前架构保持一致。

答案 4 :(得分:1)

我知道这已经过时了,但是我创建了一个稍微不同的版本,它实际上重新创建了所有存储过程,因此如果它们无法编译就会抛出错误。这是使用SP_Recompile命令无法实现的。

CREATE PROCEDURE dbo.UTL_ForceSPRecompilation
(
    @Verbose BIT = 0
)
AS
BEGIN

    --Forces all stored procedures to recompile, thereby checking syntax validity.

    DECLARE @SQL NVARCHAR(MAX)
    DECLARE @SPName NVARCHAR(255)           

    DECLARE abc CURSOR FOR
         SELECT NAME, OBJECT_DEFINITION(o.[object_id])
         FROM sys.objects AS o 
         WHERE o.[type] = 'P'
         ORDER BY o.[name]

    OPEN abc

    FETCH NEXT FROM abc
    INTO @SPName, @SQL
    WHILE @@FETCH_STATUS = 0 
    BEGIN       

        --This changes "CREATE PROCEDURE" to "ALTER PROCEDURE"
        SET @SQL = 'ALTER ' + RIGHT(@SQL, LEN(@SQL) - (CHARINDEX('CREATE', @SQL) + 6))

        IF @Verbose <> 0 PRINT @SPName

        EXEC(@SQL)

        FETCH NEXT FROM abc
        INTO @SPName, @SQL
    END
    CLOSE abc
    DEALLOCATE abc  

END

答案 5 :(得分:1)

我知道这是一个老问题,但是当我找不到任何适合的时候,这是我的解决方案。

我需要在数据库中进行大量更改后验证我的存储过程和视图。

基本上我想要的是尝试使用当前程序和视图(而不是实际更改它们)来执行ALTER PROCEDURE和ALTER VIEW。

我写的这个效果相当不错。

注意!不要在实时数据库上执行,制作副本以进行验证,然后修复需要修复的内容。 sys.sql_modules也可能不一致,所以要特别小心。我不使用它来实际进行更改,只检查哪些不能正常工作。

DECLARE @scripts TABLE
(
    Name NVARCHAR(MAX),
    Command NVARCHAR(MAX),
    [Type] NVARCHAR(1)
)

DECLARE @name NVARCHAR(MAX),        -- Name of procedure or view
    @command NVARCHAR(MAX),         -- Command or part of command stored in syscomments
    @type NVARCHAR(1)               -- Procedure or view

INSERT INTO @scripts(Name, Command, [Type])
SELECT P.name, M.definition, 'P' FROM sys.procedures P 
JOIN sys.sql_modules M ON P.object_id = M.object_id

INSERT INTO @scripts(Name, Command, [Type])
SELECT V.name, M.definition, 'V' FROM sys.views V 
JOIN sys.sql_modules M ON V.object_id = M.object_id

DECLARE curs CURSOR FOR
SELECT Name, Command, [Type]  FROM @scripts

OPEN curs

FETCH NEXT FROM curs
INTO @name, @command, @type


WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        IF @type = 'P'
            SET @command = REPLACE(@command, 'CREATE PROCEDURE', 'ALTER PROCEDURE')
        ELSE
            SET @command = REPLACE(@command, 'CREATE VIEW', 'ALTER VIEW')


        EXEC sp_executesql @command
        PRINT @name + ' - OK'
    END TRY
    BEGIN CATCH
        PRINT @name + ' - FAILED: ' + CAST(ERROR_NUMBER() AS NVARCHAR(MAX)) + ' ' + ERROR_MESSAGE()
        --PRINT @command
    END CATCH

    FETCH NEXT FROM curs
    INTO @name, @command, @type
END

CLOSE curs 

答案 6 :(得分:0)

有点抽出的选项:

  1. 创建数据库的副本 (备份还原)。如果您的置信度很高,则可以在目标数据库上执行此操作。
  2. 使用SSMS编写所有脚本 存储过程到单个脚本文件中
  3. DROP所有程序
  4. 运行脚本以重新创建它们。任何无法创建的都会出错。
  5. 在这里有几个挑剔的陷阱,例如:

    • 你想要“如果proc存在 然后放下proc GO创建过程... GO“ 语法来分离每个程序。
    • 如果嵌套过程失败,则会失败 叫一个尚未过去的过程 (重新)建立。运行几个脚本 时间应该赶上(因为 正确地订购它们可能是真实的 疼痛)。
    • 其他更隐蔽的问题可能会突然出现,所以要小心。

    要快速删除10或1000个程序,请运行

    SELECT 'DROP PROCEDURE ' + schema_name(schema_id) + '.' +  name
     from sys.procedures
    

    选择输出,然后运行它。

    这假设您正在执行非常罕见的任务。如果你必须定期(每天,每周...),请告诉我们原因!

答案 7 :(得分:0)

无法从T-SQL或企业管理器中执行此操作,因此我必须从客户端代码中编写内容。我不会在这里发布所有代码,但诀窍是:

1)获取所有存储过程的列表

 SELECT ROUTINE_NAME AS StoredProcedureName
 FROM INFORMATION_SCHEMA.ROUTINES
 WHERE ROUTINE_TYPE = 'PROCEDURE' --as opposed to a function
 ORDER BY ROUTINE_NAME

2)获取存储过程create T-SQL:

select
   c.text
from dbo.syscomments c
where c.id = object_id(N'StoredProcedureName')
order by c.number, c.colid
option(robust plan)

3)运行带有NOEXEC的create语句,以便检查语法,但它实际上并不尝试创建存储过程:

connection("SET NOEXEC ON", ExecuteNoRecords);
connection(StoredProcedureCreateSQL, ExecuteNoRecords);
connection("SET NOEXEC ON", ExecuteNoRecords);

答案 8 :(得分:0)

这是一个处理多个模式的修正案

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[RefreshAllViews] AS

-- This sp will refresh all views in the catalog. 
--     It enumerates all views, and runs sp_refreshview for each of them

DECLARE abc CURSOR FOR
     SELECT TABLE_SCHEMA+'.'+TABLE_NAME AS ViewName
     FROM INFORMATION_SCHEMA.VIEWS
OPEN abc

DECLARE @ViewName varchar(128)

-- Build select string
DECLARE @SQLString nvarchar(2048)

FETCH NEXT FROM abc 
INTO @ViewName
WHILE @@FETCH_STATUS = 0 
BEGIN
    SET @SQLString = 'EXECUTE sp_RefreshView ['+@ViewName+']'
    PRINT @SQLString
    EXECUTE sp_ExecuteSQL @SQLString

    FETCH NEXT FROM abc
    INTO @ViewName
END
CLOSE abc
DEALLOCATE abc
GO