是一个比较不容易发现的问题。下面几条SQL,哪一条SQL的append hint会生效:

1. INSERT /*+ append,parallel(t1) */ INTO T1 SELECT * FROM T2;  
2. INSERT /*+ parallel(t1), append */ INTO T1 SELECT * FROM T2;  
3. INSERT /*+ this is append */ INTO T1 SELECT * FROM T2;  
4. INSERT /*+ this append */ INTO T1 SELECT * FROM T2;  

要回答这个问题,请先看下面的测试(测试环境:10.2.0.1 for Windows):


SQL> INSERT /*+ append,parallel(t1) */ INTO T1 SELECT * FROM T2;  
已创建55640行。  
统计信息  
----------------------------------------------------------  
      12304  redo size  
SQL> COMMIT;  

SQL> INSERT /*+ parallel(t1), append */ INTO T1 SELECT * FROM T2;  
已创建55640行。  
统计信息  
----------------------------------------------------------  
    5739584  redo size  
SQL> COMMIT;  

SQL> INSERT /*+ this is append */ INTO T1 SELECT * FROM T2;  
已创建55640行。  
统计信息  
----------------------------------------------------------  
    5746604  redo size  
SQL> COMMIT;  

SQL> INSERT /*+ this append */ INTO T1 SELECT * FROM T2;  
已创建55640行。  
统计信息  
----------------------------------------------------------  
      12052  redo size  
SQL> COMMIT;  

从上面的输出可以看到,通过insert语句执行产生的redo size判断,4条SQL语句中,1和4这2条SQL中的append hint起了作用,而2和3这2条SQL中的append hint没有起作用。我们看看第1和第2条SQL,只不过是parallel和append换了个位置,结果就截然不同;而第3和第4条SQL,只是一个多了"is"这个词,另一个没有,其结果也完全不同。这里有什么玄机吗?

这里就需要了解Oracle在解析SQL时,是怎样解析hint的。
Oracle在解析hint,从左到右进行,如果遇到一个词是oracle关键字或者说是保留字,将忽略这个词以及之后的所有词。如果遇到的一个词即不是关键字也不是hint,就忽略该词。如果遇到的一个词是有效的hint,那么就会保留该hint。

Oracle的保留字或者说是关键词(虽然二者在意义不一样,但这里不将其区分),可以通过视图v$reserved_words来查询。"is"正是一个关键词,甚至连","(逗号)也是一个关键词。这样,上面的第2和第3条SQL,Oracle解析时当遇到","和"is"时,就忽略了后面的所有hint。在第4条SQL中,this并不是一个关键词,所以append hint有效。基于这个原理,下面的一条SQL中的hint也是不起作用的:

INSERT /*+ NOLOGGING APPEND */ INTO T1 SELECT * FROM T2; 

在9.2.0.8和11.2.0.2这2个版本下进行同样的测试,结果完全一样。
为了避免这样的情况,在SQL中书写hint时,在/+ /和--+这2种结构内只写hint,而不要写逗号,或者是其他的注释。如果要对SQL写注释,在专门的注释结构中写入。比如/ test comment /。如果与hint混写注释,虽然当时没有关键词在里面,但随着版本升级,很可能会加入新的关键词。

另外,一些很常见的hint形式,比如/+ parallel(t,8) /,/+ index(t,t_idx) /,虽然当前没有问题,但标准的写法应该是:
/+ parallel(t 8) /,/+ index(t t_idx) /

Related Posts