【问题标题】:Update SQL table in a loop with dynamic value from other table使用来自其他表的动态值循环更新 SQL 表
【发布时间】:2017-03-27 10:16:41
【问题描述】:

表一:#tdac

insert #tdac(secnum,bucketcode,acc,defqty)
select 'ax1','cor',1,'012',-100
insert #tdac(secnum,bucketcode,acc,defqty)
select 'ax1','cor',2,'012',-50

表2:#dac

insert #dac(secnum,bucketcode,acc,defqty)
select 'ax1','cor',0,'012',-125

我想通过从最旧的caseid 开始从表 2 中减去 defqty 来更新表 1 中的 dfqty

If #tdac.defqty-#dac.defqty > 0, then 
    #tdac.defqty=0 and #dac.defqty = (#tdac.defqty-#dac.defqty) 
proceed to the next row. 
Continue this until the  value of (#tdac.defqty-#dac.defqty) becomes less than 0. 

在我的示例中,我想如下更新表 1。请帮忙

secnum  bucketcode  caseid  acc     defqty
ax1     cor         1       012     0     ((-100 -(-125)=25)>0, So make it 0)
ax1     cor         2       012    -25    ((-50 -(-25)=-25)<0, So make it -25)

【问题讨论】:

  • 这个问题解决了吗?您需要进一步的帮助吗?请给我一个提示:如果这个问题得到了解决,您将非常好心,在(最佳)答案的投票计数器下方勾选接受检查。这将 1) 将此问题标记为已解决 2) 让追随者更容易找到最佳解决方案 3) 向回答者支付积分和 4) 向您支付积分。一旦您自己越过了 15 点边界,您还被要求对贡献进行投票。这是说谢谢的方式。编码快乐!

标签: sql sql-server sql-server-2008 loops


【解决方案1】:

我不确定我是否完全理解你的逻辑,但我认为这可以通过递归 CTE 来完成:

我添加了另一个案例组来演示,这种方法可以一次性处理不同的组。

declare @tdac table(secnum varchar(100),bucketcode varchar(100),caseid int,acc varchar(100),defqty int);
insert @tdac(secnum,bucketcode,caseid,acc,defqty)
values('ax1','cor',1,'012',-100)
     ,('ax1','cor',2,'012',-50)
     ,('ax1','cor',3,'012',-150)
     ,('ax2','cor',1,'012',-100)
     ,('ax2','cor',2,'012',-100);

declare @dac table(secnum varchar(100),bucketcode varchar(100),caseid int,acc varchar(100),defqty int);
insert @dac(secnum,bucketcode,caseid,acc,defqty)
values('ax1','cor',0,'012',-125)
     ,('ax2','cor',0,'012',-150);

--第一个 CTE 将找到所有不同的组并将@dac 的值作为起始值添加到集合中

WITH DistinctGroups AS
(
    SELECT t.secnum,t.bucketcode,t.acc,d.defqty
    FROM @tdac AS t
    LEFT JOIN @dac AS d ON d.secnum=t.secnum and d.bucketcode=t.bucketcode AND d.acc=t.acc
    GROUP BY t.secnum,t.bucketcode,t.acc,d.defqty
)

-递归CTE

,recursiveCTE AS
(
    --anchor: Get the rows with the minimal caseid from each group

    SELECT t.secnum,t.bucketcode,t.caseid,t.acc,t.defqty,CASE WHEN x.NewQty>0 THEN 0 ELSE x.NewQty END AS NewQty,CASE WHEN x.NewQty>0 THEN x.NewQty ELSE 0 END AS NewDiff
    FROM @tdac AS t
    INNER JOIN DistinctGroups AS gr ON t.secnum=gr.secnum AND t.bucketcode=gr.bucketcode AND t.acc=gr.acc
    CROSS APPLY(SELECT t.defqty-gr.defqty AS NewQty) AS x
    WHERE t.caseid=(SELECT MIN(caseid) FROM @tdac AS t2 WHERE t2.secnum=gr.secnum AND t2.bucketcode=gr.bucketcode AND t2.acc=gr.acc)

    UNION ALL

    --find the row with the next caseid and add the diff value of the previous row

    SELECT t.secnum,t.bucketcode,t.caseid,t.acc,t.defqty,CASE WHEN x.NewQty>0 THEN 0 ELSE x.NewQty END AS NewQty,CASE WHEN x.NewQty>0 THEN x.NewQty ELSE 0 END AS NewDiff
    FROM @tdac AS t
    INNER JOIN recursiveCTE AS r ON t.secnum=r.secnum AND t.bucketcode=r.bucketcode AND t.acc=r.acc
    CROSS APPLY(SELECT t.defqty+r.NewDiff AS NewQty) AS x
    WHERE t.caseid=r.caseid+1
)
select * 
from recursiveCTE
order by secnum,caseid

结果

ax1 cor 1   012 -100       0    25
ax1 cor 2   012  -50     -25     0
ax1 cor 3   012 -150    -150     0

ax2 cor 1   012 -100       0    50
ax2 cor 2   012 -100     -50     0

【讨论】:

    【解决方案2】:
    UPDATE #tdac SET defqty = CASE WHEN ( (#tdac.defqty) -(F.defqty) ) > 0 THEN 0 ELSE ( (#tdac.defqty) -(F.defqty) ) END FROM #dac F WHERE F.secnum = #tdac.secnum
    

    【讨论】:

      【解决方案3】:

      为 defqty 构建一个运行总计(使用 sum() over())。然后更新总和在校正值范围内的位置。

      update tdac
      set defqty = case when tdac.total >= dac.total then 0 else tdac.total - dac.total end
      from 
      (
        select secnum, bucketcode, acc, sum(defqty) as total
        from #dac 
        group by secnum, bucketcode, acc
      ) dac
      join
      (
        select secnum, bucketcode, acc, caseid, defqty,
          sum(defqty) over (partition by secnum, bucketcode, acc order by caseid) as total
        from #tdac
        where defqty < 0
      ) tdac on  tdac.secnum = dac.secnum
             and tdac.bucketcode = dac.bucketcode
             and tdac.acc = dac.acc
             and tdac.total - tdac.defqty >= dac.total;
      

      我怀疑你想加入 secnumbucketcodeacc。如果不是这种情况,请相应地更改 GROUP BYPARTITION BYON 子句。

      我编写查询以便能够处理#dac 中相同secnumbucketcodeacc 的多个条目。如果这三个是唯一的(那么应该是表的主键),你当然不需要聚合子查询,可以直接从#dac 中选择。

      这是一个存储的测试:http://rextester.com/VJJY97875

      【讨论】:

      • 我没有建议 SUM() OVER(),因为我认为 SQL Server 2008 不支持此功能。但文档实际上并没有说明此限制。你确定这在这里有效吗?目前没有机会测试这个......
      • @Shnugo:我也无法测试。我查阅了文档,他们说sum() over() 在 SQL Server 2008 中可用,所以我使用了这个。似乎他们只是在 2012 年添加了缺少的窗口功能(行/范围子句)。
      • 搜索了一下,发现this。在 Martin Smith 问题下方的评论中,OVER(PARTITION BY ... ORDER BY) 是在 2012 年推出的...... OP 将不得不尝试一下:-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-23
      • 2020-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多