回答

收藏

Oracle中看似关键的保留视图的更新引发了ORA-01779

技术问答 技术问答 272 人阅读 | 0 人回复 | 2023-09-14

问题
7 H/ R; H# I; W( X, R我正在尝试将性能不佳的MERGE语句重构为UPDATEOracle 12.1.0.2.0中的语句。该MERGE语句如下所示:
! C0 [( ~, A$ C' I; z; fMERGE INTO t. P* x* [- n1 Q5 y8 K9 j
USING (5 }4 P) G% Q5 a5 `6 O
  SELECT t.rowid rid, u.account_no_new. R6 _( o( s9 m
  FROM t, u, v
, m* @4 M; }/ T- ^+ @+ P* Q  WHERE t.account_no = u.account_no_old9 m, j; }, J! l" u
  AND t.contract_id = v.contract_id+ o+ b9 q# x' c0 @: ?$ Q4 x
  AND v.tenant_id = u.tenant_id
0 h* e) Q7 w7 H0 w: D$ @) s& U0 w1 y1 v) u
ON (t.rowid = s.rid)9 I, {  \6 S( m, l$ W) `
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new: I! v' Z4 @9 T0 S$ l
它主要是低性能的,因为对大型(100M行)表有两次昂贵的访问 t
- k: b+ w% F( z架构图
7 U9 M' H# I% m2 z* Q- ~这些是涉及的简化表:
! h0 m- w- F; G$ a& `t``account_no正在迁移其列的目标表。
2 P* E2 A- W5 Yu含有迁移指令表account_no_old鈫account_no_new映射, o. A/ r% w0 b$ H* L, O
v辅助表,用于模拟contract_id和之间的一对一关系tenant_id
" D; k4 f9 x* f+ [- C9 @+ W

0 ^  Z# J  V3 Y/ r( N! F该架构为:  W% J9 H/ h7 I7 E& S0 {/ _0 m1 r: J
CREATE TABLE v (
& b' z9 s! q  v  contract_id NUMBER(18) NOT NULL PRIMARY KEY,
! `  k% _& x: m0 f. d  tenant_id NUMBER(18) NOT NULL
; a) L& h' ?- A: L0 W3 R);
2 m! S9 f9 x7 A7 v2 KCREATE TABLE t (
# Z9 ^# w0 z  N) I9 L  t_id NUMBER(18) NOT NULL PRIMARY KEY,' j7 J' w" U; v8 l; A
  -- tenant_id column is missing here/ L1 [9 [3 g, Z; K0 c* j
  account_no NUMBER(18) NOT NULL,
6 N( u9 s" G0 P  contract_id NUMBER(18) NOT NULL REFERENCES v- o+ G! l0 J0 q2 @2 u) z6 `! Y4 _
);
) U4 f- Z* t4 L( I* |7 zCREATE TABLE u (! C" o$ C1 i9 }  o, o
  u_id NUMBER(18) NOT NULL PRIMARY KEY,
% b8 ^3 }! f, V2 n  tenant_id NUMBER(18) NOT NULL,
) W, M# a7 ?( f+ o: a, k' t  account_no_old NUMBER(18) NOT NULL," L4 ^6 X8 t6 G; U2 D; V
  account_no_new NUMBER(18) NOT NULL,
8 o7 I+ z3 a. T2 n, M- I1 |  k  UNIQUE (tenant_id, account_no_old)- E8 c, l" {( }7 ~' H
);! [* O4 Y# b& ?- r% b/ J2 J" P9 J
我无法修改架构。我知道添加t.tenant_id将通过阻止JOIN来解决问题v' L( k: y, o. P% b) Q; Y+ k
替代MERGE不起作用:$ [6 d5 a, j( o  J* @

: V2 V. l  G/ gORA-38104:ON子句中引用的列无法更新) E* a8 ^: f) ]9 k" ]

1 a3 [5 `" A1 t注意,无法避免自我联接,因为这种替代的等效查询会导致ORA-38104:
! p& i5 l7 L5 Y0 p# ^/ kMERGE INTO t
# V; t' a9 o" iUSING (
6 |0 @) a+ u- b4 d) S- j  SELECT u.account_no_old, u.account_no_new, v.contract_id
4 a* y% Q( h4 Y( T$ o  FROM u, v
# v% w6 Z. s# W4 `4 ]2 G: W  R  WHERE v.tenant_id = u.tenant_id- h  e* E, {2 m% z
) s
+ `) o4 z& A; cON (t.account_no = s.account_no_old AND t.contract_id = s.contract_id)
9 s& p' j( v, g! r# g- {WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new7 ~+ d8 |& j( Y5 R  e
UPDATE视图不起作用:8 ?: C" b' M: R; M( x4 O
- w0 o/ T3 @1 E3 N1 N+ l0 S. P5 S
ORA-01779:无法修改映射到非键保留表的列5 ]( s1 E/ X' O- ~7 x! R& M
" J* e9 ^' @; V  [2 J
凭直觉,我将在此处应用传递闭包,这应确保in中的每个更新t行inu和in中最多只能有1行v。但是显然,Oracle无法识别这一点,因此以下UPDATE语句不起作用:  L1 O$ {* N, u& n. h
UPDATE (# N, _* c+ N) n2 _6 m
  SELECT t.account_no, u.account_no_new
4 a; M$ {7 n  b# n  \" l( B  FROM t, u, v
0 M9 D6 L, ?& u, P4 L1 }; p  WHERE t.account_no = u.account_no_old, ]; @5 s  b/ U, q  K+ t! L
  AND t.contract_id = v.contract_id* q: h% ?* R. m9 \; o: Q- ~
  AND v.tenant_id = u.tenant_id# p/ B6 ~6 z% }! z& a( M1 q# W
)" X9 @/ P  x2 E1 {; Z! p; W
SET account_no = account_no_new
$ l. U7 M7 C9 i2 S  U6 m以上提出ORA-01779。添加未记录的提示/*+BYPASS_UJVC*/似乎在12c上不再起作用。# X2 z: a3 w* s+ T
如何告诉Oracle视图是关键的?
8 q0 g, s9 }# l9 i' r/ O9 x在我看来,该视图仍然是关键的保护,即,对于每行t, 恰好在 其中一行v,因此 最多
% b& _- r; e9 T( ^在一行u。因此,该观点应该是可更新的。有什么方法可以重写此查询以使Oracle相信我的判断?, v4 m+ \" g6 q# Y
还是有其他我忽略的语法阻止了该MERGE语句的双重访问t?# P! E7 U* e5 ~- J( |1 q) P8 b' x
                & c9 d* W4 U) `# i) e/ e% v
解决方案:4 ]0 z. T" g) {" b3 l
                4 y* h6 j4 i! s1 r, x& {: W0 X

2 V2 z) B" b% P7 h9 M+ M
' v% b. |* M2 f5 n% }! v0 w' }               
2 T- M, C' q& h& g; w6 ^# F有什么方法可以重写此查询以使Oracle相信我的判断?
9 a5 j6 C' ^2 F$ e3 S. w) F/ A8 g; F2 e& j- m% \
通过在目标中引入帮助器列,我设法“说服” Oracle进行合并:2 Z# C+ |& E) [( n4 j9 R% D+ q- e
MERGE INTO (SELECT (SELECT t.account_no FROM dual) AS account_no_temp,6 E9 W+ e" f, @# t6 U/ j6 ~0 ^
                    t.account_no, t.contract_id ' I% N$ X/ q* a! e6 |3 E% B
            FROM t) t& Z# g- m, `% l: m. m2 l) ~
USING (7 U' q  X0 V* W9 g0 p8 V
  SELECT u.account_no_old, u.account_no_new, v.contract_id
' H3 |, i! T) p- ~/ q& Q' N  FROM u, v1 B) T4 s7 d3 f/ F# B
  WHERE v.tenant_id = u.tenant_id8 Q8 |. q4 n% o$ Y( R6 p# [
) s
2 T; @( P2 e& w4 N! Q0 l% `ON (t.account_no_temp = s.account_no_old AND t.contract_id = s.contract_id)# }! m; f8 m& R. g9 v
WHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;; r1 O( `9 f  W/ c& ^9 P( k( T3 u
db & T  v# t' _0 ^% s2 Z
fiddle演示
1 y, B; m* N# n2 A; o' Y1 }

( O8 r$ A# \3 W) r4 V编辑( B; _( h1 j7 Y) p( M# }
上面的想法的变体-子查询直接移至ON部分:* N* N; `1 p1 c& E3 U2 l7 {$ I. X
MERGE INTO (SELECT t.account_no, t.contract_id FROM t) t4 |9 Z2 V8 d5 A: m3 b/ Z' u% T) |
USING (; |0 O/ _$ I" R, t! e# L8 s( R8 |
      SELECT u.account_no_old, u.account_no_new, v.contract_id
/ f3 o) s% L8 Y) F      FROM u, v; v# \! d/ N) E' A1 H  h( n
      WHERE v.tenant_id = u.tenant_id; C$ m8 ?4 X( y- h3 G( f0 D
    ) s1 c1 ~; \5 C; W5 h0 S6 Y( o
ON ((SELECT t.account_no FROM dual) = s.account_no_old
* n+ g. N) y4 H8 J) O8 F( Z     AND t.contract_id = s.contract_id)
/ _. @/ E% ~+ }/ h! f: L1 i, {$ RWHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;
. r0 }. u/ H2 w9 f7 \( I0 W/ pdb  fiddle
( b' E5 ?! N; ]  h# Edemo2
7 O4 J' S0 {" x9 ^6 V4 u# t
相关文章:ON子句中引用的列无法更新) V; E  K- v$ `2 s' y& G4 L
编辑2:) B) J, v. j4 d" a) q/ I
MERGE INTO (SELECT t.account_no, t.contract_id FROM t) t
; o  H; M( Y$ K/ X/ k9 b" RUSING (SELECT u.account_no_old, u.account_no_new, v.contract_id9 Q1 ^' f% B  h  S7 S1 X8 P3 z
       FROM u, v/ y, R( t4 E" b4 z/ N- R
       WHERE v.tenant_id = u.tenant_id) s
5 U2 L  d/ R: b0 Y7 OON((t.account_no,t.contract_id,'x')=((s.account_no_old,s.contract_id,'x')) OR 1=2)
' A# i  T; `' h5 C7 p7 q1 W; J' hWHEN MATCHED THEN UPDATE SET t.account_no = s.account_no_new;
8 t3 l' m. B0 A: J8 D3 @db  fiddle6 L. X( j/ n" E4 m: ^
demo3
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则