【问题标题】:Recursivity in SQL Server not updating rows inside recursive procedureSQL Server 中的递归不更新递归过程中的行
【发布时间】:2014-07-07 14:43:04
【问题描述】:

我正在 SQL Server 2012 Express Edition SP1 上开发数据库。

我想对这两个存储过程进行递归:

Create procedure [dbo].[DisaggreagateChildren]
      @parentCode nvarchar(20) 
    , @parentLevel tinyint  
    , @parentFlag tinyint   
    , @childrenFlag tinyint 
as
begin
    declare @aggregationId int
          , @childCode nvarchar(20)
          , @childLevel tinyint;

    declare @tempChilds table (
        Code nvarchar(20),
        CodeLevel tinyint)

    [ ... ]

    set rowcount 1
    select @childCode = code, @childLevel = CodeLevel from @tempChilds

    -- Loop all child codes.
    while @@rowcount <> 0
    begin
        -- Get first code and delete it from temporary table.
        set rowcount 0
        select * from @tempChilds where code = @childCode and CodeLevel = @childLevel;
        delete @tempChilds where code = @childCode and CodeLevel = @childLevel;

        if (@childLevel = 1)
            update dbo.CODES
                set COMMISIONING_FLAG = @childrenFlag
                where ID_CODE = @childCode and CODE_LEVEL = @childLevel;
        else
        begin
            update dbo.CODES
                set COMMISIONING_FLAG = @parentFlag
                where ID_CODE = @childCode and CODE_LEVEL = @childLevel;

            -- Recursive call to disaggregate @childCode's childrens.
            EXEC dbo.DisaggreagateChildren @childCode, @childLevel, @parentFlag, @childrenFlag
        end

        -- Get next code.
        set rowcount 1
        select @childCode = code, @childLevel = CodeLevel from @tempChilds
    end -- while end
    set rowcount 0

end -- Procedure end.
go

Create procedure [dbo].[Disaggreagate]
      @parentCode nvarchar(20)          -- Code to disaggregate.
    , @parentLevel tinyint              -- Code's level.
    , @withChildren bit                 -- Disaggragate with childrens?
    , @isManual bit                     -- Is manual or automatic disaggregation?
as
begin
    Declare @parentFlag tinyint
          , @childrenFlag tinyint
          , @trancount int;

    [ ... ]

    -- Star transaction.
    set @trancount = @@trancount;
    if @trancount = 0
        begin transaction
    else
        save transaction Disaggreagate;

    begin try
        -- Update parent code commissioning flag.
        Update dbo.CODES
            set COMMISIONING_FLAG = @parentFlag
            where CODE = @parentCode and CODE_LEVEL = @parentLevel;

        -- If it is a disaggregation with children, disaggregate its children.
        if (@withChildren = 1)
        begin
            EXEC dbo.DisaggreagateChildren @parentCode, @parentLevel, @parentFlag, @childrenFlag
        end

        if @trancount = 0
            commit;
    end try
    begin catch
        declare @error int
            , @message nvarchar(2048)
            , @xactState smallint;
        select @error = ERROR_NUMBER()
            , @message = ERROR_MESSAGE()
            , @xactState = XACT_STATE();
        if @xactState = -1
            rollback;
        if @xactState = 1 and @trancount = 0
            rollback
        if @xactState = 1 and @trancount > 0
            rollback transaction Disaggreagate;

        raiserror(N'Error: %i, %s', 16, 1, @error, @message);
    end catch
end -- Procedure end.
go

Disaggreagate 中的更新完美运行,但 DissaggreagateWithChildren 中的更新不起作用。执行后我看不到更改。

发生了什么事?

样本数据:

CodeLevel | 3 | 2 | 1 |
----------+---+---+---+
Code      | 0 |   |   |
----------+---+---+---+
Code      |   | 1 |   |
----------+---+---+---+
Code      |   |   | 2 |
----------+---+---+---+
Code      |   |   | 3 |
----------+---+---+---+
Code      |   | 4 |   |
----------+---+---+---+
Code      |   |   | 5 |
----------+---+---+---+
Code      |   |   | 6 |
----------+---+---+---+

Aggregations 表上,我有代码 0、1 和 4。 在Aggregations_Childs 表上,我有代码 1、2、3、4、5 和 6。

【问题讨论】:

  • 很少需要使用循环和/或递归过程调用 - 您通常应该努力编写一个查询,将所有要更新的行及其新值标识为单个 UPDATE 查询。不幸的是,这里有太多的机制,我发现很难看出实际应用了什么逻辑。您能否创建一小组样本数据、调用上述代码时使用的参数以及样本数据的预期结果?
  • @Damien_The_Unbeliever 我问了,因为我认为我需要在递归过程中添加一个提交。
  • 我认为这只是使用@@rowcountset rowcount的问题
  • 最后,您只是想添加分支,还是从自连接的树中修剪分支?即我有代码 0,其孩子是 1 和 4,其孙子是 2,3(1 岁的孩子)和 5,6(4 岁的孩子)。因此,如果我删除孩子 4,我还需要删除 5,6,因为它们已连接?

标签: sql-server tsql recursion sql-server-2012-express


【解决方案1】:

如果我错了,请纠正我,因为我不完全了解您的存储过程在做什么,但我相信您的使用或 rowcount@@rowcount 导致没有更新的问题。

如果您在 SQL Management Studio 中运行以下命令:

set rowcount 1
print @@rowcount

您会注意到0 已返回。您是否期望这会返回 1

如果您随后运行以下命令来设置变量@tempId

declare @temp table (id int)
insert into @temp (id) values (1)

declare @tempId int    
select top 1 @tempId = id from @temp

select @@rowcount

行数变为1,因为这条语句:select top 1 @tempId = id from @temp

所以控制你的循环的逻辑总是错误的:

while @@rowcount <> 0

这是我为复制您的逻辑而执行的简化测试,它永远不会进入 while 循环,希望它与您正在尝试的匹配:

declare @temp table (id int)

insert into @temp (id) values (1)
insert into @temp (id) values (2)
insert into @temp (id) values (3)
insert into @temp (id) values (4)

declare @tempId int

set rowcount 1
select top 1 @tempId = id from @temp
print 'step 1 row count: ' + cast(@@rowcount as varchar(10))

-- Loop
while @@rowcount <> 0
begin
    -- delete first id
    set rowcount 0        
    print 'step 2 row count: ' + cast(@@rowcount as varchar(10))
    delete @temp where id = @tempId
    print 'step 3 row count: ' + cast(@@rowcount as varchar(10))
    -- Get next id
    set rowcount 1
    print 'step 4 row count: ' + cast(@@rowcount as varchar(10)) 
    select top 1 @tempId = id from @temp
    print 'step 4 row count: ' + cast(@@rowcount as varchar(10)) 
end -- while end
set rowcount 0

-- check what's left
select * from @temp

您可以改写它以在 WHILE 循环上使用 EXISTS 逻辑,例如:

declare @temp table (id int)

insert into @temp (id) values (1)
insert into @temp (id) values (2)
insert into @temp (id) values (3)
insert into @temp (id) values (4)

declare @tempId int = 1
select * from @temp

-- Loop to recursively delete
while exists (select top 1 id from @temp where id = @tempId)
begin
    -- Get next code        
    select top 1 @tempId = id from @temp    
    -- delete select id    
    delete @temp where id = @tempId   
    -- how many rows left
    select count(id) as RowsLeft from @temp
    select @tempId = @tempId + 1     
end -- while end

【讨论】:

    【解决方案2】:

    我认为你需要提交你的保存点。

    if @trancount = 0
                commit
    ELSE
    BEGIN
        commit Disaggreagate
    END
    

    由于您只在没有传入事务的情况下提交,但您创建了一个保存点,我不相信它会被提交。

    【讨论】:

    • 谢谢,但我在调试时发现没有使用保存点。我不认为这是问题所在。也许我需要添加另一个提交。
    • 您只需要提交,如果 sp 是创建事务的那个。请检查此(example
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-06
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 2013-08-03
    • 2022-01-05
    • 1970-01-01
    相关资源
    最近更新 更多