Почему этот запрос занимает много времени? - PullRequest
0 голосов
/ 03 января 2019

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

SELECT GET_DAYSNS_SF(CONTRACT_ID) 
FROM   CONTRACT_TEMP;

и функция ...

CREATE OR REPLACE FUNCTION GET_DAYSNS_SF
    (CONTRACT_ID IN NUMBER)
    RETURN DATE
AS
    CONTRACT_ID1 NUMBER(16) := CONTRACT_ID;
    DAYSNS DATE;

BEGIN
  BEGIN
    SELECT MIN(EXPECTED_DT)
    INTO DAYSNS
    FROM FLOW
    WHERE REVERSAL_STATUS = 4200
    AND FLOW_TYPE IN(1003,1006,1027)
    AND IS_CASH = 1
    AND AMOUNT > 0
    AND AMOUNT > NVL(AMT_MATCHED,0)
    AND CONTRACT_ID = CONTRACT_ID1;
 EXCEPTION 
    WHEN NO_DATA_FOUND THEN
       DAYSNS := NULL;
  END;
  RETURN DAYSNS;
END;

Ответы [ 2 ]

0 голосов
/ 03 января 2019

Тривиальный ответ (весьма вероятное правильное предположение) - это отсутствующий индекс для FLOW(CONTRACT_ID).

Это приводит к FULL TABLE SCAN при каждом вызове функции.

Обратите внимание, что в случае, если столбец CONTRACT_ID не очень избирателен, вам может потребоваться добавить в функцию какой-либо другой столбец из предиката, используемого в запросе (например, REVERSAL_STATUS).

Но вы можете сделать еще лучше - вам нужно только оставить позади курсора PL / SQL строка за строкой logik и ввести подход SQL .

Это решит две дополнительные проблемы , которые остаются после добавления индекса:

  • Контекст PL / SQL, который обходится дорого в случае большого количества вызовов функций

  • Вы реализовали ВНЕШНЕЕ СОЕДИНЕНИЕ с вопросом DIY, что намного медленнее, чем в Oracle.

Какой вам нужен идентификатор, чтобы получить для каждого contract_id из CONTRACT_TEMP минимального EXPECTED_DT из FLOW, если существует, отфильтрованного с помощью предиката WHERE в функции .

Это можно сделать в два этапа:

1) Предварительный расчет для каждого CONTRACT_ID минимального EXPECTED_DT - см. Подзапрос ниже. Обратите внимание, что с помощью GROUP BY вы рассчитываете результат для каждого контракта за один шаг.

2) LEFT OUTER JOIN таблица CONTRACT_TEMP с результатом 1)

Запрос - это эквивалентно вашему первоначальному запросу:

SELECT   f.EXPECTED_DT 
FROM   CONTRACT_TEMP c
LEFT OUTER JOIN 
   (SELECT CONTRACT_ID, MIN(EXPECTED_DT) EXPECTED_DT
    FROM FLOW
    WHERE REVERSAL_STATUS = 4200
    AND FLOW_TYPE IN(1003,1006,1027)
    AND IS_CASH = 1
    AND AMOUNT > 0
    AND AMOUNT > NVL(AMT_MATCHED,0)
    GROUP BY CONTRACT_ID) f
on c.CONTRACT_ID = f.CONTRACT_ID
0 голосов
/ 03 января 2019

Вы можете удалить условие AND AMOUNT > 0, поскольку в запросе уже есть еще одно подобное условие AMOUNT > NVL(AMT_MATCHED,0)

...