【问题标题】:Slow Update When Using Oracle PL/SQL Table使用 Oracle PL/SQL 表时更新缓慢
【发布时间】:2016-10-31 00:57:05
【问题描述】:

我们正在使用一个 PL/SQL 表(名为 pTable)来收集一些要更新的 id。

然而,声明

UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
                     FROM   TABLE (pTable));

需要很长时间才能执行。

似乎优化器提出了一个非常糟糕的执行计划,它决定对 aTable 使用全表扫描,而不是使用在 id 上定义的索引(作为主键)。 pTable 通常包含很少的值(大多数情况下只有一个)。

我们可以做些什么来加快速度?我们想出的最好方法是将低 pTable.Count(1 和 2)作为特殊情况处理,但这肯定不是很优雅。

感谢所有伟大的建议。我在我的博客http://smartercoding.blogspot.com/2010/01/performance-issues-using-plsql-tables.html 中写过这个问题。

【问题讨论】:

  • 你在这里忘记了一些东西我认为声明在哪里
  • 是的,抱歉,我按了返回,并且已经发布了问题。
  • 一个表有多少行? aTable 上还有其他索引吗?
  • aTable 目前大约有 175.000 行。还有其他索引,但在这种情况下它们不可用。
  • 大家的好建议。感谢您的提示!我的发言速度更快,也学到了很多东西!

标签: oracle plsql


【解决方案1】:

您可以尝试基数提示。如果您(大致)知道集合中的行数,这很好。

UPDATE aTable 
SET aColumn = 1 
WHERE id IN (SELECT /*+ cardinality( pt 10 ) */ 
                     COLUMN_VALUE 
              FROM   TABLE (pTable) pt ); 

【讨论】:

  • 我接受这个答案,因为它可能是最干净的解决方案。
【解决方案2】:

这是另一种方法。创建临时表:

create global temporary table pTempTable ( id int primary key )
    on commit delete rows;

要执行更新,请使用pTable 的内容填充pTempTable 并执行:

update
(
    select aColumn
    from aTable aa join pTempTable pp on aa.id = pp.id
)
set aColumn = 1;

在不借助优化器提示的情况下应该表现得相当好。

【讨论】:

  • 好主意,我试试看。
【解决方案3】:

糟糕的执行计划可能是不可避免的(不幸的是)。 PL/SQL 表没有统计信息,因此优化器无法知道其中的行数很少。是否可以在 UPDATE 中使用提示?如果是这样,您可能会以这种方式强制使用索引。

【讨论】:

  • 感谢 Nils,我应用了提示来使用索引 .. 查看我的解决方案。
【解决方案4】:

它有助于告诉优化器使用“正确”的索引,而不是进行疯狂的全表扫描:

UPDATE /*+ INDEX(aTable PK_aTable) */aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
                  FROM   TABLE (CAST (pdarllist AS list_of_keys)));

我无法将此解决方案应用于更复杂的场景,但找到了其他解决方法。

【讨论】:

    【解决方案5】:

    您可以尝试添加 ROWNUM

    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)|          |
    ---------------------------------------------------------------------------------------------------
    

    【讨论】:

      【解决方案6】:

      我想知道 PL/SQL 表的子选择中的 MATERIALIZE 提示是否会强制临时表实例化并帮助优化器?

      UPDATE aTable
      SET aColumn = 1
      WHERE id IN (SELECT /*+ MATERIALIZE */ COLUMN_VALUE
                           FROM   TABLE (pTable));
      

      【讨论】:

        猜你喜欢
        • 2018-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-18
        • 2015-12-10
        • 1970-01-01
        • 2013-03-31
        相关资源
        最近更新 更多