使用无间隙的升序值有条件地更新行

Lei*_*fel 6 oracle oracle-11g-r2 gaps-and-islands

用户有一个他们想要更新的表。根据其他列中的数据,他们希望使用从 1 开始的无间隙升序值更新特定列。不包含此升序值的行也需要更新。有没有办法用一个 UPDATE 语句来做到这一点?

创建示例数据:

CREATE TABLE t1 AS (
   SELECT CHR(ASCII('a')+rownum-1) letter, rownum Number1, 0 Number2 
   FROM dual connect by rownum<=7);
Run Code Online (Sandbox Code Playgroud)

分配升序 rownum 的更新语句,但它不是无间隙的:

UPDATE t1 SET Letter=’x’, 
   Number2 = DECODE(letter,’a’,rownum,’d’,rownum,’e’,rownum,NULL);
Run Code Online (Sandbox Code Playgroud)

使用序列返回相同的结果:

CREATE SEQUENCE s1;
UPDATE t1 SET letter=’x’, 
   number2 = DECODE(letter,’a’,s1.nextval,’d’, s1.nextval,’e’, s1.nextval,NULL);
Run Code Online (Sandbox Code Playgroud)

完成后,数据应如下所示:

L    NUMBER1    NUMBER2
- ---------- ----------
x          1          1
x          2
x          3
x          4          2
x          5          3
x          6
x          7
Run Code Online (Sandbox Code Playgroud)

我不是在寻找仅适用于我的特定测试用例的技巧,而是寻找问题的通用解决方案。

Vin*_*rat 7

请参阅AskTom 上有关表达式计算方式的讨论decode在大多数情况下,该函数确实会执行短路:如果左侧的条件被评估为 ,则不会评估右侧的表达式true。然而,序列是特殊的,它们在所有情况下都针对所有行进行评估。

最简单的解决方法是使用调用序列的函数:

SQL> CREATE SEQUENCE seq;

Sequence created

SQL> CREATE OR REPLACE FUNCTION f RETURN NUMBER
  2  AS
  3     l_result NUMBER;
  4  BEGIN
  5     SELECT seq.nextval INTO l_result FROM dual;
  6     RETURN l_result;
  7  END;
  8  /

Function created

SQL> UPDATE t1 SET letter='x',
  2     number2 = DECODE(letter,'a',f,'d', f,'e', f,NULL);

7 rows updated

SQL> select * from t1;

LETTER    NUMBER1    NUMBER2
------ ---------- ----------
x               1          1
x               2 
x               3 
x               4          2
x               5          3
x               6 
x               7 

7 rows selected
Run Code Online (Sandbox Code Playgroud)

如您所见,该函数仅在 DECODE 表达式需要时才进行计算。


在大多数情况下,您可以使用纯 SQL 解决方案。如果我们假设该表具有 PK(我将采用number1),则此自联接 SQL 解决方案将起作用:

SQL> UPDATE t1 t1_out
  2     SET letter  = 'x',
  3         number2 = CASE WHEN letter IN ('a', 'd', 'e') --
  4                        THEN (SELECT COUNT(*)
  5                                FROM t1 t1_in
  6                               WHERE letter IN ('a', 'd', 'e')
  7                                 AND t1_in.number1 <= t1_out.number1)
  8                   END;

7 rows updated

SQL> select * from t1;

LETTER    NUMBER1    NUMBER2
------ ---------- ----------
x               1          1
x               2 
x               3 
x               4          2
x               5          3
x               6 
x               7 

7 rows selected
Run Code Online (Sandbox Code Playgroud)

您可能已经尝试过分析解决方案。可以编写一个返回适当结果集的查询,但更新和分析不能很好地协同工作(分析往往会产生不可更新的视图)。