你如何编写递归存储过程

时间:2010-05-27 22:26:19

标签: sql-server database

我只想要一个存储过程来计算唯一ID(与标识列分开)并插入它。如果失败,它只是调用自己重新生成所述id。我一直在寻找一个例子,但是找不到一个,我不确定如何让SP调用自己,并设置适当的输出参数。我也很感激有人指出如何测试这个SP。

修改

我现在提出的是以下内容(注意我已经有了一个标识列,我需要一个辅助标识列。

ALTER PROCEDURE [dbo].[DataInstance_Insert] 
    @DataContainerId int out,
    @ModelEntityId int,
    @ParentDataContainerId int, 
    @DataInstanceId int out
AS
BEGIN

    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    WHILE (@DataContainerId is null) 
        EXEC DataContainer_Insert @ModelEntityId, @ParentDataContainerId, @DataContainerId output   

    INSERT INTO DataInstance (DataContainerId, ModelEntityId)
    VALUES (@DataContainerId, @ModelEntityId)   

    SELECT @DataInstanceId = scope_identity()                       
END    

    ALTER PROCEDURE [dbo].[DataContainer_Insert]
        @ModelEntityId int,
        @ParentDataContainerId int,
        @DataContainerId int out
    AS
    BEGIN 
    BEGIN TRY
        SET NOCOUNT ON;
        DECLARE @ReferenceId int

        SELECT @ReferenceId = isnull(Max(ReferenceId)+1,1) from DataContainer Where ModelEntityId=@ModelEntityId

        INSERT INTO DataContainer (ReferenceId, ModelEntityId, ParentDataContainerId)
        VALUES (@ReferenceId, @ModelEntityId, @ParentDataContainerId)

        SELECT @DataContainerId = scope_identity()
    END TRY
    BEGIN CATCH
    END CATCH
    END

4 个答案:

答案 0 :(得分:2)

  1. 在CATCH块中,必须检查XACT_STATE值。您可能处于注定的事务(-1)中,在这种情况下,您将被迫回滚。或者您的交易可能已经已经回滚,您不应该在现有交易的假设下继续工作。对于处理T-SQL异常的模板过程,请正确尝试/捕获blcoks和事务,请参阅Exception handling and nested transactions
  2. 在任何语言下,从不在异常块中执行递归调用。您没有检查为什么您遇到异常,因此您不知道是否可以再次尝试。如果异常是652,只读文件组怎么办?或者您的数据库是最大尺寸?你会重新诅咒,直到你遇到stackoverflow ......
  3. 读取值的代码,根据该值做出决定,然后写入某些内容始终在并发下失败,除非得到适当保护。您需要在事务中包装SELECT和INSERT,并且SELECT必须在SERIALISABLE隔离级别下。
  4. 最后,忽略帖子中明显错误的代码,这里是你如何调用传入OUTPUT参数的存储过程:

    exec DataContainer_Insert  @SomeData, @DataContainerId OUTPUT;
    

答案 1 :(得分:1)

为什么不使用:

 NewId() 

T SQL函数? (假设sql server 2005/2008)

答案 2 :(得分:1)

更好的是,为什么不将UserID设置为标识列而不是尝试手动重新实现标识列?

顺便说一句:我认为你的意思是

VALUES (@DataContainerId + 1 , SomeData)

答案 3 :(得分:0)

sp永远不会成功插入,你在DataContainer表上有一个标识属性但是你要插入ID,在这种情况下你需要设置identity_insert,但是scope_identity()将不起作用

也可能没有陷入PK违规,因此您可能还需要检查XACT_STATE()

你为什么要乱用max,使用scope_identity()并完成它