Как изменить обновление в Oracle, чтобы оно работало быстрее?

8

У меня есть этот запрос:

UPDATE   (
    SELECT   h.valid_through_dt, h.LAST_UPDATE_TMSTMP
    FROM   ETL_FEE_SCH_TMP d, FEE_SCHEDULE_HISTORICAL h
    WHERE       h.FUND_ID = d.FUND_ID
    AND h.FEETYPE_NAME = d.FEETYPE_NAME
    AND h.BREAKPOINT_TYPE = d.BREAKPOINT_TYPE
    AND h.BREAKPOINT_QTY = d.BREAKPOINT_QTY
    AND h.LOW_BREAKPOINT_AMT = d.LOW_BREAKPOINT_AMT
    AND h.VALID_THROUGH = TO_DATE ('31-DEC-9999', 'dd-mon-yyyy')
    AND h.universe = 'DC'
    AND h.universe = d.universe
    AND EXISTS
    (
        SELECT 1
        FROM FEE_SCHEDULE s
        WHERE s.FUND_ID = h.FUND_ID
        AND s.FEETYPE_NAME = h.FEETYPE_NAME
        AND s.BREAKPOINT_TYPE = h.BREAKPOINT_TYPE
        AND s.BREAKPOINT_QTY = h.BREAKPOINT_QTY
        AND s.LOW_BREAKPOINT_AMT = h.LOW_BREAKPOINT_AMT
        AND s.universe = 'DC'
    )
) updateTable
SET     updateTable.VALID_THROUGH = (SYSDATE - 1),
updateTable.LAST_UPDATE_TMSTMP = SYSTIMESTAMP;

Проблема в том, что этот запрос занимает много времени. Я не знаю, возможно ли запустить это параллельно, или было бы проще обновить курсор в функции конвейера.

Что ты предлагаешь?

Это вся информация, которую я считаю актуальной.

Это план выполнения внутреннего выбора:

Execution Plan
----------------------------------------------------------
Plan hash value: 57376096
---------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name                     | Rows  | Bytes| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                          |     1 |   306 |  8427   (1)| 00:01:42 |
|   1 |  NESTED LOOPS                |                          |       |       |            |          |
|   2 |   NESTED LOOPS               |                          |     1 |    306|  8427   (1)| 00:01:42 |
|   3 |    MERGE JOIN CARTESIAN      |                          |     1 |    192|  8426   (1)| 00:01:42 |
|*  4 |     INDEX RANGE SCAN         | SYS_C000666              |     1 |     96|     2   (0)| 00:00:01 |
|   5 |     BUFFER SORT              |                          |  3045K|   278M|  8425   (1)| 00:01:42 |
|   6 |      SORT UNIQUE             |                          |  3045K|   278M|  8425   (1)| 00:01:42 |
|*  7 |       TABLE ACCESS FULL      | FEE_SCHEDULE             |  3045K|   278M|  8425   (1)| 00:01:42 |
|*  8 |    INDEX RANGE SCAN          | FEE_SCHDL_IDX1           |     1 |       |     1   (0)| 00:00:01 |
|*  9 |   TABLE ACCESS BY INDEX ROWID| FEE_SCHEDULE_HISTORICAL  |     1 |   114 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("D"."UNIVERSE"='DC')
   7 - filter("S"."UNIVERSE"='DC')
   8 - access("H"."UNIVERSE"='DC' AND "S"."FUND_ID"="H"."FUND_ID" AND
              "S"."FEETYPE_NAME"="H"."FEETYPE_NAME" AND
              "S"."BREAKPOINT_TYPE"="H"."BREAKPOINT_TYPE" AND
              "S"."BREAKPOINT_QTY"="H"."BREAKPOINT_QTY" AND
              "S"."LOW_BREAKPOINT_AMT"="H"."LOW_BREAKPOINT_AMT")
       filter("H"."FUND_ID"="D"."FUND_ID" AND
              "H"."FEETYPE_NAME"="D"."FEETYPE_NAME" AND
              "H"."BREAKPOINT_TYPE"="D"."BREAKPOINT_UNIT_TY

Данные таблицы:

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
UNIVERSE|FUND_ID   |FEETYPE_NAME |BREAKPOINT_TYPE|BREAKPOINT_QTY|LOW_BREAKPOINT_AMT|HIGH_BREAKPOINT_AMT|FEE_PCT|FEE_SCHDL_SEQ_ID|GROUP_ID|LAST_UPDATE_TMSTMP  |VALID_FROM|VALID_THROUGH|INSERT_TMSTMP        |JOB_ID|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
DC      |DC9ZTPLPHO|DeferLoad    |Percentage     |4             |10000             |300000             |3.14   |780250          |null    |1/4/2012  3:59:54 PM|6/23/2012 |12/31/9999   |1/5/2011   3:59:54 PM|666   |
DC      |DCE86Y8XFU|RedemptionFee|Percentage     |9             |  100             |100500             |7.67   |780251          |null    |6/4/2012  4:49:54 PM|11/12/2011|12/31/9999   |8/17/2011  2:00:54 PM|666   |
DC      |DCAYL0KONA|FrontLoad    |Percentage     |2             |50000             |601500             |5.00   |780252          |null    |4/25/2012 4:49:54 PM|8/2/2012  |12/31/9999   |12/19/2012 9:59:00 PM|666   |
DC      |DC9ZTPLPHO|DeferLoad    |Percentage     |7             |80000             |900000             |2.24   |780252          |null    |4/25/2012 4:49:54 PM|8/2/2012  |12/31/9999   |12/19/2012 9:59:00 PM|666   |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Это скрипт исторической таблицы:

CREATE TABLE FEE_SCHEDULE_HISTORICAL
(
  UNIVERSE                        VARCHAR2(2 BYTE) NOT NULL,
  FUND_ID                         VARCHAR2(10 BYTE) NOT NULL,
  FEETYPE_NAME                    VARCHAR2(75 BYTE),
  BREAKPOINT_TYPE                 VARCHAR2(50 BYTE),
  BREAKPOINT_QTY                  VARCHAR2(10 BYTE),
  LOW_BREAKPOINT_AMT              NUMBER(19,6),
  HIGH_BREAKPOINT_AMT             NUMBER(19,6),
  FEE_PCT                         NUMBER(19,6),
  FEE_SCHDL_SEQ_ID                NUMBER        NOT NULL,
  GROUP_ID                        NUMBER,
  LAST_UPDATE_TMSTMP              DATE          NOT NULL,
  VALID_FROM                      DATE          NOT NULL,
  VALID_THROUGH                   DATE          NOT NULL,
  INSERT_TMSTMP                   DATE          NOT NULL,
  JOB_ID                          NUMBER        NOT NULL
);

CREATE UNIQUE INDEX FEE_SCHDL_PK ON FEE_SCHEDULE_HISTORICAL(FEE_SCHDL_SEQ_ID);

CREATE UNIQUE INDEX FEE_SCHDL_HST_IDX ON FEE_SCHEDULE_HISTORICAL (
    UNIVERSE,
    FUND_ID,
    FEETYPE_NAME,
    BREAKPOINT_TYPE,
    BREAKPOINT_QTY, 
    LOW_BREAKPOINT_AMT,
    VALID_FROM,
    JOB_ID
)

CREATE INDEX FEE_SCHEDULE_HST_IDX2 ON FEE_SCHEDULE_HISTORICAL(LAST_UPDATE_TMSTMP)

CREATE INDEX FEE_SCHEDULE_HST_IDX3 ON FEE_SCHEDULE_HISTORICAL(VALID_THROUGH)

ALTER TABLE FEE_SCHEDULE_HISTORICAL ADD (
    CONSTRAINT FEE_SCHDL_PK
    PRIMARY KEY
    (FEE_SCHDL_SEQ_ID)
);

Это другая таблица:

CREATE TABLE FEE_SCHEDULE
(
  UNIVERSE                        VARCHAR2(2 BYTE) NOT NULL,
  FUND_ID                         VARCHAR2(10 BYTE) NOT NULL,
  FEETYPE_NAME                    VARCHAR2(75 BYTE),
  BREAKPOINT_TYPE                 VARCHAR2(50 BYTE),
  BREAKPOINT_QTY                  VARCHAR2(10 BYTE),
  LOW_BREAKPOINT_AMT              NUMBER(19,6),
  HIGH_BREAKPOINT_AMT             NUMBER(19,6),
  FEE_PCT                         NUMBER(19,6),
  JOB_RUN_ID                      NUMBER        NOT NULL,
  FILE_DATE                       DATE          NOT NULL,
  CYCLE_DATE                      DATE          NOT NULL
)

Временная таблица является результатом FEE_SCHEDULE_HISTORICAL минус FEE_SCHEDULE

StrayChild01
источник
Вопрос настолько подробный, насколько это действительно должно быть в стеке DBA.
ПСТ
Не ..AND EXISTS (SELECT NULL..всегда вернет false? Будет ли это - ..AND NOT EXISTS (SELECT 1..иметь больше смысла?
AnBisw
2
@Annjawn Привет, в соответствии с этим forum.oracle.com/forums/thread.jspa?threadID=353014 NULL или 1 являются только «символическими» именами (они никогда не используются вне суб-выбора).
Да, и я знаю об этом. Но это не то, что я спросил? SELECT NULL...просто сбивает с толку и не должен использоваться (если вы не используете UNION).
AnBisw
3
Вы пытались добавить индекс fee_schedule (universe, fund_id, feetype_name, breakpoint_type, breakpoint_qty, low_breakpoint_amt). Может быть, один universe, fund_id уже достаточно хорош, чтобы превратить этот FTS в сканирование индекса.
a_horse_with_no_name

Ответы:

2

Я предполагаю, что ваш FEE_SCHEDULEстол намного меньше, чем FEE_SCHEDULE_HISTORICALстол, так что вы можете использовать EXISTSнемного больше. Погружение в FEE_SCHEDULEтаблицу построчно может быть относительно дешевой операцией по сравнению с присоединением ее ко всем строкам в FEE_SCHEDULE_HISTORICAL.

Поскольку ETL_FEE_SCH_TMPтаблица является FEE_SCHEDULE_HISTORICALминусом FEE_SCHEDULE, вы можете выполнить обновление всего лишь с помощью нескольких EXISTSоператоров, без всех объединений и избавив вас от необходимости создавать временную таблицу. Вам не нужен временный стол.

Я думаю, что это может быть стоит посмотреть:

update FEE_SCHEDULE_HISTORICAL H
set H.VALID_THROUGH = (sysdate - 1), H.LAST_UPDATE_TMSTMP = SYSTIMESTAMP
where 
    H.VALID_THROUGH = TO_DATE ('31-DEC-9999', 'dd-mon-yyyy')
    AND H.universe = 'DC'
    AND NOT EXISTS
    (
    SELECT 1
        FROM FEE_SCHEDULE F
        WHERE 
            F.universe = H.Universe
            AND F.FUND_ID = H.FUND_ID
            AND F.FEETYPE_NAME = H.FEETYPE_NAME
            AND F.BREAKPOINT_TYPE = H.BREAKPOINT_TYPE
            AND F.BREAKPOINT_QTY = H.BREAKPOINT_QTY
            AND F.LOW_BREAKPOINT_AMT = H.LOW_BREAKPOINT_AMT
            AND F.HIGH_BREAKPOINT_AMT = H.HIGH_BREAKPOINT_AMT
            AND F.FEE_PCT = H.FEE_PCT
    )
    AND EXISTS
        (
        SELECT 1
        FROM FEE_SCHEDULE FF
        WHERE 
            FF.universe = 'DC'
            AND FF.FUND_ID = h.FUND_ID
            AND FF.FEETYPE_NAME = h.FEETYPE_NAME
            AND FF.BREAKPOINT_TYPE = h.BREAKPOINT_TYPE
            AND FF.BREAKPOINT_QTY = h.BREAKPOINT_QTY
            AND FF.LOW_BREAKPOINT_AMT = h.LOW_BREAKPOINT_AMT
  )

Также рассмотрите возможность добавления в FEE_SCHEDULEтаблицу индекса , аналогичного индексу на FEE_SCHEDULE_HISTORICAL. Это действительно помогает объяснить план вместе.

CREATE UNIQUE INDEX FEE_SCHDL_IDX ON FEE_SCHEDULE (
    UNIVERSE,
    FUND_ID,
    FEETYPE_NAME,
    BREAKPOINT_TYPE,
    BREAKPOINT_QTY, 
    LOW_BREAKPOINT_AMT
);
Друзин
источник
1

Вы пытались запустить SQL Tuning Advisor для своего заявления? Обычно он находит отсутствующие индексы, статистику, дорогостоящие операции и т. Д. Это довольно легко сделать с помощью разработчика SQL, например. см. http://www.oracle.com/webfolder/technetwork/tutorials/obe/db/sqldev/r30/TuningAdvisor/TuningAdvisor.htm

Дитер DHoker
источник