【问题标题】:SQL - Update rows between two values in a columnSQL - 更新列中两个值之间的行
【发布时间】:2016-01-13 13:06:49
【问题描述】:

我在SQL Server中有如下数据集:

ROW_NUM  EMP_ID  DATE_KEY  TP_DAYS
1        U12345  20131003   1
2        U12345  20131004   0
3        U12345  20131005   0
4        U12345  20131006   0
5        U12345  20150627   1
6        U12345  20150628   0
1        U54321  20131003   1
2        U54321  20131004   0
3        U54321  20131005   0
4        U54321  20131006   0

我需要更新列 TP_DAYS 中的所有零,并将值递增 1 到上一个值。
所需的结果集如下:

ROW_NUM  EMP_ID  DATE_KEY  TP_DAYS
1        U12345  20131003   1
2        U12345  20131004   2
3        U12345  20131005   3
4        U12345  20131006   4
5        U12345  20150627   1
6        U12345  20150628   2
1        U54321  20131003   1
2        U54321  20131004   2
3        U54321  20131005   3
4        U54321  20131006   4

我尝试在 SQL 中使用 LAGLEAD 函数。但无法达到预期的效果。

谁能帮我实现它。

【问题讨论】:

    标签: sql sql-server tsql sql-update gaps-and-islands


    【解决方案1】:

    使用窗口函数(SUM/ROW_NUMBER 所以它可以与SQL Server 2008 一起使用):

    WITH cte AS
    (
      SELECT *, s =  SUM(TP_DAYS) OVER(PARTITION BY EMP_ID ORDER BY ROW_NUM)
      FROM #tab
    ), cte2 AS
    (
      SELECT *,
        tp_days_recalculated = ROW_NUMBER() OVER (PARTITION BY EMP_ID, s ORDER BY ROW_NUM)
      FROM cte
    )
    UPDATE cte2
    SET TP_DAYS = tp_days_recalculated;
    
    SELECT *
    FROM #tab;
    

    LiveDemo

    输出:

    ╔═════════╦════════╦══════════╦═════════╗
    ║ ROW_NUM ║ EMP_ID ║ DATE_KEY ║ TP_DAYS ║
    ╠═════════╬════════╬══════════╬═════════╣
    ║       1 ║ U12345 ║ 20131003 ║       1 ║
    ║       2 ║ U12345 ║ 20131004 ║       2 ║
    ║       3 ║ U12345 ║ 20131005 ║       3 ║
    ║       4 ║ U12345 ║ 20131006 ║       4 ║
    ║       5 ║ U12345 ║ 20150627 ║       1 ║
    ║       6 ║ U12345 ║ 20150628 ║       2 ║
    ║       1 ║ U54321 ║ 20131003 ║       1 ║
    ║       2 ║ U54321 ║ 20131004 ║       2 ║
    ║       3 ║ U54321 ║ 20131005 ║       3 ║
    ║       4 ║ U54321 ║ 20131006 ║       4 ║
    ╚═════════╩════════╩══════════╩═════════╝   
    

    #附录

    原始OP问题和样本数据非常清楚tp_days指标是01不是任何其他值。

    尤其是Atheer Mostafa

    检查此示例作为证明:https://data.stackexchange.com/stackoverflow/query/edit/423186

    这应该是新问题,但我会处理这种情况:

    ;WITH cte AS
    (
      SELECT *
       ,rn = s +  ROW_NUMBER() OVER(PARTITION BY EMP_ID, s ORDER BY ROW_NUM) -1
       ,rnk = DENSE_RANK() OVER(PARTITION BY EMP_ID ORDER BY s)
      FROM (SELECT *, s =  SUM(tp_days) OVER(PARTITION BY EMP_ID ORDER BY ROW_NUM)
            FROM #tab) AS sub
    ), cte2 AS
    (
      SELECT c1.*,
       tp_days_recalculated = c1.rn - (SELECT COALESCE(MAX(c2.s),0)
                                       FROM cte c2
                                       WHERE c1.emp_id = c2.emp_id
                                         AND c2.rnk = c1.rnk-1)
      FROM cte c1
    )
    UPDATE cte2
    SET tp_days = tp_days_recalculated;
    

    LiveDemo2

    输出:

    ╔═════════╦════════╦══════════╦═════════╗
    ║ row_num ║ emp_id ║ date_key ║ tp_days ║
    ╠═════════╬════════╬══════════╬═════════╣
    ║       1 ║ U12345 ║ 20131003 ║       2 ║
    ║       2 ║ U12345 ║ 20131004 ║       3 ║
    ║       3 ║ U12345 ║ 20131005 ║       4 ║
    ║       4 ║ U12345 ║ 20131006 ║       3 ║
    ║       5 ║ U12345 ║ 20150627 ║       4 ║
    ║       6 ║ U12345 ║ 20150628 ║       5 ║
    ║       1 ║ U54321 ║ 20131003 ║       2 ║
    ║       2 ║ U54321 ║ 20131004 ║       3 ║
    ║       3 ║ U54321 ║ 20131005 ║       1 ║
    ║       4 ║ U54321 ║ 20131006 ║       2 ║
    ╚═════════╩════════╩══════════╩═════════╝
    

    它不应该将值 3,4,2 更改为 1 .... 就是这种情况。 当我有另一个通用答案时,我不需要你的解决方案,你不告诉我该怎么做......谢谢你

    Solution mentioned in comment 只不过是quirky update。是的,它会起作用,但很容易失败:

    • 首先,没有有序表本身这种东西
    • 查询优化器可以以任何方式读取数据(尤其是当数据集很大并且涉及并行执行时)。没有ORDER BY,你不能保证稳定的结果
    • 该行为未记录在案,今天可能有效,但将来可能会中断

    相关文章:

    1. Robyn Page's SQL Server Cursor Workbench
    2. Calculate running total / running balance
    3. No Seatbelt - Expecting Order without ORDER BY

    【讨论】:

    • 非常感谢。我正在使用 SQL Server 2012。我们是否还有其他功能可以替换 ROW_NUMBER()
    • 如果初始表中的 TB_Days 值具有不同于 1 的值,则此查询将无法按要求运行“值以 1 递增到前一个值”。 ...尝试 TB_Days 中的一些初始值作为 3 ,4 , 10 ...它将不起作用。谢谢
    • @AtheerMostafa 这个反对票是开玩笑的。关键是 OP 原始问题使用 1 作为岛的末端。这是经典的间隙和孤岛问题,但为了简单起见,我将其减少了(0 和 1)。如果 OP 想要不同的值,他应该写它。对于有问题的案例,我的回答是有效的。谢谢。
    • @AtheerMostafa 请再次仔细阅读我的最后评论。 1 用于岛的开始/结束。不是 2 不是任何其他值。如果 OP 想要不同的值,他应该编辑问题。如果您想要一般情况,请提出新问题,我会向您展示将任何其他值作为岛屿边界处理的解决方案。谢谢。
    【解决方案2】:

    让我假设 SQL Server 2012+。您需要识别由 1 分隔的组。计算组的一种简单方法是进行 1 的累积和。然后row_number()可以用来计算新的值。您可以使用可更新的 CTE 来完成这项工作:

    with toupdate as (
          select t.*,
                 row_number() over (partition by empid, grp order by row_num) as new_tp_days
          from (select t.*, 
                       sum(tp_days) over (partition by emp_id order by row_num) as grp
                from t
               ) t
         )
    update toupdate
        set tp_days = new_tp_days;
    

    在早期版本的 SQL Server 中,您可以完成同样的事情(效率较低)。一种方法使用outer apply

    【讨论】:

    • 嗨,戈登,看来分组没有按预期给出结果。我正在努力以产生预期的结果。感谢您的帮助。
    • @user2107971 。 . .我将empid 留在了row_number() 的分区子句之外。哎呀。
    【解决方案3】:

    我有一个更简单的方法,简单的代码如下:

    DECLARE @last int=0
    UPDATE #Employees set @last=CASE WHEN TP_DAYS=0 THEN @last+1 ELSE TP_DAYS END,
    TP_DAYS=CASE WHEN TP_DAYS=0 THEN @last ELSE TP_DAYS END
    

    这可以在任何 SQL Server 引擎中运行 在这里查看演示

    https://data.stackexchange.com/meta.stackoverflow/query/422955/sql-update-rows-between-two-values-in-a-column?opt.withExecutionPlan=true#resultSets

    【讨论】:

    • 棘手且不完全正确。这是某种quirky update。当涉及并行性或查询优化器决定以与您想要的不同顺序读取数据时,祝您好运。在你向任何人推荐这个解决方案之前,请指出它的弱点。 More info
    • 正如您在演示中看到的那样,结果是正确的
    • 没有有序表之类的东西。如果您引用其他接受的答案,请完整阅读。最后评论:stackoverflow.com/a/11313533/26167 导致 Quirky update - do this at your own risk: The "quirky update" method is more efficient than the above, but the behavior is not documented, there are no guarantees about order, and the behavior might work today but could break in the future. I'm including this because it is a popular method and it is efficient, but that doesn't mean I endorse it. 如果您建议的方法不稳定,请添加警告。
    • 同样正确的结果并不能证明任何事情(只需尝试更大的并行样本)。这与人们说他们不需要使用 ORDER BY 的推理类型相同,因为数据是基于聚集索引读取的(这显然不是真的)。​​
    • 不,因为我在评论中写的不正确!
    猜你喜欢
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    • 2023-04-08
    • 2017-12-24
    • 1970-01-01
    • 2014-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多