Хранимая процедура у меня работает очень медленно / никогда не заканчивается.Есть лучший способ сделать это? - PullRequest
0 голосов
/ 06 декабря 2018

Итак, что я по сути пытаюсь сделать, это получить данные, помеченные в value_flag (что будет включать в себя объединение многочисленных таблиц), а затем манипулировать / обновлять эти записи в этих таблицах (в основном, в таблице 'VALUE') на основе некоторых бизнес-правил./ логика, которая у меня есть.Я придумал некоторый код (который должен работать), который показан ниже, но на его запуск уходит много часов.Есть ли более эффективный способ сделать это?Мой код выглядит следующим образом:

create or replace procedure IMP_JOB IS
CURSOR c1 is
select v.value_id
     , vf.VALUE_FLAG_ID
     , sv.sub_value_id    
     , ff.form_Field_tx
     , v.hr_num
     , v.data_date
     , v.code
from sub s
join sub_value sv on s.sub_id = sv.sub_id
join value v on sv.value_id = v.value_id
join field f on sv.Field_id = ff.form_Field_id
join line l on ff.line_id = fl.form_line_id
join section s on fl.section_id = fs.form_section_id
join flagrel vf on sv.sub_value_id = vf.sub_value_id
join flag f on vf.value_flag_type_id = f.flag_id
where
(
((to_date(LPAD(V.DATA_DATE, 7, 0), 'DDDYYYY') = trunc(sysdate)) AND     fl.form_line_label_tx in ('Same Day', 'Same-Day'))
OR
(l.line_label_tx in ('V2', 'Daily'))
)
AND 
(
(flag_tx IN ('??', 'N'))
OR
v.value_nb is null
);
l_var c1%ROWTYPE;
v_value_id value.value_id%type;
v_calc_id calculation.calculation_id%type;
v_forecast_hr value.value_nb%type;
v_prior_hour_value value.value_nb%type;
v_prior_day_value value.value_nb%type;
--
BEGIN
--
OPEN c1;
LOOP 
--
        FETCH c1 into l_var;
          --
          SELECT GET_DA_VALUE(l_var.hr_num, l_var.Data_date, l_var.code)
          INTO v_forecast_hr
          FROM DUAL;
          --
      SELECT GET_PRIOR_HOUR_VALUE(l_var.hr_num, l_var.data_Date, l_var.code, l_var.form_Field_tx)
      INTO v_prior_hour_value
      FROM DUAL;
      --
      SELECT GET_PRIOR_DAY_VALUE(l_var.hr_num, l_var.data_Date, l_Var.code, l_var.form_field_Tx)
      INTO v_prior_day_value
      FROM DUAL;
      --
      --IF1
      IF l_var.form_field_tx = 'X'
      THEN
          --IF1A
          IF v_forecast_hr IS NOT NULL
          THEN
              Update Value
              SET Value_tx = v_forecast_hr 
              WHERE value_id = l_var.value_id;
              COMMIT;
              --
              Update Value_flag
              SET Value_Flag_Type_Id = 2
              WHERE sub_value_id = l_var.sub_value_id;                  
              COMMIT;
          --IF1A Continued
          ELSE
              --IF1A1
              IF v_prior_hour_value IS NULL
              THEN
                   --IF1A2
                  IF v_prior_day_value IS NULL
                  THEN
                      Update Value
                         SET Value_Tx = null
                        WHERE value_id = l_var.value_id;
                       COMMIT;
                 --
                    Update Value_flag
                       SET Value_Flag_Type_Id = 5
                     WHERE submission_value_id = l_var.sub_value_id;                  
                    COMMIT;
                  --IF1A2 Continued
                  ELSE
                    Update Value
                       SET Value_Tx = v_prior_day_value
                     WHERE value_id = l_var.value_id;
                    COMMIT;
                  --
                    Update Value_flag
                       SET Value_Flag_Type_Id = 7
                     WHERE subm_value_id = l_var.sub_value_id;                  
                    COMMIT;
                  --IF1A2 End
                  END IF;
              --IF1A1 Continued  
              ELSE
              Update Value
                 SET Value_Tx = v_prior_hour_value
               WHERE value_id = l_var.value_id;
               COMMIT;
              --
              Update Value_flag
                 SET Value_Flag_Type_Id = 9
               WHERE sub_value_id = l_var.sub_value_id;                  
              COMMIT;
              --IF1A1 End
              END IF;
          --IF1A End
          END IF;
      --IF1 Continued
      ELSIF l_var.form_field_tx = 'TX'
      THEN
          --IF1B
          IF v_prior_hour_value IS NULL
          THEN
              --IF1B1
                  IF v_prior_day_value IS NULL
                  THEN
                      Update Value
                         SET Value_Tx = null
                       WHERE value_id = l_var.value_id;
                      COMMIT;
                 --
                    Update Value_flag
                       SET Value_Flag_Type_Id = 02
                     WHERE sub_value_id = l_var.sub_value_id;                  
                    COMMIT;
              --IF1B1 Continued    
                  ELSE
                    Update Value
                       SET Value_Tx = v_prior_day_value
                     WHERE value_id = l_var.value_id;
                    COMMIT;
                  --
                    Update Value_flag
                       SET Value_Flag_Type_Id = 9
                     WHERE sub_value_id = l_var.sub_value_id;                  
                    COMMIT;
              --IF1B1 End
                  END IF;
          --IF1B Continued
          ELSE
              Update Value
                 SET Value_Tx = v_prior_hour_value
               WHERE value_id = l_var.value_id;
               COMMIT;
              --
              Update Value_flag
                 SET Value_Flag_Type_Id = 890
               WHERE sub_value_id = l_var.sub_value_id;                  
              COMMIT;          
          --IF1B End
          END IF;                
      --IF1 Continued
      ELSE
      null;
      --IF1 End
      END IF;
END LOOP;
CLOSE C1;
END IMP_JOB;

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

То, что вы здесь делаете, это циклический просмотр набора результатов и обработка его построчно.Это не называется "медленно-медленно" даром.SQL является ориентированным на множество языком и наиболее эффективен при работе на множествах.

  1. Ваша процедура просто обновляет value и value_flag, поэтому лучше всего переписать это, чтобы превратить его в два оператора обновления. Это лучшее, что вы можете сделать для улучшения производительности.
  2. В вашем цикле есть случаи, когда form_field_tx равен X или TX и достигает ELSE NULL во всех других случаях.Поскольку вас не волнуют строки, не являющиеся X или TX, укажите это в предложении WHERE вашего запроса, чтобы вам не приходилось обрабатывать эти строки в первую очередь.
  3. Для вашего V.DATA_DATEПожалуйста, скажите, пожалуйста, что вы не храните даты как VARCHAR2s, потому что это просто плохо.Если это так, вы можете переписать эту строку как v.data_date = TO_CHAR( SYSDATE, 'DDDYYYY' ), чтобы не нужно было запускать TO_DATE для каждой строки в вашем наборе результатов.
  4. Вы могли бы иметь возможность получить некоторое улучшение производительности, если вы объявили массив вашего l_var и использовали BULK COLLECT для обработки этого набора результатов в виде фрагментов.См. this для примера того, что я имею в виду. Но вы, вероятно, не добьетесь значительного улучшения производительности, поскольку PL / SQL делает это автоматически за кулисами.Я думаю, что размер выборки составляет 100 строк, но я не уверен.

Редактировать: Таким образом, чтобы преобразовать это в набор из двух запросов на обновление, вы можете сделать что-то вродеэто:

UPDATE value v
   SET value_tx = ( SELECT ... FROM ... WHERE ... = v.value_id )
 WHERE v.value_id IN ( SELECT ... FROM ... WHERE ... )

Я буду честен, у вас есть 10 разных случаев в трех вложенных утверждениях IF, и я не пытался следовать всем этим.Вы хотите разбить курсор c1 на две части и использовать их в SELECTs выше.

Первый подзапрос должен вернуть новое значение value_tx для данного v.value_id.Этот подзапрос должен возвращать одну строку для заданного значения value_id.Вероятно, у него будет довольно сложный оператор CASE, поскольку у вас так много разных возможностей.Например:

SELECT CASE WHEN ( form_field_tx = 'X' ) THEN
           COALESCE( GET_DA_VALUE(hr_num, Data_date, code),
           ... END

Второй подзапрос должен вернуть только value_ids строк в value, которые вы хотите обновить.Это должно быть намного проще и, вероятно, выглядело бы очень похоже на ваш запрос c1 с более коротким списком столбцов.

0 голосов
/ 06 декабря 2018

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

Вероятно, не влияет на производительность, потому что Oracle обрабатывает DUAL очень эффективно, но присваивает значение, а не использует select в.Это поможет с удобочитаемостью:

        v_forecast_hr   := get_da_value( l_var.hr_num, l_var.data_date, l_var.code );

    v_prior_hour_value   :=
        get_prior_hour_value( l_var.hr_num
                            , l_var.data_date
                            , l_var.code
                            , l_var.form_field_tx );

    v_prior_day_value   :=
        get_prior_day_value( l_var.hr_num
                           , l_var.data_date
                           , l_var.code
                           , l_var.form_field_tx );

Читаемость очень важна для устранения неисправностей.Ваш курсор , где предложение - беспорядок.Мне интересно, если вы обрабатываете больше записей, чем вы ожидали.Вы можете несколько улучшить читаемость, избавившись от лишних скобок, например, вокруг ваших поднаборов 'IN':

(flag_tx IN ('??', 'N'))

становится

flag_tx IN ('??', 'N')

Возможно, вы также захотите отформатировать свой код.Первым делом я бросил его в TOAD и запустил средство форматирования PL / SQL.Я сразу обнаружил, что вы пропустили точку с запятой после курсора, которая говорит мне, что код, который вы вставили здесь, не является реальным кодом, с которым вы работаете.Как показано здесь, это не скомпилируется.

Есть ли какая-то причина, которую вы фиксируете после каждого оператора обновления?Это не SQL Server, где блокировка является проблемой.Один коммит непосредственно перед

end loop;

- это все, что нужно.Если бы не тот факт, что это занимает несколько часов, я бы поместил один коммит за пределы цикла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...