Как улучшить код процедуры pl-sql, 2 часа для 100k записей - PullRequest
0 голосов
/ 27 сентября 2018

Как улучшить производительность по приведенному ниже коду?Для записи 100 тыс. Требуется 2 часа.

create or replace PROCEDURE APO_RMS_E_PRIO_ITEM_REPL_P
    AS
       O_error_message  VARCHAR2(2000);
       L_program        VARCHAR2(100):= 'APO_RMS_E_PRIO_ITEM_REPL_P';
       L_SOH NUMBER(20,4);
       L_PRIO_ITEM_SOH NUMBER(20,4);
       L_SUM_SOH NUMBER(20,4);
       L_ITEM VARCHAR2(25);
       L_MIN_STOCK NUMBER(20,4);
       L_MAX_STOCK NUMBER(20,4);
       L_ORDER_ROQ NUMBER(20,4);

    --cursor to get all prio items............
    CURSOR CUR_1 IS
    SELECT OT.LOCATION,
           OT.ITEM
      FROM ITEM_MASTER IM,
           ORD_TEMP OT
     WHERE IM.ITEM = OT.ITEM
       AND EXISTS (SELECT 1 FROM RPM_UDA_VIEW RUV WHERE RUV.UDA_ID = 115 AND RUV.UDA_ITEM = IM.ITEM)
       AND EXISTS (SELECT 1 FROM RPM_UDA_VIEW RUV WHERE RUV.UDA_ID = 116 AND RUV.UDA_ITEM = IM.ITEM)
       AND EXISTS (SELECT 1 FROM RPM_UDA_VIEW RUV WHERE RUV.UDA_ID = 117 AND RUV.UDA_ITEM = IM.ITEM)
       AND EXISTS (SELECT 1 FROM RPM_UDA_VIEW RUV WHERE RUV.UDA_ID = 163 AND RUV.UDA_LOV_VALUE != 0 AND RUV.UDA_ITEM = IM.ITEM)
       AND NOT EXISTS (SELECT 1 FROM RPM_UDA_VIEW RUV WHERE RUV.UDA_ID = 122 AND RUV.UDA_ITEM = IM.ITEM)
       AND NOT EXISTS (SELECT 1 FROM RPM_UDA_VIEW RUV WHERE RUV.UDA_ID = 123 AND RUV.UDA_ITEM = IM.ITEM);

    --cursor to get all items for the prio item group........
    cursor cur_2 (l_item varchar2) is
    WITH RUV AS
    (
    SELECT UDA_ID, UDA_ITEM, UDA_LOV_VALUE FROM RPM_UDA_VIEW
    ),
    ARIIS as
    (
    SELECT * FROM APO_RMS_I_ITEM_STG
    )
    SELECT ITEM
      FROM ITEM_MASTER IM
       WHERE EXISTS (SELECT 1 FROM RUV RUV1, RUV RUV2 WHERE RUV2.UDA_ITEM = L_ITEM AND RUV2.UDA_ID = 115 AND RUV1.UDA_ITEM = IM.ITEM AND RUV1.UDA_LOV_VALUE = RUV2.UDA_LOV_VALUE AND RUV1.UDA_ID = RUV2.UDA_ID)
       AND EXISTS (SELECT 1 FROM RUV RUV1, RUV RUV2 WHERE RUV2.UDA_ITEM = L_ITEM AND RUV2.UDA_ID = 116 AND RUV1.UDA_ITEM = IM.ITEM AND RUV1.UDA_LOV_VALUE = RUV2.UDA_LOV_VALUE AND RUV1.UDA_ID = RUV2.UDA_ID)
       AND EXISTS (SELECT 1 FROM RUV RUV1, RUV RUV2 WHERE RUV2.UDA_ITEM = L_ITEM AND RUV2.UDA_ID = 117 AND RUV1.UDA_ITEM = IM.ITEM AND RUV1.UDA_LOV_VALUE = RUV2.UDA_LOV_VALUE AND RUV1.UDA_ID = RUV2.UDA_ID)
       AND EXISTS (SELECT 1 FROM RUV RUV1, RUV RUV2 WHERE RUV2.UDA_ITEM = L_ITEM AND RUV2.UDA_ID = 163 AND RUV2.UDA_LOV_VALUE != 0 AND RUV1.UDA_ITEM = IM.ITEM AND RUV1.UDA_ID = RUV2.UDA_ID)
       AND NOT EXISTS (SELECT 1 FROM RUV RUV1, RUV RUV2 WHERE RUV2.UDA_ITEM = L_ITEM AND RUV2.UDA_ID = 122 AND RUV1.UDA_ITEM = IM.ITEM AND RUV1.UDA_LOV_VALUE = RUV2.UDA_LOV_VALUE AND RUV1.UDA_ID = RUV2.UDA_ID)
       AND NOT EXISTS (SELECT 1 FROM RUV RUV1, RUV RUV2 WHERE RUV2.UDA_ITEM = L_ITEM AND RUV2.UDA_ID = 123 AND RUV1.UDA_ITEM = IM.ITEM AND RUV1.UDA_LOV_VALUE = RUV2.UDA_LOV_VALUE AND RUV1.UDA_ID = RUV2.UDA_ID)
       AND IM.ITEM IN (SELECT ITEM_PARENT FROM ITEM_MASTER WHERE ITEM IN (
    (
    (SELECT A.VARNUMMER
     FROM APO_RMS_I_ITEM_STG A
     WHERE a.date_received =
    (SELECT MAX(a1.date_received)
    FROM apo_rms_i_item_stg a1
    WHERE a1.varuid=a.varuid
    )
     AND a.ANTAL_NUMERISK *NVL(a.ANTAL_MULTIPEL_1, 1)*NVL(a.ANTAL_MULTIPEL_2, 1) BETWEEN
    (SELECT MAX(L.ANTAL_NUMERISK*NVL(L.ANTAL_MULTIPEL_1, 1)*NVL(L.ANTAL_MULTIPEL_2, 1)*0.88)
    FROM APO_RMS_I_ITEM_STG L
    WHERE L.date_received =
      (SELECT MAX(L1.date_received)
      FROM apo_rms_i_item_stg L1
      WHERE L1.varuid=L.varuid
      )
    AND L.VARNUMMER =(select item from item_master where ITEM_PARENT=L_ITEM and PRIMARY_REF_ITEM_IND='Y')
    )
     AND (SELECT MAX(H.ANTAL_NUMERISK*NVL(H.ANTAL_MULTIPEL_1, 1)*NVL(H.ANTAL_MULTIPEL_2, 1)*1.12)
    FROM APO_RMS_I_ITEM_STG H
    WHERE H.date_received =
      (SELECT MAX(H1.date_received)
      FROM apo_rms_i_item_stg H1
      WHERE H1.varuid=H.varuid
      )
    AND H.VARNUMMER=(select item from item_master where ITEM_PARENT=L_ITEM and PRIMARY_REF_ITEM_IND='Y'))
    AND IM.ITEM NOT IN
      (SELECT I.uda_ITEM FROM rpm_uda_view I WHERE I.UDA_ID in (122,123)
      )
    ))
    ));

    BEGIN

    FOR REC_1 IN CUR_1
    LOOP

    L_SUM_SOH := 0;

    FOR REC_2 IN CUR_2(REC_1.ITEM)

    LOOP

    --get stock_on_hand for each item in prio group.....
    SELECT IL.STOCK_ON_HAND
      INTO L_PRIO_ITEM_SOH
      FROM ITEM_LOC_SOH IL
     WHERE IL.LOC = REC_1.LOCATION
       AND IL.ITEM = REC_1.ITEM;

    --get total available stock for each item in prio group.....
    SELECT (CASE WHEN AVAILABLE_SOH < 0 THEN 0 ELSE AVAILABLE_SOH END) INTO L_SOH FROM
    (
    SELECT
         SUM(
         (NVL(ILS.TSF_EXPECTED_QTY, 0) + NVL(ILS.IN_TRANSIT_QTY, 0) + NVL(ILS.STOCK_ON_HAND, 0) + NVL(ON_ORDER_QTY, 0))
         -
         (NVL(ILS.TSF_RESERVED_QTY, 0) + NVL(ILS.RTV_QTY, 0) + NVL(ILS.NON_SELLABLE_QTY, 0) + NVL(ILS.CUSTOMER_RESV, 0))
         ) AVAILABLE_SOH
    FROM ITEM_LOC_SOH ILS,
         (SELECT L.ITEM,
                 NVL(SUM(L.QTY_ORDERED - NVL(L.QTY_RECEIVED, 0)), 0) ON_ORDER_QTY
            FROM ORDHEAD H,
                 ORDLOC L
           WHERE L.ITEM = REC_2.ITEM
             AND L.LOCATION = REC_1.LOCATION
             AND L.LOC_TYPE ='S'
             AND L.ORDER_NO = H.ORDER_NO
             AND H.STATUS = 'A'
             AND L.QTY_ORDERED > NVL(L.QTY_RECEIVED, 0)
             AND H.ORDER_TYPE != 'CO'
        GROUP BY L.ITEM) ORD
    WHERE ILS.ITEM = ORD.ITEM(+)
      AND ILS.ITEM = REC_2.ITEM
      AND ILS.LOC = REC_1.LOCATION);

    L_SUM_SOH := L_SUM_SOH + L_SOH;

    END LOOP;

    --get min max for the prio item....
    SELECT RL.MIN_STOCK,
           RL.MAX_STOCK
      INTO L_MIN_STOCK,
           L_MAX_STOCK
      FROM REPL_ITEM_LOC RL
     WHERE RL.LOCATION = REC_1.LOCATION
       AND RL.ITEM = REC_1.ITEM;

    IF (L_SUM_SOH < L_MIN_STOCK)

    Then

    SELECT OT.QTY_ORDERED
      INTO L_ORDER_ROQ
      FROM RMS13PRD.ORD_TEMP OT
     WHERE OT.LOCATION = REC_1.LOCATION
       AND OT.ITEM = REC_1.ITEM;

    INSERT INTO APO_RMS_E_PRIO_ITEM
     SELECT ORD_TEMP_SEQ_NO,
            CROSSDOCK_IND,
            CROSSDOCK_STORE,
            DEPT,
            SUPPLIER,
            ITEM,
            PACK_IND,
            ORIGIN_COUNTRY_ID,
            UNIT_COST,
            PICKUP_LEAD_TIME,
            SUPP_LEAD_TIME,
            LOCATION,
            LOC_TYPE,
            L_ORDER_ROQ,
            SUPP_PACK_SIZE,
            ORDER_STATUS,
            CONTRACT_NO,
            CONTRACT_TYPE,
            DUE_IND,
            L_SUM_SOH,
            L_PRIO_ITEM_SOH,
            SYSDATE,
            'update_prio_item'
        FROM RMS13PRD.ORD_TEMP OT
       WHERE OT.LOCATION = REC_1.LOCATION
         AND OT.ITEM = REC_1.ITEM;

    COMMIT;

    UPDATE ORD_TEMP OT
       SET OT.QTY_ORDERED = NVL(L_MAX_STOCK, 0) - NVL(L_SUM_SOH, 0)
     WHERE OT.LOCATION = REC_1.LOCATION
       AND OT.ITEM = REC_1.ITEM;

    COMMIT;

    ELSE

    INSERT INTO APO_RMS_E_PRIO_ITEM
     SELECT ORD_TEMP_SEQ_NO,
            CROSSDOCK_IND,
            CROSSDOCK_STORE,
            DEPT,
            SUPPLIER,
            ITEM,
            PACK_IND,
            ORIGIN_COUNTRY_ID,
            UNIT_COST,
            PICKUP_LEAD_TIME,
            SUPP_LEAD_TIME,
            LOCATION,
            LOC_TYPE,
            QTY_ORDERED,
            SUPP_PACK_SIZE,
            ORDER_STATUS,
            CONTRACT_NO,
            CONTRACT_TYPE,
            DUE_IND,
            L_SUM_SOH,
            L_PRIO_ITEM_SOH,
            SYSDATE,
            'delete_prio_item'
       FROM RMS13PRD.ORD_TEMP OT
      WHERE OT.LOCATION = REC_1.LOCATION
        AND OT.ITEM = REC_1.ITEM;

    COMMIT;

    DELETE FROM RMS13PRD.ORD_TEMP OT
     WHERE OT.LOCATION = REC_1.LOCATION
       AND OT.ITEM = REC_1.ITEM;

    COMMIT;

    END IF;

    END LOOP;

    EXCEPTION
       WHEN OTHERS THEN
       ROLLBACK;
       O_error_message := L_program||'-'||SQLCODE||SQLERRM;

    END APO_RMS_E_PRIO_ITEM_REPL_P;

Ответы [ 2 ]

0 голосов
/ 28 сентября 2018

Когда мне нужно найти, какая часть процедуры занимает (слишком много) времени для выполнения, я использую простую технику регистрации: таблица, содержащая информацию о , где я и , что яЯ делаю вместе с отметкой времени.Он заполняется с помощью процедуры AUTONOMOUS_TRANSACTION (чтобы я мог зафиксировать информацию журнала, не влияя на вызывающего).

Вот скрипт (не стесняйтесь его изменять):

CREATE TABLE a1_log
(
   id      NUMBER,
   datum   DATE,
   descr   VARCHAR2 (500)
);

CREATE SEQUENCE seqlognap START WITH 1 INCREMENT BY 1;

CREATE OR REPLACE PROCEDURE a1_p_log (par_descr IN VARCHAR2)
IS
   PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
   INSERT INTO a1_log
      SELECT seqlognap.NEXTVAL, SYSDATE, par_descr FROM DUAL;

   COMMIT;
END a1_p_log;

Затем в процедуре, над которой я работаю, поместите вызовы a1_p_log, такие как

begin
  a1_p_log('step 1: initializing');
  -- some init code here

  a1_p_log('step 2: an ugly query follows now');
  SELECT blabla --> very ugly query

  a1_p_log('step 3: updating xyz table');
  UPDATE xyz set ...

  a1_p_log('step 4: finish');
end;

Эта процедура может занять некоторое время (минуты, часы ...), но я могу запросить a1_log таблица в любое время:

  SELECT id,
         datum,
         LEAD (datum) OVER (ORDER BY id) datum_next,
         (LEAD (datum) OVER (ORDER BY id) - datum) * 24 * 60 * 60 diff_sec,
         descr
    FROM a1_log
ORDER BY id;

Обратите внимание на значение DIFF_SEC, которое показывает количество секунд между двумя шагами.Если вы заметили большое число здесь, вы должны попытаться оптимизировать этот запрос, чтобы он работал быстрее.


Что касается вашего кода: я слишком сложен, чтобы что-то сказать умный, кроме: вы, вероятно, захотите удалить все эти COMMIT вызовы из цикла.Используйте один COMMIT, в конце процедуры.

0 голосов
/ 28 сентября 2018

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

Тогда это будет указывать на то, какие запросы в вашей процедуре должны быть оптимизированы, и соответствующий администратор базы данных будет иметь некоторые подсказки об этом.

Здесь нам иногда нужно использовать отчетысгенерировано Automatic Workload Repository (AWR) из Oracle, которое предоставляет вам обширную статистику о том, что происходит в вашей базе данных.Поэтому, если вам удастся остановить всю остальную обработку во время фазы тестирования, у вас будет четкое представление о том, что происходит внутри вашей процедуры, без излишней работы для вас.

(обратите внимание, что я не связан с Oracle в любом«корпоративный» путь).

...