Почему данный запрос зависает или замедляется? - PullRequest
1 голос
/ 17 июня 2019

Обычно это выполняется нормально, но иногда один или два раза в неделю это зависало или замедлялось в Oracle.

Есть ли лучший способ оптимизировать его?

FOR i IN ( SELECT * FROM process_data) LOOP
    BEGIN
        SELECT employee_plan_id
          INTO lc_emp_plan_id
          FROM employee
         WHERE employeeid = i.emp_id
           AND join_year = (
            SELECT join_year
              FROM employeedata
             WHERE employeeid = i.emp_id
               AND i.joining_date BETWEEN join_date AND termination_date
        );
    END;
    SELECT employee_plan_type
      INTO lc_emp_type
      FROM employee_plans
     WHERE employee_plan_id = lc_emp_plan_id;

    -- Mark failed record if emp_lastname is null
    UPDATE process_data
       SET
        is_failure = 1
     WHERE emp_lastname IS NULL
       AND emp_plan_type = lc_emp_type;
END LOOP;

Помните

SELECT join_year
  FROM employeedata
 WHERE employeeid = i.emp_id
   AND i.joining_date BETWEEN joining_date AND termination_date;

Он всегда вернет 1 запись, и это доказано.

Здесь lc_emp_plan_id - это переменная, и цикл for выполняется внутри процедуры?

1 Ответ

2 голосов
/ 17 июня 2019

Наиболее естественным объяснением наблюдаемого поведения является то, что количество обрабатываемых строк (process_data) варьируется. Общее прошедшее время линейно пропорционально числу обработанных строк, поэтому в дни с большим количеством строк цикл "зависает".

Лучший способ ускорить работу - не использовать FOR LOOP в PL / SQL.

Просто переформулируйте его в операторе SQL (это не всегда возможно, иногда приводит к сложному SQL, но может привести к резкому ускорению.

В вашем случае это должно быть довольно простое упражнение:

Этот запрос возвращает тот же результат, что и ваш первый цикл.

SELECT e.employee_plan_id
  FROM process_data p
  JOIN employee e ON p.emp_id = e.employeeid
  JOIN employeedata ed ON p.emp_id = ed.employeeid
   AND p.joining_date BETWEEN ed.join_date AND ed.termination_date;

Соответственно, вы можете переписать всю процедуру одним оператором UPDATE.

UPDATE process_data
   SET is_failure = 1
 WHERE emp_lastname IS NULL
   AND emp_plan_type IN (
    SELECT employee_plan_type
      FROM employee_plans
     WHERE employee_plan_id IN (
        /* copy the query above here */
    )
);
...