Оператор обновления медленный с функцией sum и nvl - PullRequest
0 голосов
/ 03 июля 2018

У меня есть процедура, в которой столбцы таблицы заполняются с использованием функций 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)) сначала заменяет ноль на 0 в столбце 'pct' таблицы t_overall, а затем добавляет все значения pct с помощью функции sum и обновляет столбец pct таблицы t_final в зависимости от критерии.

Объясните план возврата ниже:

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 голосов
/ 03 июля 2018

Вы не предоставили достаточно информации для уникальной диагностики, поэтому я могу лишь дать вам подсказку, как устранить неполадки в вашем запросе.

Вот моя настройка симуляции ваших данных

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 с

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

Просто перепроверьте тот же подход с вашими данными, если вы наблюдаете другое поведение, не стесняйтесь опубликовать его .

...