编写迭代自引用SQL查询

编写迭代自引用SQL查询,sql,sql-server,tsql,Sql,Sql Server,Tsql,我需要在SQL中实现一个复杂的算法,该算法需要能够为第一行计算某个值,并且在所有后续行上,需要能够使用前一行的计算值作为其自身计算的一部分。它是递归的,但从包含所有空值的目标列的位置开始 我使用下面的光标简化了解决此问题的尝试: if object_id('tempdb..#t') is not null drop table #t; -- create a simple demo table containing IDs 1 - 10 and an empty int column

我需要在SQL中实现一个复杂的算法,该算法需要能够为第一行计算某个值,并且在所有后续行上,需要能够使用前一行的计算值作为其自身计算的一部分。它是递归的,但从包含所有空值的目标列的位置开始

我使用下面的光标简化了解决此问题的尝试:

if object_id('tempdb..#t') is not null
    drop table #t;

-- create a simple demo table containing IDs 1 - 10 and an empty int column
with cte as (
    select x = 1

    union all

    select x = x + 1
    from cte
    where x < 10
)
select *, WriteVal = cast(null as int)
into #t
from cte



declare c cursor for
    select x from #t

declare @x int

open c

fetch next from c into @x

while @@FETCH_STATUS = 0
begin
    declare @WriteVal int

    -- Set @WriteVal to the previous row's [WriteVal] + 10. If previous row's [WriteVal] is NULL,
    -- (i.e. we're in the first row), use 0 + 10 instead.
    select @WriteVal = isnull(lag(WriteVal, 1) over (order by x), 0) + 10
    from #t
    where x = @x

    -- Update this row on the temp table with the value stored in @WriteVal
    update #t
    set WriteVal = @WriteVal
    where x = @x

    -- return the full table for debugging
    select *, [@WriteVal] = @WriteVal
    from #t

    fetch next from c into @x
end

close c
deallocate c
我希望看到的是:

  • 在第一次循环迭代中,
    lag(WriteVal,1)
    为空,因此
    @WriteVal
    被设置为10,然后
    10
    被写入第1行的
    [WriteVal]
  • 在第二次循环迭代中,
    lag(WriteVal,1)
    是10,因此
    @WriteVal
    被设置为(10+10)20,然后
    20
    被写入第2行的
    [WriteVal]
  • 在第三次循环迭代中,
    lag(WriteVal,1)
    是20,因此
    @WriteVal
    被设置为(20+10)30,然后
    30
    被写入第3行的
    [WriteVal]
  • 。。。等等
  • 预期结果集:

    x   | WriteVal
    ----|---------
    1   | 10
    2   | 20
    3   | 30
    4   | 40
    ...
    10  | 100
    
    x   | WriteVal
    ----|---------
    1   | 10
    2   | 10
    3   | 10
    4   | 10
    ...
    10  | 10
    
    实际返回的是将every
    [WriteVal]
    设置为
    10
    ,进一步检查将表明后续循环迭代无法识别更新的前一行值,因此
    lag(WriteVal,1)
    始终返回NULL。我认为这要归咎于某些优化或缓存机制,或者在查询完成或其他情况下更新才真正提交

    实际结果集:

    x   | WriteVal
    ----|---------
    1   | 10
    2   | 20
    3   | 30
    4   | 40
    ...
    10  | 100
    
    x   | WriteVal
    ----|---------
    1   | 10
    2   | 10
    3   | 10
    4   | 10
    ...
    10  | 10
    

    我如何解决这个问题?有更好的方法吗?如果可能的话,我宁愿避免使用游标,但它需要能够首先计算第n行,然后使用该结果计算第n+1行,这意味着我在使用自联接或窗口函数的非游标解决方案方面没有任何运气。根据您的示例数据,您希望:

    select x,
           10 * row_number() over (order by x) as writeval
    from #t;
    
    要更新值,请使用可更新的CTE:

    with toupdate as (
          select t.*,
                 10 * row_number() over (order by x) as new_writeval
          from #t t
         )
    update toupdate
        set writeval = new_writeval;
    

    请注意,您的查询返回了不一致的结果,因为您的游标没有使用
    order by
    ,因此结果可以在调用之间更改。

    示例数据、所需结果以及对您想要完成的操作的清晰解释确实会有所帮助。如果这是递归的,我怀疑您是否需要
    LAG
    。一般来说,您从对递归CTE的回调中得到“previous”值。@GordonLinoff实际的数据集非常庞大且非常广泛(甚至仅考虑算法所需的列),而实际的算法非常复杂,需要大量的上下文,所以我故意把它归结为问题的根源——否则这个问题会长好几英里,主要的实际问题会被完全掩盖。此外,这个问题解释了期望的结果,并解释了我试图实现的目标。如果你能在我提交问题后15秒内收到你的评论,如果你能抽出时间先阅读,我将不胜感激。@GordonLinoff为他们辩护说:“如果你能在我提交问题后15秒内收到你的评论,他们在SQL方面非常有经验;对于我们这些人来说,回答一个确实需要样本数据和预期结果的问题通常非常容易,我同意他们的说法(我也没有花超过15秒的时间得出这个结论)。它不必是您的完整数据集,这就是为什么它被称为“示例”。仅仅是一个小的代表性数据集,以及您对该数据的预期结果,将极大地帮助我们帮助您。或者从#tt中选择(x-1)*10?