有没有办法让 SQL Server 验证存储过程中的对象引用?

问题描述 投票:0回答:8

以下代码在 SQL Server 中执行良好

create proc IamBrokenAndDontKnowIt as
select * from tablewhichdoesnotexist

当然,如果我尝试运行它,它会失败并显示

无效的对象名称“tablewhichdoesnotexist”。

有什么方法可以编译或验证存储过程是否有效?

sql-server stored-procedures typechecking
8个回答
1
投票

你可以使用

SET FMTONLY ON
EXEC dbo.My_Proc
SET FMTONLY OFF

您需要以某种方式捕获错误,但构建一个快速实用程序应用程序并利用此功能来查找无效的存储过程并不需要太多时间。

我没有广泛使用过,所以我不知道是否有任何副作用需要注意。


1
投票

当您尝试创建这样的存储过程时,您常常会收到警告消息。 它会说:

无法将行添加到当前存储过程的 sysdepends,因为它依赖于缺少的对象“dbo.nonexistenttable”。存储过程仍将被创建。

出于某种原因,我现在没有收到它,我不确定它是否已更改,或者是否只是有一些设置可以打开或关闭警告。 无论如何,这应该会给您一个关于这里发生的事情的提示。

SQL Server 确实 跟踪依赖关系,但仅跟踪实际存在的依赖关系。 不幸的是,像 sp_depends

sp_MSdependencies
 这样的依赖技巧在这里不起作用,因为你正在寻找 
missing 依赖关系。

即使我们可以假设想出一种方法来检查这些缺失的依赖项,但编造一些东西来击败检查仍然是微不足道的:

CREATE PROCEDURE usp_Broken AS DECLARE @sql nvarchar(4000) SET @sql = N'SELECT * FROM NonExistentTable' EXEC sp_executesql @sql

你也可以尝试解析像“FROM xxx”这样的表达式,但也很容易打败它:

CREATE PROCEDURE usp_Broken2 AS SELECT * FROM NonExistentTable

确实没有任何可靠的方法来

检查存储过程并在不实际运行它的情况下检查是否缺少依赖项。

您可以像 Tom H 提到的那样使用

SET FMTONLY ON

,但请注意,这会改变程序“运行”的方式。  它不会捕捉到一些东西。  例如,没有什么可以阻止您编写这样的过程:

CREATE PROCEDURE usp_Broken3 AS DECLARE @TableName sysname SELECT @TableName = Name FROM SomeTable WHERE ID = 1 DECLARE @sql nvarchar(4000) SET @sql = N'SELECT * FROM ' + @TableName EXEC sp_executesql @sql

假设您有一个名为

SomeTable

 的真实表和一个包含 
ID = 1
 的真实行,但 
Name
 不引用任何表。  如果将其包装在 
SET FMTONLY ON/OFF
 块中,则不会出现任何错误。

这可能是一个人为的问题,但是

FMTONLY ON

 会执行其他奇怪的事情,例如执行 
IF
/
THEN
/
ELSE
 块的每个分支,这可能会导致其他意外错误,因此您必须非常具体你的错误处理。

测试程序的唯一真正可靠的方法是实际运行它,如下所示:

BEGIN TRAN BEGIN TRY EXEC usp_Broken END TRY BEGIN CATCH PRINT 'Error' END CATCH ROLLBACK

此脚本将在事务中运行该过程,对错误采取一些操作(在

CATCH

中),并立即回滚事务。  当然,即使这样也可能会产生一些副作用,例如如果它插入表(成功),则更改 
IDENTITY
 种子。  只是需要注意一些事情。

说实话,我不会用 50 英尺长的杆子来解决这个问题。


1
投票
否(但请继续阅读,请参阅最后一行)

这是设计使然:

延迟名称解析

Erland SommarskogSET STRICT_CHECKS ON 提出了

MS Connect

连接请求有一个

解决方法(我自己没有尝试过):

使用检查执行计划。唯一的 弱点是你可能需要 查看执行计划的权限 首先


0
投票
您可以运行

sp_depends

(请参阅 
http://msdn.microsoft.com/en-us/library/ms189487.aspx)并使用该信息来查询信息架构 (http://msdn.microsoft.com)。 com/en-us/library/ms186778.aspx) 查看所有对象是否存在。从我在这里读到的内容(http://msdn.microsoft.com/en-us/library/aa214346(SQL.80).aspx)你只需要检查引用的表,这是可行的。


0
投票
在 SQL 2005 或更高版本中,您可以使用事务和 try/catch 来测试存储过程:

BEGIN TRANSACTION BEGIN TRY EXEC (@storedproc) ROLLBACK TRANSACTION END TRY BEGIN CATCH WHILE @@TRANCOUNT > 0 ROLLBACK END CATCH

测试数据库中所有存储过程的算法有点复杂,因为如果您有许多返回许多结果集的 SP,则需要解决 SSMS 限制。请参阅我的

博客以获取完整的解决方案


0
投票
您可以检查information_schema.tables来检查表是否存在,然后执行代码

这里有快挂功能来检查

create function fnTableExist(@TableName varchar(64)) returns int as begin return (select count(*) from information_schema.tables where table_name=@tableName and Table_type='Base_Table') end go if dbo.fnTableExist('eObjects') = 0 print 'Table exist' else print 'no suchTable'

同样,您可以检查

中是否存在存储过程/函数

.INFORMATION_SCHEMA.ROUTINES.Routine_name 表示存储过程/函数的名称


0
投票
我刚刚发现VS2010的数据库项目会进行语法和名称引用检查。似乎是最好的选择。


0
投票
抱歉,我知道这是一篇旧帖子,但是这段代码应该有所帮助。 它是来自多个来源的代码和建议的汇编。 代码标题解释了代码的用途并列出了来源。 希望有帮助。

------------------------------------------------------------------------------ -- Find Database Object Issues for an SQL Server Database: -- 1) Refresh & Validate Database Objects (ValidationStatus, ValidationError1) - may not catch everything (dynamic SQL or latebound objects). Schema bound objects not supported. -- May be more than 1 error, but a bug in sp_refreshsqlmodule causes only the 1st to return. -- 2) Check for Binding Errors (IsIncomplete = 1) - checks latebound objects (deferred name resolution). Problem is it may still return CTE aliases and temp tables as NULL references. -- 3) Lists missing references (missing_referenced_obj_name) -- 4) Find invalid object definitions (SQLModuleDefinition_CheckObjectName) that dont have the object name in the definition according to all object names. (eg: issue created by sp_rename - instead, drop & re-create object with new name) -- You could also use 'SET STRICT_CHECKS ON' to help find issues when creating sql modules (eg stored procedures). ------------------------------------------------------------------------------ -- Sources: -- https://stackoverflow.com/questions/22064284/validating-that-all-stored-procedures-are-valid -- https://stackoverflow.com/questions/55018130/validate-all-objects-after-renaming-a-table-in-sql-server -- https://stackoverflow.com/questions/3027399/how-to-check-all-stored-procedure-is-ok-in-sql-server -- https://www.sqlservercentral.com/forums/topic/sp_refreshsqlmodule-run-on-a-view-completely-changed-the-view-definition -- http://blogs.msdn.com/b/askjay/archive/2012/07/22/finding-missing-dependencies.aspx (comment) -- https://www.sommarskog.se/strict_checks.html -- http://technet.microsoft.com/en-us/library/bb677315(v=sql.110).aspx ------------------------------------------------------------------------------ DECLARE @L_CHECK_VALIDATION SMALLINT = 1 -- 1 = Refreshes & validates the sql objects (excludes schema bound objects) ,@L_CHECK_BINDING SMALLINT = 1 -- 1 = Checks for binding errors (is_complete = 1) for the sql objects ------------------------------------------------------------------------------ SET NOCOUNT ON ------------------------------------------------------------------------------ -- Table variable to store sql objects DECLARE @sqlObjects TABLE ( RowID INT IDENTITY(1,1) ,ObjectType NVARCHAR(MAX) ,ObjectName NVARCHAR(MAX) ,ObjectID INT ,IsSchemaBound SMALLINT ,ValidationStatus NVARCHAR(100) ,ValidationError1 NVARCHAR(200) ,IsIncomplete INT ) -- Obtain the list of sql objects INSERT INTO @sqlObjects( ObjectType, ObjectName, ObjectID, IsSchemaBound ) SELECT obj.[type_desc] AS ObjectType ,QUOTENAME(sch.[name]) + '.' +QUOTENAME(obj.[name]) AS ObjectName ,obj.[object_id] AS ObjectID ,OBJECTPROPERTY([object_id], 'IsSchemaBound') AS IsSchemaBound FROM sys.objects obj INNER JOIN sys.schemas sch ON sch.[schema_id] = obj.[schema_id] WHERE obj.[type_desc] IN ('SQL_STORED_PROCEDURE' ,'SQL_INLINE_TABLE_VALUED_FUNCTION' ,'SQL_SCALAR_FUNCTION' ,'SQL_TABLE_VALUED_FUNCTION' --,'SQL_TRIGGER' -- *** DO NOT USE without checking definitions before & after - as may re-activate any triggers that are disabled ***. ,'VIEW' ) AND obj.is_ms_shipped <> 1 -- ignore system objects --AND sch.[name] = 'APU' ORDER BY ObjectType ,ObjectName ------------------------------------------------------------------------------ -- Variables DECLARE @Count INT ,@Total INT ,@ObjectName NVARCHAR(MAX) ,@IsSchemaBound SMALLINT ,@TempCount INT SELECT @Count = 1 SELECT @Total = COUNT(*) FROM @sqlObjects -- Loop through all sql objects WHILE @Count <= @Total BEGIN SELECT @ObjectName = ObjectName ,@IsSchemaBound = IsSchemaBound FROM @sqlObjects WHERE RowID = @Count PRINT 'Checking... ' + @ObjectName -- 1) Refreshes & validates the sql objects (excludes schema bound objects) IF @L_CHECK_VALIDATION = 1 AND @IsSchemaBound <> 1 BEGIN BEGIN TRY -- Refresh & Validate the sql module/object -- Excludes schema-bound objects. If include them, may cause message: Metadata was not updated for the schema-bound object '...'. EXEC sp_refreshsqlmodule @ObjectName UPDATE @sqlObjects SET ValidationStatus = 'Passed', ValidationError1 = '' WHERE RowID = @Count END TRY BEGIN CATCH PRINT 'Validation failed (module does not compile) for : ' + @ObjectName + ', Error:' + ERROR_MESSAGE() + CHAR(13) -- Sometimes a transaction remains open in case an error appears, hence added ROLLBACK TRAN IF @@TRANCOUNT > 0 ROLLBACK TRAN UPDATE @sqlObjects SET ValidationStatus = 'Failed', ValidationError1 = ERROR_MESSAGE() WHERE RowID = @Count END CATCH END -- 2) Checks for binding errors (is_complete = 1) for the sql objects IF @L_CHECK_BINDING = 1 BEGIN BEGIN TRY -- The object or column has a binding error and is incomplete. SELECT @TempCount = COUNT(*) FROM sys.dm_sql_referenced_entities(@ObjectName, 'OBJECT') WHERE is_incomplete = 1 UPDATE @sqlObjects SET IsIncomplete = (CASE WHEN @TempCount > 0 THEN 1 ELSE 0 END) WHERE RowID = @Count END TRY BEGIN CATCH PRINT 'Check binding errors failed for : ' + @ObjectName + ', Error:' + ERROR_MESSAGE() + CHAR(13) -- Sometimes a transaction remains open in case an error appears, hence added ROLLBACK TRAN IF @@TRANCOUNT > 0 ROLLBACK TRAN END CATCH END SET @Count = @Count + 1 END -- SQL Objects with potential issues SELECT * FROM @sqlObjects WHERE ISNULL(ValidationStatus,'') = 'Failed' OR ISNULL(IsIncomplete,0) > 0 OR IsSchemaBound = 1 -- not fully checked as not compatible with sp_refreshsqlmodule ------------------------------------------------------------------------------ -- 3) List Missing References SELECT referencing_id ,QUOTENAME(OBJECT_SCHEMA_NAME(referencing_id)) + '.' +QUOTENAME(OBJECT_NAME(referencing_id)) AS referencing_obj_name ,referenced_id ,ISNULL(QUOTENAME(referenced_server_name + '.'),'') +ISNULL(QUOTENAME(referenced_database_name + '.'),'') +ISNULL(QUOTENAME(referenced_schema_name + '.'),'') +ISNULL(QUOTENAME(referenced_entity_name),'') AS missing_referenced_obj_name ,is_caller_dependent ,is_ambiguous FROM sys.sql_expression_dependencies WHERE referenced_id IS NULL AND referenced_entity_name NOT IN ('spt_values','dtproperties') -- from a different database (eg: master), but valid ORDER BY referencing_id ,referencing_obj_name ,referenced_id ------------------------------------------------------------------------------ -- 4) Avoid using sp_rename. When you change the name in SSMS, it executes sp_rename. -- sp_rename comes with a set of it's own issues explained in the documentation: sp_rename (Transact-SQL). -- Renaming a stored procedure, function, view, or trigger WILL NOT change the name of the corresponding object either in the definition column of sys.sql_modules / sys.all_sql_modules or using the OBJECT_DEFINITION built-in function. -- Therefore, we recommend that sp_rename not be used to rename these object types. Instead, drop and re-create the object with its new name. -- Find object definitions that dont have the object name within the definition according to all object names. -- Now this is not the best approach since any occurrence of that name within the def will exclude the result, but its a start. SELECT ao.[object_id] AS AllObjectsID ,ao.[type_desc] AS AllObjectsType ,QUOTENAME(SCHEMA_NAME(ao.[schema_id])) + '.' +QUOTENAME(ao.[name]) AS AllObjectsName ,ao.create_date ,asm.[object_id] AS SQLModuleID ,asm.[definition] AS SQLModuleDefinition_CheckObjectName -- may be incorrect (eg if sp_rename has been used) --,OBJECT_DEFINITION(asm.[object_id]) AS SQLModuleDefinition_CheckObjectName_v2 -- may be incorrect (eg if sp_rename has been used) FROM sys.all_sql_modules asm INNER JOIN sys.all_objects ao ON ao.[object_id] = asm.[object_id] WHERE asm.[definition] NOT LIKE '%' + ao.[name] + '%' ORDER BY AllObjectsType ,AllObjectsName ------------------------------------------------------------------------------ /* SELECT * FROM sys.all_objects WHERE [object_id] in (1486289446,1964195139) SELECT * FROM sys.all_sql_modules WHERE [object_id] in (1486289446,1964195139) -- may be incorrect (eg if sp_rename has been used) SELECT * FROM sys.sql_modules WHERE [object_id] in (1486289446,1964195139) -- may be incorrect (eg if sp_rename has been used) */ ------------------------------------------------------------------------------
    
© www.soinside.com 2019 - 2024. All rights reserved.