Luk*_*der 6 sql oracle merge updatable-views
我正在尝试将低性能MERGE语句重构为UPDATEOracle 12.1.0.2.0中的语句.该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
Run Code Online (Sandbox Code Playgroud)
它大多是低性能的,因为对大型(100M行)表有两个昂贵的访问 t
这些是涉及的简化表:
taccount_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)
);
Run Code Online (Sandbox Code Playgroud)
我无法修改架构.我知道添加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
Run Code Online (Sandbox Code Playgroud)
ORA-01779:无法修改映射到非密钥保留表的列
直观地说,我会在这里适用传递闭包,这应该保证每个更新的行中t,有能里最多只有1行u和v.但显然,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
Run Code Online (Sandbox Code Playgroud)
以上提出ORA-01779./*+BYPASS_UJVC*/在12c上添加未记录的提示似乎不再起作用.
在我看来,认为仍是关键保护,即在每一行t,有恰好一排v,因此至多在一排u.因此,视图应该是可更新的.有没有办法重写此查询以使Oracle信任我的判断?
或者是否有任何其他语法我忽略了阻止MERGE语句的双重访问t?
有没有办法重写这个查询,让Oracle相信我的判断?
我成功地“说服”Oracle 通过在目标中引入辅助列来进行 MERGE:
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;
Run Code Online (Sandbox Code Playgroud)
编辑
上述想法的变体 - 子查询直接移动到ON部分:
MERGE INTO (SELECT 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 ((SELECT t.account_no FROM dual) = s.account_no_old
AND t.contract_id = s.contract_id)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;
Run Code Online (Sandbox Code Playgroud)
相关文章:ON 子句中引用的列无法更新
编辑2:
MERGE INTO (SELECT 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,t.contract_id,'x')=((s.account_no_old,s.contract_id,'x')) OR 1=2)
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
354 次 |
| 最近记录: |