【问题标题】:TSQL - create a stored proc inside a transaction statementTSQL - 在事务语句中创建存储过程
【发布时间】:2010-04-26 16:42:38
【问题描述】:

我有一个设置为投入生产的 sql 脚本。我已经将各种项目包装成单独的交易。在每个事务中,我们都创建了存储过程。我收到错误消息

消息 156,级别 15,状态 1,第 4 行 关键字“procedure”附近的语法不正确。

我创建了这个示例脚本来说明

Begin Try
Begin Transaction 
    -- do a bunch of add/alter tables here
    -- do a bunch of data manipulation/population here

    -- create a stored proc
  create procedure dbo.test
  as
  begin
    select * from some_table
  end
Commit  
End Try
Begin Catch
    Rollback  
    Declare @Msg nvarchar(max)
    Select @Msg=Error_Message();
    RaisError('Error Occured: %s', 20, 101,@Msg) With Log;
End Catch

该错误似乎暗示我无法在事务中创建存储过程,但我没有找到任何其他说明的文档(也许谷歌今天不友好)。

【问题讨论】:

  • 是的,对不起,我应该提到这一点。 sql 2008.
  • 我决定将 create proc 从事务中提取出来,并将 try/catch 放到它自己的部分中。我输入了一个“如果存在则删除”语句,并在存储过程创建周围添加了一个“如果存在则打印消息”语句。感谢所有的反馈,我从中学到了一些东西。

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


【解决方案1】:

尝试在EXEC('...') 中执行create procedure,如下所示:

Begin Try
Begin Transaction 
    -- do a bunch of add/alter tables here
    -- do a bunch of data manipulation/population here

    -- create a stored proc
  EXEC ('create procedure dbo.test
  as
  begin
    select * from some_table
  end')
Commit  
End Try
Begin Catch
    Rollback  
    Declare @Msg nvarchar(max)
    Select @Msg=Error_Message();
    RaisError('Error Occured: %s', 20, 101,@Msg) With Log;
End Catch

GO

【讨论】:

  • +1。这必须完成,因为create procedure... 调用必须是脚本中的第一条语句。使用EXEC 将其放入自己的批次中解决了这个问题。这也可用于创建创建或更新存储过程的脚本文件。对于过程脚本归档非常有用。
  • 在示例中,如果您直接运行它(为 some_table 输入一个真实的表名)并注释掉 ddl/dml 内容,错误仍然会发生,但创建过程是第一条语句,还是我误会了。
  • 代码对我来说运行良好,我不确定你在改变什么
  • KM,下面user1035920刚刚指出的问题你知道吗?
  • @knocte,您始终可以按照您的 EXEC() 检查系统表以查看该过程是否存在,如果不存在,则发出 RAISERROR() 以进入 CATCH 并回滚。
【解决方案2】:

您不能以这种方式编写脚本,因为有许多语句必须在它们自己的批处理中运行。相反,我建议做一些类似于 Red-Gate 的 SQL 比较如何构建其脚本的事情:

Set Xact_Abort On
GO
Begin Transaction 
GO
If object_id('tempdb..#tmpErrors') is not null
    Drop Table #tmpErrors
GO
Create Table #tmpErrors( [Error] int not null )
GO
Create Procedure dbo.Test
As
Begin
    --....
End
GO
If @@Error <> 0 AND @@TranCount >0 Rollback Transaction
GO
IF @@TranCount = 0 Begin Insert #tmpErrors (Error) Select 1 Begin Transaction End
GO

-- more statements

GO
If @@Error <> 0 AND @@TranCount >0 Rollback Transaction
GO
IF @@TranCount = 0 Begin Insert #tmpErrors (Error) Select 1 Begin Transaction End
GO

--.....

IF NOT EXISTS(SELECT * FROM #tmpErrors)
    BEGIN
        PRINT 'The database update succeeded'
        IF @@TRANCOUNT > 0 COMMIT TRANSACTION
    END
ELSE 
    BEGIN
        PRINT 'The database update failed'
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION
    END
GO

DROP TABLE #tmpErrors
GO

【讨论】:

  • SSMS 抱怨Error 不是#tmpErrors (Error) Select 1 中的列,应该是ID(或者Create Table #tmpErrors( Id int not null ) 应该是Create Table #tmpErrors( Error int not null )?
  • @SamHolder - 是的。这是我纠正的错字。如果你使用Id,那么 Create Table 语句显然应该使用Id。如果您选择Error,则需要针对该事实更新 Create Table 语句。
  • 感谢您的澄清。 +1 来自我。很好的答案。
  • 这是一个非常好的答案,非常感谢。你从哪里学来的?
  • @PleaseStopUpvotingMe - 正如我的帖子中提到的,这就是 Red-Gate 的 SQL 比较脚本其数据更改的方式。
【解决方案3】:

KM 提出的解决方案存在各种问题:

  • 如果您在 EXEC() 调用中的内容在语义上不正确(例如,您在存储过程中的 FROM 中放置了一个不存在的表),则该错误不会冒泡并且事务没有回滚。

  • 如果您在 EXEC() 调用中的内容在语法上不正确(例如,您将 SELECTT 而不是 SELECT 放入存储过程中),则事务似乎已回滚,但会出现一个完全神秘的错误:

    A transport-level error has occurred when receiving results from the server. (provider: TCP Provider, error: 0 - The specified network name is no longer available.)
    

所以我仍然不知道如何在事务中创建一个过程,但仍然让事务和 try-catch 有用。

【讨论】:

【解决方案4】:

我似乎记得您不能在事务中执行诸如创建、修改或删除数据库架构对象(包括存储过程)之类的操作(因为此类结构更改不是事务性的:您无法通过数据更改回滚它们)。

【讨论】:

  • 我从我的答案中测试了代码,它不仅创建了一个存储过程,而且如果你在代码中强制回滚,它也会回滚。
  • @KM:当然,这在 SQL Server 的较新版本中可能有所改变。
猜你喜欢
  • 2016-12-04
  • 1970-01-01
  • 1970-01-01
  • 2019-08-16
  • 2020-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多