以下代码在 SQL Server 中执行良好
create proc IamBrokenAndDontKnowIt as
select * from tablewhichdoesnotexist
当然,如果我尝试运行它,它会失败并显示
无效的对象名称“tablewhichdoesnotexist”。
有什么方法可以编译或验证存储过程是否有效?
你可以使用
SET FMTONLY ON
EXEC dbo.My_Proc
SET FMTONLY OFF
您需要以某种方式捕获错误,但构建一个快速实用程序应用程序并利用此功能来查找无效的存储过程并不需要太多时间。
我没有广泛使用过,所以我不知道是否有任何副作用需要注意。
当您尝试创建这样的存储过程时,您常常会收到警告消息。 它会说:
无法将行添加到当前存储过程的 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 英尺长的杆子来解决这个问题。
这是设计使然:
Erland Sommarskog 为 SET STRICT_CHECKS ON
提出了
MS Connect连接请求有一个
解决方法(我自己没有尝试过):
使用检查执行计划。唯一的 弱点是你可能需要 查看执行计划的权限 首先
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)你只需要检查引用的表,这是可行的。
BEGIN TRANSACTION
BEGIN TRY
EXEC (@storedproc)
ROLLBACK TRANSACTION
END TRY
BEGIN CATCH
WHILE @@TRANCOUNT > 0
ROLLBACK
END CATCH
测试数据库中所有存储过程的算法有点复杂,因为如果您有许多返回许多结果集的 SP,则需要解决 SSMS 限制。请参阅我的
博客以获取完整的解决方案。
这里有快挂功能来检查
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 表示存储过程/函数的名称
------------------------------------------------------------------------------
-- 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)
*/
------------------------------------------------------------------------------