如何使用来自另一个表列的值优化列的更新?
Posted
技术标签:
【中文标题】如何使用来自另一个表列的值优化列的更新?【英文标题】:How to optimize an update of a column using values from another table's column? 【发布时间】:2017-09-22 21:15:41 【问题描述】:我在更新表 t1
中的字段时遇到问题,从表 t2
中获取相同字段的值。我的问题是表t1
有10 万条记录,而表t2
有20000 条记录。
当我运行这个时:
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
and t1.cliente_codigo = '115')
where exists(select * 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)
命令运行了几个小时,我无法更新该字段。
我不是 Oracle 专家,但我想知道是否有任何方法可以优化 SQL 语句?
【问题讨论】:
您确定更新时不涉及锁定吗? 还有where exists应该换成IN,会更快 您的查询没有“table a”或“table b”。 终于摆脱了 t1.anio = inline sql 你可以开始思考 UPDATE ( subquery-with-a-join ) SET cola=colb 我很困惑。要更新,您从 t2 中选择 anio,从相关子查询中您要求两个表中的 cliente_codigo 相同并且 cliente_codigo 是特定值115
?那么也许 where
子句是问题所在 - 你没有那个条件(在特定值上),所以当三列值匹配时,但 cliente_codigo
不是 115,那么 anio
将是更新至NULL
!这是期望的结果吗?我错过了什么?
【参考方案1】:
对于您的查询,您需要tempos_Cajas(cliente_codigo, caja_codigo, caja_numero, anio)
上的复合索引。
也就是说,我认为你想要的逻辑是:
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 exists (select 1
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
) and
t1.cliente_codigo = '115';
除了上述索引之外,您还需要cajas(cliente_codigo)
上的索引。如果cliente_codigo
的类型是数字,则去掉常量上的单引号。
【讨论】:
复合索引为(cliente_codigo, caja_codigo, caja_numero);数据类型为:cliente_codigo(varchar)、caja_codigo(varchar)、caja_numero(numeric)。我已经尝试过你写的逻辑。结果是一样的。【参考方案2】:在过去,建议将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")
请记住,解释计划是指示性的,对于使用带有伪造统计数据的玩具表的计划来说,这会加倍,因此请在真实数据结构上对不同方法进行基准测试。
【讨论】:
以上是关于如何使用来自另一个表列的值优化列的更新?的主要内容,如果未能解决你的问题,请参考以下文章