sum和nvl函数的更新语句很慢

时间:2018-07-03 06:21:14

标签: oracle performance sum updates nvl

我有一个过程,其中使用其他表的列上的sum和nvl函数填充表的列。这些更新查询速度很慢,这会使整体Proc速度变慢。此类更新查询之一如下:

 UPDATE t_final wp
    SET PCT =
        (
        SELECT SUM(NVL(pct,0))
        FROM t_overall
        WHERE rid  = 9
        AND rtype  = 1
        AND sid = 'r12'
        AND pid = 21
        AND mid   = wp.mid
        )
    WHERE rid  = 9 AND rtype  = 1 AND sid = 'r12' AND  pid = 21;

这里t_overall和t_final,两个表都没有任何索引,因为它们在整个过程中都有多个更新。表t_final的记录数约为8500,表t_overall的记录数约为13000。还有其他方法,我可以以更优化的方式编写上述查询吗?

编辑1:此处SUM(NVL(pct,0))函数首先将表t_overall的'pct'列中的null替换为0,然后使用sum函数将所有pct值相加,并根据更新表t_final的pct列条件。

解释计划返回如下:

OPERATION                OBJECT_NAME   CARDINALITY  COST
UPDATE STATEMENT                               6     424
 UPDATE                     T_FINAL
   TABLE ACCESS(FULL)       T_FINAL            6     238
   .  Filter Predicates
   .   AND
   .   RTYPE=6
   .   SID='R12'
   .   RID=9    
   .   PID=21
   SORT(AGGREGATE)                             1
    TABLE ACCESS(FULL)      T_OVERALL          1      30
       Filter Predicates
         AND
         MID-:B1
         RTYPE=6
         SID='R12'
         RID=9  
         PID=21

更新的行数约为2200

编辑2:我已经使用提示/ * + collect_plan_statistics * /运行更新查询,如下所示:

 ALTER session SET statistics_level=ALL;
 UPDATE /*+ gather_plan_statistics */ t_final wp
        SET PCT =
            (
            SELECT SUM(NVL(pct,0))
            FROM t_overall
            WHERE rid  = 9
            AND rtype  = 1
            AND sid = 'r12'
            AND pid = 21
            AND mid   = wp.mid
            )
        WHERE rid  = 9 AND rtype  = 1 AND sid = 'r12' AND  pid = 21;

 select * from
    table (dbms_xplan.display_cursor (format=>'ALLSTATS LAST')); 

结果是:

SQL_ID  gypnfv5nzurb0, child number 1
-------------------------------------
select child_number from v$sql   where sql_id = :1     order by 
child_number

Plan hash value: 4252345203

---------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                | Name                      | Starts | E-Rows | A-Rows |   A-Time   |  OMem |  1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |                           |      1 |        |      2 |00:00:00.01 |       |       |          |
|   1 |  SORT ORDER BY           |                           |      1 |      1 |      2 |00:00:00.01 |  2048 |  2048 | 2048  (0)|
|*  2 |   FIXED TABLE FIXED INDEX| X$KGLCURSOR_CHILD (ind:2) |      1 |      1 |      2 |00:00:00.01 |       |       |          |
---------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(("KGLOBT03"=:1 AND "INST_ID"=USERENV('INSTANCE')))

谢谢。

1 个答案:

答案 0 :(得分:0)

您没有提供足够的信息来进行独特的诊断,所以我只能提示您如何对查询进行故障排除。

这是我的设置模拟您的数据

create table t_final as
select  rownum  mid, 8 + mod(rownum,4) rid,  1 rtype, 'r12' sid, 21 pid, 0 pct from dual
connect by level <= 8800;

drop table T_OVERALL;
create table T_OVERALL as
select  mod(rownum,8800) mid, 8 + mod(rownum,4) rid,  1 rtype, 'r12' sid, 21 pid, rownum pct from dual
connect by level <= 13000;

现在,我运行查询以激活统计信息收集以查看查询在做什么:

SQL> UPDATE /*+ gather_plan_statistics */ t_final wp
  2      SET PCT =
  3          (
  4          SELECT SUM(NVL(pct,0))
  5          FROM t_overall
  6          WHERE rid  = 9
  7          AND rtype  = 1
  8          AND sid = 'r12'
  9          AND pid = 21
 10          AND mid   = wp.mid
 11          )
 12      WHERE rid  = 9 AND rtype  = 1 AND sid = 'r12' AND  pid = 21;

2200 rows updated.

Elapsed: 00:00:00.97

因此,经过了将近一秒钟的时间,如果您进行了大量此类更新,这将很慢。要查看原因,我们显示光标和统计信息(可以使用提示/*+ gather_plan_statistics */来获得历史记录)

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------------------

SQL_ID  3ctaz5gvksb54, child number 0
-------------------------------------
UPDATE /*+ gather_plan_statistics */ t_final wp     SET PCT =         (
        SELECT SUM(NVL(pct,0))         FROM t_overall         WHERE rid
 = 9         AND rtype  = 1         AND sid = 'r12'         AND pid =
21         AND mid   = wp.mid         )     WHERE rid  = 9 AND rtype  =
1 AND sid = 'r12' AND  pid = 21

Plan hash value: 1255260726

-------------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------------------

| Id  | Operation           | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT    |           |      1 |        |      0 |00:00:00.96 |     116K|
|   1 |  UPDATE             | T_FINAL   |      1 |        |      0 |00:00:00.96 |     116K|
|*  2 |   TABLE ACCESS FULL | T_FINAL   |      1 |   2200 |   2200 |00:00:00.01 |      33 |
|   3 |   SORT AGGREGATE    |           |   2200 |      1 |   2200 |00:00:00.92 |     112K|
|*  4 |    TABLE ACCESS FULL| T_OVERALL |   2200 |     33 |   3250 |00:00:00.85 |     112K|
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------------------


   2 - filter(("RID"=9 AND "RTYPE"=1 AND "PID"=21 AND "SID"='r12'))
   4 - filter(("RID"=9 AND "RTYPE"=1 AND "PID"=21 AND "MID"=:B1 AND "SID"='r12'))

所以您看到主要问题出在FULL TABLE SCAN上的T_OVERALL上,它被调用了2200次(列开始,第4行)。

一种补救措施可以根据第4行的过滤谓词提供一个索引:

create index T_OVERALL_IDX on T_OVERALL(mid, rid, rtype, sid, pid);

在相同的数据上,我得到了:

Elapsed: 00:00:00.05

更改后的计划现在使用2200 INDEX RANGE SCAN s

--------------------------------------------------------------------------------------------------------- 
| Id  | Operation                     | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT              |               |      1 |        |      0 |00:00:00.05 |   10272 |
|   1 |  UPDATE                       | T_FINAL       |      1 |        |      0 |00:00:00.05 |   10272 |
|*  2 |   TABLE ACCESS FULL           | T_FINAL       |      1 |   2200 |   2200 |00:00:00.01 |      33 |
|   3 |   SORT AGGREGATE              |               |   2200 |      1 |   2200 |00:00:00.01 |    5755 |
|   4 |    TABLE ACCESS BY INDEX ROWID| T_OVERALL     |   2200 |     33 |   3250 |00:00:00.01 |    5755 |
|*  5 |     INDEX RANGE SCAN          | T_OVERALL_IDX |   2200 |      1 |   3250 |00:00:00.01 |    2505 |
--------------------------------------------------------------------------------------------------------- 

如果您发现其他行为,可以简单地对数据重新检查相同的方法