【问题标题】:Consecutively update values starting from a number in the table SQL从表 SQL 中的数字开始连续更新值
【发布时间】:2014-08-28 22:46:57
【问题描述】:

我对@9​​87654321@ 语句的用法有疑问。有没有办法让我只用 SQL 编写以下场景而不是编写存储过程?

我试图简化案例。

myTable有4列,其值如下:

COMP1   COMP2   NO  ACTIVE
0       0       4   N
4       1           Y
2       2       21  Y
3       1       1   Y
1       3       43  Y
2       1           Y
3       1       12  Y
2       2       0   Y
3       2           Y
1       1       5   N
  • 我想选择值WHERE ACTIVE = 'Y' 并开始更新 它们在 NO 列中的值按COMP1, COMP2 ASC 排序。
  • 编号必须从表WHERE ACTIVE = 'N'中的MAX(NO)值开始

  • 具有相同 COMP1 和 COMP2 的行将具有相同的 NO 值。

  • (评论后更新)NO 列并不总是 NULL。此列中存在错误值,需要更新查询进行更正。

这样,UPDATE 语句执行后,myTable 将如下(如果我们订购的话)

COMP1   COMP2   NO  ACTIVE
0       0       4   N
1       1       5   N
1       3       6   Y
2       1       7   Y
2       2       8   Y
2       2       8   Y
3       1       9   Y
3       1       9   Y
3       2       10  Y
4       1       11  Y

我想知道是否可以在不需要选择整个列表并在存储过程中循环访问 CURSOR 的情况下做到这一点。

(更新:我写了SP,简化版如下,可能有问题,因为我改了所有名字,删除了很多条件。)

 PROCEDURE updateAllNo
   IS
      CURSOR c1
      IS
           SELECT *
             FROM MyTable SE
            WHERE SE.ACTIVE = 'Y'
         ORDER BY SE.COMP1, SE.COMP2;

      v_last_no    NUMBER := 0;
      v_last_comp2   DATE := SYSDATE + 100;
      v_last_comp1   DATE := SYSDATE + 100;
      v_now_comp2    DATE := SYSDATE;
      v_now_comp1    DATE := SYSDATE;
   BEGIN
     SELECT MAX (SE.NO)
       INTO v_last_no
       FROM MyTable SE
      WHERE SE.ACTIVE = 'N';

     SELECT MAX (SE.COMP1), MAX (SE.COMP2)
       INTO v_last_comp1, v_last_comp2
       FROM MyTable SE
      WHERE     SE.ISLEMBASARILI = 'E'
            AND SE.NO = v_last_no;

     FOR r1 IN c1
     LOOP
        BEGIN
           v_now_comp2 := r1.COMP2;
           v_now_comp1 := r1.COMP1;

           IF v_now_comp2 != v_last_comp2 OR v_now_comp1 != v_last_comp1
           THEN
              v_last_no := v_last_no + 1;
           END IF;

           UPDATE MyTable SE
              SET SE.NO = v_last_no
            WHERE SEQ_ID = r1.seq_id;

           v_last_comp2 := v_now_comp2;
           v_last_comp1 := v_now_comp1;
        END;
     END LOOP;

     COMMIT;

   END;

我正在使用 Oracle 11g。

【问题讨论】:

    标签: sql oracle sql-update


    【解决方案1】:

    可以使用窗口函数检索新号码:

    以下将检索活动行并计算 no 的新值

    select comp1, 
           comp2,
           row_number() over (order by comp1, comp2) + (select max(no) from mytable where active = 'N') as rn
    from mytable
    where active = 'Y'
    

    现在可以在MERGE 语句中使用它来对表运行更新。由于该表显然没有PK,我将使用ROWID 来匹配行:

    merge into mytable tg
    using (
        select rowid as rid,
               comp1, 
               comp2,
               row_number() over (order by comp1, comp2) + (select max(no) from mytable where active = 'N') as rn
        from mytable
        where active = 'Y'
    ) t on (t.rid = tg.rowid)
    when matched then update
       set no = t.rn;
    

    这肯定会比单行更新的循环更快——尤其是对于较大的表。

    【讨论】:

    • 谢谢。除了 COMP1 = COMP2 的情况外,它几乎解决了这个问题。我也在尝试,但是您知道如何为与我的示例中具有相同 COMP1 和 COMP2 的行设置相同的 NO。我想,我们需要在 row_number() 行中写一个 CASE 或类似的东西。
    • @YigitalpErtem:comp1 = comp2 的问题可以通过使用dense_rank() 而不是row_number() 来解决
    • 非常感谢。我需要学习分析函数,它们看起来很强大。
    【解决方案2】:

    我假设NO 字段在ACTIVE='Y' 时始终为空。如果是这种情况,无论是否指定了WHERE ACTIVE = 'N' 条件,MAX(NO) 都将返回相同的值。有了这个假设,这个UPDATE 语句应该可以解决问题。

    UPDATE myTable 
    SET NO = (SELECT MAX(NO) FROM myTable) + 1
    WHERE ROWID IN 
    (SELECT ROWID FROM myTable
    WHERE ACTIVE = 'Y'
    ORDER BY COMP1, COMP2);
    

    【讨论】:

    • 感谢您的回答。但在其 ACTIVE 列为“Y”的 NO 字段中存在错误值,即它们并不总是为空。更新语句必须更正这些值并将正确的数字设置为 NO 列(我在问题中添加了这个)。我也试过你的例子,但它给出了 ORA-00934 错误,不允许我使用 Group 函数。
    • 我进行了编辑以消除错误。仍然不适合你,但我不想在这里留下损坏的代码。
    猜你喜欢
    • 2016-03-06
    • 1970-01-01
    • 1970-01-01
    • 2010-12-21
    • 2017-03-11
    • 1970-01-01
    • 2016-03-27
    • 1970-01-01
    • 2016-08-26
    相关资源
    最近更新 更多