【问题标题】:error in multi batch transactional sql script多批处理事务 sql 脚本中的错误
【发布时间】:2015-07-29 12:44:20
【问题描述】:
  BEGIN TRAN
  SET XACT_ABORT ON

  GO

  BEGIN TRY
   IF OBJECT_ID('dbo.Offer_GetByStudyId', 'p') IS NULL
  EXEC ('CREATE PROCEDURE Offer_GetByStudyId as select 1')
  END TRY

 BEGIN CATCH
 THROW;
  END CATCH

 GO
 IF @@error <> 0 and @@trancount > 0 ROLLBACK
 IF @@trancount = 0 BEGIN SET NOCOUNT ON; SET NOEXEC ON; END
 GO

  BEGIN TRY

  ALTER PROCEDURE dbo.Offer_GetByStudyId
  @StudyId NVARCHAR(MAX) = NULL
  AS
 BEGIN
  DECLARE @Conditions NVARCHAR(MAX) = '';

IF @StudyId IS NOT NULL
BEGIN
    SET @Conditions = @Conditions + ' AND o.StudyId = ' + cast(@StudyId as varchar(10))
END

DECLARE @sql NVARCHAR(MAX) = 'SELECT
    o.StudyId as StudyId,
    o.SampleId as SampleId,
    o.Status as Status,
    o.Title as Title,
    o.Topic as Topic,
    o.Description as Description,
    o.TestOffer as TestOffer,
    T.CPI as CPI
    FROM Offers o
    LEFT JOIN [dbo].[Terms] T ON (o.[Id] = T.[OfferId]) AND T.Active = 1
    WHERE 1 = 1' + @Conditions

    EXEC(@sql)
END
END TRY
BEGIN CATCH
    THROW;
END CATCH

这是我的 SQL 脚本,我试图让一个多批处理脚本作为单个事务运行,所以如果一个语句失败,所有语句都将回滚。但是我在这里不断收到此错误:

BEGIN 附近的语法不正确。期待外部

他们所说的开始是后面的:

@StudyId NVARCHAR(MAX) = NULL 

【问题讨论】:

  • 在另一个过程中改变一个过程有什么意义?
  • 您正在尝试在事务中进行程序安装?我不明白这一点,因为 create 只是在执行一个虚拟过程,如果 alter 过程失败,它将把旧版本留在数据库中。
  • 另外,您的过程容易受到 SQL 注入的攻击。您应该使用 sp_executesql 和变量,而不是 exec 并将值连接到 sql 中。

标签: sql-server


【解决方案1】:

您需要删除“GO”语句。这些不是 TSQL 语言的一部分,它们只是告诉 SSMS/SQLCMD 上面的批处理应该执行的语句。我不清楚跨多个 GO 语句拆分事务的行为。我将开始删除“GO”。

https://msdn.microsoft.com/en-us/library/ms188037.aspx

【讨论】:

  • 批处理和事务在 SQL Server 中是完全分开的。单个事务可以包含多个批次。一个批次可以包含多个事务。
【解决方案2】:

如果您从 TRY CATCH 中取出声明 ALTER PROCEDURE dbo.Offer_GetByStudyId (Line 22 - 47),它将起作用。
即删除TRY CATCH (Line 20 and Line 48 - 51)

【讨论】:

    【解决方案3】:

    我意识到我已经把答案埋在下面了,所以我会把它放到顶部以使其更清楚:你不能包装任何流控制语句来尝试创建或更改过程


    一个更简单的例子演示了这个问题:

    create procedure ABC
    as
    go
    select * from sys.objects
    alter procedure ABC as
    

    哪个产生了消息:

    Msg 111, Level 15, State 1, Procedure ABC, Line 2
    'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
    

    这是为CREATE PROCEDURE 明确记录的,但由于某种原因不适用于ALTER PROCEDURE

    CREATE PROCEDURE 语句不能与单个批处理中的其他 Transact-SQL 语句组合。

    因此结果是,您不能将任何流控制语句包装在尝试创建或更改过程的过程中。


    原因其实很简单 - BEGINEND 不是必需 围绕存储过程的主体 - 而且一个存储过程实际上可以包含多个“顶级”@ 987654330@/END对:

    create procedure ABC as
    begin 
        select * from sys.objects
    end
    begin 
        select * from sys.columns
    end
    

    很好 - 所以 SQL Server 在定义存储过程时知道其范围的唯一方法是“从CREATE/ALTERPROCEDURE 直到批处理结束。”

    【讨论】:

    • 如何修复它,同时仍然保持逻辑,这是一个多批次可滚动事务
    • @GeorgesL - 您可以将整个过程定义包装为一个字符串并处理所有引号转义,因此您可以EXEC 它,就像您使用CREATE 一样,或者您可以接受TRY/CATCH 没有太多价值并将其删除。我通常倾向于第二条路线。
    猜你喜欢
    • 1970-01-01
    • 2014-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-17
    • 2015-01-19
    相关资源
    最近更新 更多