我们正在使用PL / SQL表(名为pTable)来收集许多要更新的ID。
然而,声明
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
FROM TABLE (pTable));
需要很长时间才能执行。
似乎优化器提出了一个非常糟糕的执行计划,而不是使用在id(作为主键)上定义的索引,它决定在aTable上使用全表扫描。 pTable通常包含很少的值(在大多数情况下只有一个)。
我们可以做些什么来加快速度?我们提出的最好的是处理低pTable.Count(1和2)作为特殊情况,但这当然不是很优雅。
感谢所有伟大的建议。我在http://smartercoding.blogspot.com/2010/01/performance-issues-using-plsql-tables.html的博客中写过这个问题。
答案 0 :(得分:5)
您可以尝试基数提示。如果您(大致)知道集合中的行数,这是很好的。
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT /*+ cardinality( pt 10 ) */
COLUMN_VALUE
FROM TABLE (pTable) pt );
答案 1 :(得分:3)
这是另一种方法。创建一个临时表:
create global temporary table pTempTable ( id int primary key )
on commit delete rows;
要执行更新,请使用pTempTable
的内容填充pTable
并执行:
update
(
select aColumn
from aTable aa join pTempTable pp on aa.id = pp.id
)
set aColumn = 1;
如果不采用优化器提示,应该可以很好地执行。
答案 2 :(得分:2)
糟糕的执行计划可能是不可避免的(不幸的是)。 PL / SQL表没有统计信息,因此优化器无法知道其中的行数很少。是否可以在UPDATE中使用提示?如果是这样,您可能会强制使用索引。
答案 3 :(得分:1)
它有助于告诉优化器使用“正确”索引而不是进行疯狂的全表扫描:
UPDATE /*+ INDEX(aTable PK_aTable) */aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
FROM TABLE (CAST (pdarllist AS list_of_keys)));
我无法将此解决方案应用于更复杂的方案,但找到了其他解决方法。
答案 4 :(得分:1)
您可以尝试添加ROWNUM< ......条款。 在该测试中,ROWNUM< 30更改计划以使用索引。 当然,这取决于具有合理最大尺寸的值集。
create table atable (acolumn number, id number);
insert into atable select rownum, rownum from dual connect by level < 150000;
alter table atable add constraint atab_pk primary key (id);
exec dbms_stats.gather_table_stats(ownname => user, tabname => 'ATABLE');
create type type_coll is table of number(4);
/
declare
v_coll type_coll;
begin
v_coll := type_coll(1,2,3,4);
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
FROM TABLE (v_coll));
end;
/
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------
UPDATE ATABLE SET ACOLUMN = 1 WHERE ID IN (SELECT COLUMN_VALUE FROM TABLE (:B1 ))
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | | | 142 (100)| |
| 1 | UPDATE | ATABLE | | | | |
|* 2 | HASH JOIN RIGHT SEMI | | 1 | 11 | 142 (8)| 00:00:02 |
| 3 | COLLECTION ITERATOR PICKLER FETCH| | | | | |
| 4 | TABLE ACCESS FULL | ATABLE | 150K| 1325K| 108 (6)| 00:00:02 |
----------------------------------------------------------------------------------------------
declare
v_coll type_coll;
begin
v_coll := type_coll(1,2,3,4);
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
FROM TABLE (v_coll)
where rownum < 30);
end;
/
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------
UPDATE ATABLE SET ACOLUMN = 1 WHERE ID IN (SELECT COLUMN_VALUE FROM TABLE (:B1 ) WHERE
ROWNUM < 30)
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | | | 31 (100)| |
| 1 | UPDATE | ATABLE | | | | |
| 2 | NESTED LOOPS | | 1 | 22 | 31 (4)| 00:00:01 |
| 3 | VIEW | VW_NSO_1 | 29 | 377 | 29 (0)| 00:00:01 |
| 4 | SORT UNIQUE | | 1 | 58 | | |
|* 5 | COUNT STOPKEY | | | | | |
| 6 | COLLECTION ITERATOR PICKLER FETCH| | | | | |
|* 7 | INDEX UNIQUE SCAN | ATAB_PK | 1 | 9 | 0 (0)| |
---------------------------------------------------------------------------------------------------
答案 5 :(得分:0)
我想知道来自PL / SQL表的subselect中的MATERIALIZE提示是否会强制临时表实例化并帮助优化器?
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT /*+ MATERIALIZE */ COLUMN_VALUE
FROM TABLE (pTable));