【发布时间】:2014-07-22 13:31:34
【问题描述】:
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: table TEST is mutating, trigger/function may not see it。使用PRAGMA AUTONOMOUS_TRANSACTION; 得到与上述查询相同的结果。
使用预先计算的 id 的解决方案,绕过查询 re_evaluation
AudriyM 使用 CTE 为 MS SQL Server 2008 解决了这个问题(参见 cmets)。据我所知,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 );
没有预先计算的 id 的解决方案,使用查询 re_evaluation
仍未找到,问题仍未得到解答。
【问题讨论】:
-
我很难理解这个查询的意义。请你能解释一下你为什么使用它吗?你的预期结果是什么?
-
我已经编辑了这个问题。我只是想让每个后续的已经存在的数字等于表中的最大数字 + 1。问题是
(SELECT max(nn.id) FROM test nn)的缓存,我想知道是否可以禁用这种缓存机制。是否有可能因为表正在变异? -
这最好是一次性(或特殊维护)查询,因为为了线程安全
SELECT MAX(...) + 1样式的东西必须锁定整个表。为了使这种事情起作用,数据库需要某种方法来区分重复的行 - 是否还有其他一些独特的列?或者,您是否需要保持匹配的行 - deleting duplicates 怎么样? -
第一次复制是什么意思?有没有办法区分第一个实例和其他实例?
-
作为评论发布,因为我还没有弄清楚如何将其翻译成 Oracle:sqlfiddle.com/#!3/b2634/2 这使用了可更新 CTE(WITH 子句)的概念。也许使用 ROWID 可以在某种程度上有所帮助,我只是不确定如何过滤掉不能更改的行和正确修改其余的行。