从表SQL中的数字开始连续更新值

时间:2014-08-28 09:05:06

标签: sql oracle sql-update

我对UPDATE声明的用法有疑问。有没有办法让我只用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排序。
  • 编号必须从表MAX(NO)

  • 中的WHERE ACTIVE = 'N'值开始
  • 具有相同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。

2 个答案:

答案 0 :(得分: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;

这肯定比单行更新的循环更快 - 特别是对于较大的表。

答案 1 :(得分:0)

我假设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);