背景
我的应用程序由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
并运行脚本。
结果在哪里:
@@ERROR
是0
@RESULT
为0
模块'A'取决于丢失的对象'B'。该模块将 仍然被创造;但是,它直到对象才能成功运行 存在。
答案 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_id
为NULL
。
因此,此查询可能会找到所有已损坏的存储过程(或可包含引用的其他对象):
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)
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问题here和here)方面取得了一些成功。您可以捕获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