【发布时间】:2019-01-30 17:39:22
【问题描述】:
问题
我正在尝试在 Oracle 12.1.0.2.0 中将性能较低的 MERGE 语句重构为 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
这主要是因为对大型(100M 行)表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 可以通过阻止加入v 来解决问题
替代 MERGE 不起作用:
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
UPDATE 视图不起作用:
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。添加未记录的提示 /*+BYPASS_UJVC*/ 在 12c 上似乎不再起作用。
如何告诉 Oracle 视图是密钥保留?
在我看来,视图仍然是保留键的,即对于t 中的每一行,v 中恰好一行,因此最多 u 中的一行。因此视图应该是可更新的。有没有办法重写这个查询,让 Oracle 相信我的判断?
或者是否有任何其他我忽略的语法阻止MERGE 语句对t 的双重访问?
【问题讨论】:
-
你能让
u.account_no_old独一无二吗?一个唯一的索引可以做到这一点。 (顺便说一句,bypass_ujvc已在 11.2 中删除。) -
@WilliamRobertson。不,account_no 值在每个租户的基础上都是唯一的,因此
v的连接。 -
嗯,这就是甲骨文认为它没有保留密钥的原因。通常的替代方法是 PL/SQL 游标,可以作为 Cursor FOR 循环或批量收集 +
forall。 -
抱歉,我误读了
V上的PK,这实际上意味着每个contract_id都有精确的一个tenant_id。所以你的加入是 - 我称之为 - count保留。但恕我直言,Oracle 只有在将 FK 连接到 PK 时才能保留密钥,这可以在更多表上传递,但在您的设置中没有完整归档。 -
添加了我的答案。不确定我是否遗漏了什么。
标签: sql oracle merge updatable-views