使用Rownum更新列

时间:2015-12-10 16:39:10

标签: oracle11g rownum

我正在尝试根据rownumber更新列。表Unpaid1包含多个帐户的多个事务。当我执行以下脚本时,我经常以不正确的序列号(Cycleage)结束事务,如下所示。循环次数应从最旧到最新的顺序为1到4。可能是这个问题的原因是什么?还有另一种分配循环的方法吗?

Begin 
For X in (Select * From temp_Unpaid1 order by trxnserno asc) 
  loop 
  Update temp_Unpaid1 t set t.cycleage = rownum where t.custid=x.custid; 
  end loop; 
End; 


+-----------+-----------+--------+--------+----------+
| TRXNSERNO | CUSTID    | AMOUNT |  DATE  | CYCLEAGE |
+-----------+-----------+--------+--------+----------+
|        66 |        45 |    -10 | 08.jan |        2 |
|        67 |        45 |    -20 | 10.jan |        1 |
|        90 |        45 |    -30 | 15.jan |        3 |
|       155 |        45 |    -15 | 20.jan |        4 |
+-----------+-----------+--------+--------+----------+

1 个答案:

答案 0 :(得分:1)

问题是更新中的ROWNUM是SELECT语句的独立,事实上,它看起来像是在进行不必要的更新。原因如下:

SELECT将按TRXNSERNO的顺序返回所有行,即使你只有一个custid,你也会得到4行具有相同的custid:

CUSTID  TRXNSERNO ...(other columns)
45      66
45      67
45      90
45      155

然后对于这些行中的每个,将运行update语句(因此您将更新相同的4行4次。)

当UPDATE运行时,它会找到custid 45的4行(它不知道/关心TRXNSERNO在SELECT中的内容)并以它找到的顺序更新它们。 (UPDATE语句中没有任何内容强制更新行的顺序。)

因此,无论第4次和最后一次更新发现它们的顺序是如何分配ROWNUM值,所以如果Oracle按照

的顺序找到它们
CUSTID  TRXNSERNO
45      67
45      66
45      90
45      155

然后ROWNUM值将按以下顺序排列:

CUSTID  TRXNSERNO ROWNUM
45      67        1
45      66        2
45      90        3
45      155       4 

在另一次运行中(取决于具体情况),Oracle可能会执行更新并以完全不同的顺序查找custid 45行,如下所示,并相应地为它们分配rownums:

CUSTID  TRXNSERNO ROWNUM
45      155       1
45      66        2
45      90        3
45      67        4 

您可以保留当前代码,但只需在SELECT列表中生成ROWNUM,并将更新中的值应用于custid / trxnserno对:

Begin 
For X in 
   (Select tmp.*
         , row_number() over (partition by tmp.custid order by tmp.trxnserno) as rn 
      From temp_Unpaid1 tmp) 
  loop 
     Update temp_Unpaid1 t 
        set t.cycleage = rn 
      where t.custid=x.custid
        and t.trxnserno = x.trxnserno; 
  end loop; 
End; 

或者,您可以在单个SQL语句中尝试使用MERGE之类的内容并仅使用WHEN MATCHED部分:

MERGE INTO temp_unpaid1 temp
USING
(Select t.*
         , row_number() over (partition by custid order by trxnserno) as rn 
      From temp_Unpaid1
) rn_qry
ON (temp.custid = rn_qry.custid 
    AND temp.trxnserno = rn_qry.trxnserno
   )
WHEN MATCHED THEN
   UPDATE SET
      temp.cyclage = rn_qry.rn
;

注意:遗憾的是,我现在无法测试语法的准确性,但希望这具有一定的价值。 :)