UPDATE语句:在每个SET之后重新计算子查询

时间:2014-06-02 12:18:54

标签: sql oracle oracle11g

CREATE TABLE test (id NUMBER(3));

INSERT INTO test VALUES (1);
INSERT INTO test VALUES (2);
INSERT INTO test VALUES (2);
INSERT INTO test VALUES (3);
INSERT INTO test VALUES (4);
INSERT INTO test VALUES (4);
INSERT INTO test VALUES (5);

我想使用此查询使test中的数字唯一(例如{1,2,3,4,5,6,7} - 即删除双打):

UPDATE test u SET u.id = (SELECT max(nn.id) FROM test nn) + 1 
  WHERE 1 < (
    SELECT tt.rown 
      FROM (SELECT rowid, row_number() over ( partition by t.id order by t.id) AS rown FROM test t) tt
      WHERE tt.rowid = u.rowid
    ); 

上述查询将表更新为{1,2,3,4,5,6,6}。它会正确地将第二个2替换为6,但第二个4也会变为6,其应为7。不应替换现有的非重复项,而只应替换第二项重复项。

上述更新语句的问题是(SELECT max(nn.id) FROM test nn)被评估一次并缓存,但实际上取决于更新的内容。是否有可能在每次SET之后强制重新评估(SELECT max(nn.id) FROM test nn)?我尝试了一些像/*+NOCACHE*/这样的提示但没有成功。

换句话说,在更新期间,您需要考虑已更新的字段。

任何想法?

我认为这可以用NON-DETERMINISTIC函数解决,但我不想创建函数。编辑:如果我尝试用函数计算id我得到 ORA-04091:表TEST正在变异,触发器/函数可能看不到它。使用PRAGMA AUTONOMOUS_TRANSACTION;会得到与上述查询相同的结果。

具有预先计算的ID的解决方案,绕过查询re_evaluation

AudriyM使用CTE为MS SQL Server 2008解决了这个问题(参见注释)。据我所知,Oracle中没有替代CTE的替代方案,但由于AudriyM的解决方案基于id的预先计算值,我可以在Oracle中使用子查询进行翻译。这是:

UPDATE test u SET u.id = ( SELECT newIDs.newID
                           FROM ( SELECT  ranked.rowid,
                                          ranked.m + row_number() over (order by ranked.id, ranked.r) as newID 
                                  FROM ( SELECT t.rowid, t.id, row_number() over ( partition by t.id order by t.id) AS r, max(id) over() AS m 
                                        FROM test t ) ranked
                                  WHERE ranked.r > 1 ) newIDs
                           WHERE u.rowid = newIDs.rowid )
WHERE u.rowid IN ( SELECT ranked.rowid
                   FROM ( SELECT t.rowid, t.id, row_number() over ( partition by t.id order by t.id) AS r, max(id) over() AS m 
                          FROM test t ) ranked
                   WHERE ranked.r > 1 );

使用查询re_evaluation

的没有预先计算的ID的解决方案

仍然没有找到,问题仍然没有答案。

1 个答案:

答案 0 :(得分:0)

尝试使用以下语句。您的表格不允许识别ID为2的行,因此要将表格与ROW_NUMBER关联,您可以使用ROWID

UPDATE
Test 
SET id = (SELECT RN FROM
                     (SELECT ROW_NUMBER() 
                             OVER (ORDER BY ID) as RN 
                             FROM Test
                     ) T1
                     WHERE T1.RowID=Test.RowID
          )

SQLFiddle demo