【ORACLE】记录一些ORACLE的merge into语句的BUG

【ORACLE】记录一些ORACLE的merge into语句的BUG

一、自相矛盾-DML重启动行为差异,违反acid原则

发现版本:10g ~ 23ai
这个用例在我之前的文章里有提过,ORACLE和PG系关于并发事务行为有一个非常大的差异,就是ORACLE在某些并发冲突的场景下会进行DML重启动,但是对比下面两个例子,可以发现无论采取何种事务一致性实现逻辑,在read commit下,最终的结果无外乎得到(1,‘Alice’)、(10,‘Alice’)、(1,‘Tom’)三种其一,但ORACLE的merge into 却得到了(10,‘Tom’)这一理论不应该存在的记录。
用例一:

drop table test_dml_restart;

create table test_dml_restart(id number ,name varchar2(10));
insert into test_dml_restart values (1,'Alice');
commit;

--会话一,执行后不提交
update test_dml_restart set id=10 where name='Alice';

--会话二执行,被锁
update test_dml_restart set name='Tom' where id=1;

--会话一提交,会话二自动解锁,更新0行,会话2根据id=1进行了重启动,找不到id=1的记录
commit;

--会话二查询
select * from test_dml_restart;

10,Alice

用例二:

drop table test_dml_restart;

create table test_dml_restart(id number ,name varchar2(10));
insert into test_dml_restart values (1,'Alice');
commit;

--会话一,执行后不提交
update test_dml_restart set id=10 where name='Alice';

--会话二执行,被锁
merge into test_dml_restart t1
using (select * from dual) t2
on (t1.id=1)
when matched then 
update set t1.name='Tom';

--会话一提交,会话二自动解锁,更新1行,会话2仍然按旧的id找到了数据进行更新
commit;

--会话二查询
select * from test_dml_restart;

10,Tom

在asktom上也有人发现过,但从10g到目前最新的23ai,这个问题一直未修复
https://asktom.oracle.com/ords/f?p=100:11:::NO::P11_QUESTION_ID:61865893444475

merge and write consistency
Andrey N. Edemsky, May 04, 2006 - 3:51 pm UTC

其实这个问题在ORACLE里通过改写SQL规避,就是把on里的条件,复制一份到update后面的where里

merge into test_dml_restart t1
using (select * from dual) t2
on (t1.id=1)
when matched then 
update set t1.name='Tom' where t1.id=1;

二、百密一疏-触发ORA600的外关联

发现版本:11g 12c 18c 19c

 create table test1 (col1 number,col2 number);
 
 create table test2 (col1 number,col2 number);
 
 insert into test1 values (1,1);

merge into test2
using test1 on (test1.col1=test2.col1(+))
when matched then
  update set test2.col2=test1.col2;

ORA-00600: 内部错误代码, 参数: [25027], [3], [3], [0], [0], [0], [1], [1], [], [], [], []

虽然这个sql本身的确不对,但是ORACLE报错却是ORA-600这个ORACLE自己没有估计到的报错。
19.20及最新的23ai版本已修复这个问题,在23ai中该SQL执行结果是merge 0行。

三、自欺欺人-不能更新on中引用的列

版本:9i ~ 23ai

详见这篇 【ORACLE】你以为的真的是你以为的么?–ORA-38104: Columns referenced in the ON Clause cannot be updated

这个是个ORACLE故意做的限制,本身可以说不算BUG,但是通过特殊方式能绕过这个限制,就可以算个BUG了。所以ORACLE要么承认这个设计是BUG,要么承认开发遗漏了场景也还是BUG,总归有一边是BUG。

merge into test_merge_a a
using test_merge_b b
on (a.id=b.id)
when matched then
  update set id=2;
  
ORA-38104: 无法更新 ON 子句中引用的列: "A"."ID"

--改写
merge into test_merge_a a
using test_merge_b b
on ((a.id=b.id or 1=2) and (a.id=b.id or 1=2 ))
when matched then
  update set a.id=2;
  
 Plan Hash Value  : 4101543598 

--------------------------------------------------------------------------------
| Id  | Operation              | Name         | Rows | Bytes | Cost | Time     |
--------------------------------------------------------------------------------
|   0 | MERGE STATEMENT        |              |    1 |    13 |    4 | 00:00:01 |
|   1 |   MERGE                | TEST_MERGE_A |      |       |      |          |
|   2 |    VIEW                |              |      |       |      |          |
| * 3 |     HASH JOIN          |              |    1 |    76 |    4 | 00:00:01 |
|   4 |      TABLE ACCESS FULL | TEST_MERGE_B |    1 |    32 |    2 | 00:00:01 |
|   5 |      TABLE ACCESS FULL | TEST_MERGE_A |    1 |    44 |    2 | 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 3 - access("A"."ID"="B"."ID")


以上是较新版本中需要注意一些问题。

在早期版本中已经修复过的就不全部列举了,MOS上搜merge into的BUG大把大把的,翻了N页没见底,什么更新视图、分区表、并行、并发、行归档、dblink、压缩表等等有一堆功能和merge into组合使用有问题的,这里只写几个简单的merge into语句有问题的场景

四、无中生有-源数据为空的结果集也能匹配上

版本:11.2.0.2.0
https://asktom.oracle.com/ords/f?p=100:11:0::::P11_QUESTION_ID:9537112000346374938
https://asktom.oracle.com/ords/f?p=100:11:::NO::P11_QUESTION_ID:61865893444475

Same guy - Correction
GPU, August 26, 2013 - 5:11 pm UTC

create table test_merge_null(col1 varchar2(20) not null);
  
MERGE INTO test_merge_null
USING (SELECT dummy
FROM DUAL
WHERE 1 = 2) s
ON (1 = 2)
WHEN NOT MATCHED
THEN
INSERT (col1)
VALUES (s.dummy);

这是11.2.0.2.0 中的 bug,已在 11.2.0.3.0 中修复。还好,11.2.0.2.0这个版本用得少。

五、一山能容二虎-突破主键唯一限制

发现版本:10.2.0.1.0
通过direct path write,能突破主键限制进行插入,使表中数据违反有效的唯一约束,案例来自:
https://blog.csdn.net/jackpk/article/details/3788143

create table KL_TEST
(
   a           NUMBER(15) not null,
   b           NUMBER(15) not null,
    c           NUMBER(15) not null,
   d           NUMBER(15) not null,
    e           NUMBER(15) not null,
   f           NUMBER(15) not null
)
 ;

 10   ;

Table created.

 alter table KL_TEST
    add constraint KL_TEST_PK primary key (A, B, C, D)
  3      ;

Table altered.

CREATE TABLE KL_TEST_1 NOLOGGING PCTFREE 0 AS
 SELECT * FROM KL_TEST
  3   WHERE 1=2;

Table created.

SYS@ora10g> INSERT INTO KL_TEST_1 VALUES (1,1,1,1,1,1);

1 row created.

SYS@ora10g> INSERT INTO KL_TEST_1 VALUES (1,2,1,2,1,2);

1 row created.

SYS@ora10g> INSERT INTO KL_TEST_1 VALUES (1,1,1,1,1,1);

1 row created.

SYS@ora10g> commit;

Commit complete.

 MERGE /*+ APPEND */ INTO KL_TEST trgt
USING
(select fct.a,fct.b,fct.c,fct.d,fct.e,fct.f from KL_TEST_1 fct) tmp
ON
(tmp.a = trgt.a
  and tmp.b = trgt.b
  and tmp.c = trgt.c
  and tmp.d = trgt.d
  and tmp.e = trgt.e
   and tmp.f = trgt.f)
 WHEN NOT MATCHED THEN
 INSERT (a,b,c,d,e,f)
 13   VALUES (tmp.a,tmp.b,tmp.c,tmp.d,tmp.e,tmp.f);

3 rows merged.

SYS@ora10g>  commit;

Commit complete.

SYS@ora10g> SELECT COUNT(*) FROM KL_TEST;

  COUNT(*)
----------
         0

SYS@ora10g> SELECT /*+ FULL (KL_TEST)*/ COUNT(*) FROM KL_TEST;

  COUNT(*)
----------
         3

SYS@ora10g> select * from kl_test;

         A          B          C          D          E          F
---------- ---------- ---------- ---------- ---------- ----------
         1          1          1          1          1          1
         1          1          1          1          1          1
         1          2          1          2          1          2

SYS@ora10g> select CONSTRAINT_NAME,CONSTRAINT_TYPE, STATUS from dba_constraints where table_name ='KL_TEST';

CONSTRAINT_NAME                C STATUS
------------------------------ - --------
SYS_C005398                    C ENABLED
SYS_C005397                    C ENABLED
SYS_C005396                    C ENABLED
SYS_C005395                    C ENABLED
SYS_C005394                    C ENABLED
SYS_C005393                    C ENABLED
KL_TEST_PK                     P ENABLED

7 rows selected.

SYS@ora10g> insert into kl_test values (1,1,1,1,1,1);

1 row created.

SYS@ora10g> select * from kl_test;

         A          B          C          D          E          F
---------- ---------- ---------- ---------- ---------- ----------
         1          1          1          1          1          1
         1          1          1          1          1          1
         1          2          1          2          1          2
         1          1          1          1          1          1

在10.2.0.5.0和11.2.0.4版本中已经没这个问题了

总结

ORACLE从很早的版本就增加了merge into这一语法,虽然给开发人员带来了很大的便利性,但是其BUG数之多非常恐怖,很多BUG从发现到修复跨越了数十年的好几个大版本,甚至还有些BUG一直延续到了最新的23ai版本。国产数据库的开发人员远没有ORACLE那么多,ORACLE都做成这样了,国产数据库要是说merge into里没有BUG我是不相信的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DarkAthena

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值