我正在尝试将性能低下的MERGE
语句重构为Oracle 12.1.0.2.0中的UPDATE
语句。 MERGE
语句看起来像这样:
MERGE INTO t
USING (
SELECT t.rowid rid, u.account_no_new
FROM t, u, v
WHERE t.account_no = u.account_no_old
AND t.contract_id = v.contract_id
AND v.tenant_id = u.tenant_id
) s
ON (t.rowid = s.rid)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new
大多数情况下性能较低,因为对大型(1亿行)表t
进行了两次昂贵的访问
这些是涉及的简化表:
t
要迁移其account_no
列的目标表。u
迁移说明表,其中包含account_no_old
→account_no_new
映射v
辅助表,用于建模contract_id
和tenant_id
之间的一对一关系架构为:
CREATE TABLE v (
contract_id NUMBER(18) NOT NULL PRIMARY KEY,
tenant_id NUMBER(18) NOT NULL
);
CREATE TABLE t (
t_id NUMBER(18) NOT NULL PRIMARY KEY,
-- tenant_id column is missing here
account_no NUMBER(18) NOT NULL,
contract_id NUMBER(18) NOT NULL REFERENCES v
);
CREATE TABLE u (
u_id NUMBER(18) NOT NULL PRIMARY KEY,
tenant_id NUMBER(18) NOT NULL,
account_no_old NUMBER(18) NOT NULL,
account_no_new NUMBER(18) NOT NULL,
UNIQUE (tenant_id, account_no_old)
);
我无法修改架构。我知道添加t.tenant_id
可以通过防止将JOIN插入v
ORA-38104:“ ON”子句中引用的列无法更新
请注意,无法避免自我联接,因为这种替代的等效查询会导致ORA-38104:
MERGE INTO t
USING (
SELECT u.account_no_old, u.account_no_new, v.contract_id
FROM u, v
WHERE v.tenant_id = u.tenant_id
) s
ON (t.account_no = s.account_no_old AND t.contract_id = s.contract_id)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new
ORA-01779:无法修改映射到非键保留表的列
直觉上,我将在此处应用传递闭包,这应该保证对于t
中的每个更新行,u
和v
中最多只能有1行。但是显然,Oracle无法识别这一点,因此以下UPDATE
语句不起作用:
UPDATE (
SELECT t.account_no, u.account_no_new
FROM t, u, v
WHERE t.account_no = u.account_no_old
AND t.contract_id = v.contract_id
AND v.tenant_id = u.tenant_id
)
SET account_no = account_no_new
以上提到了ORA-01779
。在12c上似乎无法添加未记录的提示/*+BYPASS_UJVC*/
。
我认为该视图仍然是键保留的,即对于t
中的每一行,v
中有确切行,因此 at u
中最多一行。因此,观点应该是可更新的。有什么方法可以重写此查询以使Oracle相信我的判断?
还是我忽略了其他语法,这些语法阻止MERGE
语句对t
的双重访问?
答案 0 :(得分:1)
您可以定义一个临时表,其中包含来自U
和V
的预联接数据。
在contract_id, account_no_old
上带有唯一索引(应该是唯一的)来支持它。
然后,您可以在可更新的联接视图中使用此临时表。
create table tmp as
SELECT v.contract_id, u.account_no_old, u.account_no_new
FROM u, v
WHERE v.tenant_id = u.tenant_id;
create unique index tmp_ux1 on tmp ( contract_id, account_no_old);
UPDATE (
SELECT t.account_no, tmp.account_no_new
FROM t, tmp
WHERE t.account_no = tmp.account_no_old
AND t.contract_id = tmp.contract_id
)
SET account_no = account_no_new
;
答案 1 :(得分:1)
是否可以重写此查询以使Oracle相信我的判断?
通过在目标中引入帮助器列,我设法“说服” Oracle进行合并:
/sys/class/net
编辑
上述想法的变体-子查询直接移至/sys/devices/virtual
部分:
MERGE INTO (SELECT (SELECT t.account_no FROM dual) AS account_no_temp,
t.account_no, t.contract_id
FROM t) t
USING (
SELECT u.account_no_old, u.account_no_new, v.contract_id
FROM u, v
WHERE v.tenant_id = u.tenant_id
) s
ON (t.account_no_temp = s.account_no_old AND t.contract_id = s.contract_id)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;
相关文章:Columns referenced in the ON Clause cannot be updated
编辑2:
ON
答案 2 :(得分:1)
尝试通过更简单的更新来做到这一点。仍然需要一个子选择。
update t
set t.account_no = (SELECT u.account_no_new
FROM u, v
WHERE t.account_no = u.account_no_old
AND t.contract_id = v.contract_id
AND v.tenant_id = u.tenant_id);
鲍比