在过去,建议将IN 用于驱动表 - t1 - 很大而子查询中的表 - t2 - 很小的情况。 WHERE EXISTS 被认为更适合该比率翻转的情况。您的数字(t1 = 10,000,000 和 t2 = 20,000)符合第一种情况。
但是,Oracle 优化器多年来变得更加聪明。鉴于您发布的情况 - t1(cliente_codigo, caja_codigo, caja_numero) 上的复合索引,t2 上没有索引 - 此更新产生与您的 where exists 版本相同的解释计划(在 11gR2 和 12cR2 上):
update cajas t1
set t1.anio = (select anio from tempos_Cajas t2
where t1.cliente_codigo = t2.cliente_codigo
and t1.caja_codigo = t2.caja_codigo
and t1.caja_numero = t2.caja_numero)
where (t1.cliente_codigo, t1.caja_codigo, t1.caja_numero) in
(select t2.cliente_codigo, t2.caja_codigo, t2.caja_numero
from tempos_Cajas t2)
;
计划是这样的:
SQL>
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1411510459
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 30M| 1230M| | 900M (4)|999:59:59 |
| 1 | UPDATE | T1 | | | | | |
| 2 | MERGE JOIN SEMI | | 30M| 1230M| | 136 (2)| 00:00:02 |
| 3 | INDEX FULL SCAN | T1_COMP_IDX | 30M| 801M| | 0 (0)| 00:00:01 |
|* 4 | SORT UNIQUE | | 20000 | 292K| 1112K| 136 (2)| 00:00:02 |
| 5 | TABLE ACCESS FULL| T2 | 20000 | 292K| | 29 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL | T2 | 1 | 28 | | 29 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T1"."CLIENTE_CODIGO"="T2"."CLIENTE_CODIGO" AND
"T1"."CAJA_CODIGO"="T2"."CAJA_CODIGO" AND "T1"."CAJA_NUMERO"="T2"."CAJA_NUMERO")
filter("T1"."CAJA_NUMERO"="T2"."CAJA_NUMERO" AND
"T1"."CAJA_CODIGO"="T2"."CAJA_CODIGO" AND
"T1"."CLIENTE_CODIGO"="T2"."CLIENTE_CODIGO")
6 - filter("T2"."CLIENTE_CODIGO"=:B1 AND "T2"."CAJA_CODIGO"=:B2 AND
"T2"."CAJA_NUMERO"=:B3)
24 rows selected.
SQL>
这是一个相当灾难性的计划,因为它会影响驾驶台上的每一行。更好的解决方案是改用 MERGE。
merge into t1
using ( select * from t2 ) t2
on (t1.cliente_codigo = t2.cliente_codigo
and t1.caja_codigo = t2.caja_codigo
and t1.caja_numero = t2.caja_numero)
when matched then
update set t1.anio = t2.anio ;
这是一个更好的计划:
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 525352362
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 20000 | 507K| 29 (0)| 00:00:01 |
| 1 | MERGE | T1 | | | | |
| 2 | VIEW | | | | | |
| 3 | NESTED LOOPS | | | | | |
| 4 | NESTED LOOPS | | 20000 | 1582K| 29 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | T2 | 20000 | 546K| 29 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | T1_COMP_IDX | 30M| | 0 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 53 | 0 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("T1"."CLIENTE_CODIGO"="T2"."CLIENTE_CODIGO" AND
"T1"."CAJA_CODIGO"="T2"."CAJA_CODIGO" AND "T1"."CAJA_NUMERO"="T2"."CAJA_NUMERO")
请记住,解释计划是指示性的,对于使用带有伪造统计数据的玩具表的计划来说,这会加倍,因此请在真实数据结构上对不同方法进行基准测试。