Обновление оператора занимает более 24 часов для обновления 32 тыс. Строк - PullRequest
0 голосов
/ 15 мая 2018

Ниже приведено утверждение об обновлении, которое я выполняю 32 раза, и оно занимает более 15 часов.Мне нужно обновить значение в таблице 2 для 32k различных ЗНАЧЕНИЙ M_DISPLAY.

UPDATE TABLE_2 T2  SET  T2.M_VALUE = 'COL_ANC' 
WHERE EXISTS (SELECT 1 FROM TABLE_2 T1  WHERE  TRIM(T1.M_DISPLAY) = 'ANCHORTST' AND T1.M_LABEL=T2.M_LABEL );

Не уверен, почему это занимает так много времени, так как я настроил запрос,

У меня естьскопировал 32000 операторов обновления в файл Update.sql и запустил SQL в командной строке.Хотя это обновление таблицы, это бесконечный процесс

Пожалуйста, сообщите, если я где-то ошибся

С уважением

Ответы [ 3 ]

0 голосов
/ 15 мая 2018

Использование FORALL

Если вы не можете переписать запрос для запуска одного массового обновления вместо 32k отдельных обновлений, вам все равно может повезти, если использовать PL / SQL FORALL.Пример:

DECLARE
  TYPE rec_t IS RECORD (
    m_value   table_2.m_value%TYPE,
    m_display table_2.m_display%TYPE
  );

  TYPE tab_t IS TABLE OF rec_t;

  data tab_t := tab_t();
BEGIN

  -- Fill in data object. Replace this by whatever your logic for matching
  -- m_value to m_display is
  data.extend(1);
  data(1).m_value := 'COL_ANC';
  data(1).m_display := 'ANCHORTST';

  -- Then, run the 32k updates using FORALL
  FORALL i IN 1 .. data.COUNT
    UPDATE table_2 t2
    SET t2.m_value = data(i).m_value
    WHERE EXISTS (
      SELECT 1
      FROM table_2 t1
      WHERE trim(t1.m_display) = data(i).m_display
      AND t1.m_label = t2.m_label
    );
END;
/

Параллелизм

Если вы не единственный процесс в системе, обновления 32 КБ в одной транзакции могут повредить.Определенно, стоит выделить несколько тысяч строк в суб-транзакциях, чтобы уменьшить эффекты параллелизма с другими процессами, которые могут читать ту же таблицу во время обновления.

Массовое обновление

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

Если у вас была промежуточная таблица, содержащая инструкции по обновлению:

CREATE TABLE update_instructions (
  m_value VARCHAR2(..),
  m_display VARCHAR2(..)
);

Тогда вы можете выполнить что-то вроде:

MERGE INTO table_2 t2
USING (
  SELECT u.*, t1.m_label
  FROM update_instructions u
  JOIN table_2 t1 ON trim(t1.m_display) = u.m_display
) t1
ON t2.m_label = t1.m_label
WHEN MATCHED THEN UPDATE SET t2.m_value = t1.m_value;

Это должно быть даже быстрее, чем FORALL (но может иметь больше последствий для параллелизма).

Индексация и очистка данных

Конечно, одна вещь, которая может определенно ранить вас при выполнении 32k отдельных операторов обновления, - это функция TRIM(), которая не позволяет эффективно использовать индекс для M_DISPLAY.Если бы вы могли санировать свои данные, чтобы вначале не нужно было их обрезать, это определенно помогло бы.В противном случае вы можете добавить индекс на основе функций только для обновления (а затем снова удалить его):

CREATE INDEX i ON table_2 (trim (m_display));
0 голосов
/ 15 мая 2018

Для значительно более быстрого выполнения:

  1. Убедитесь, что вы создали индекс для столбцов M_DISPLAY и M_LABEL, которые содержатся в предложении WHERE.

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

Вот так.

Кстати, как уже упоминалось, вам не нужно 32 тыс. Запросов для достижения вашей цели. Один, вероятно, будет достаточно. Посмотрите на запрос на основе обновления. В качестве примера посмотрите принятый ответ здесь: Oracle SQL: обновление таблицы данными из другой таблицы

0 голосов
/ 15 мая 2018

Запрос и подзапрос запрашивают одну и ту же таблицу: TABLE_2.Предполагая, что M_LABEL уникален, подзапрос возвращает 1 для всех строк в TABLE_2, где M_DISPLAY равен ANCHORTST.Затем запрос на обновление обновляет одну и ту же (!) Таблицу TABLE_2 для всех 1, возвращаемых из подзапроса, поэтому для всех строк, где M_DISPLAY равен ANCHORTST.

Следовательно, запрос можно упростить, используя тот факт, что и обновление, и выбор работаютна той же таблице - TABLE_2:

UPDATE TABLE_2 T2  SET  T2.M_VALUE = 'COL_ANC' WHERE TRIM(T2.M_DISPLAY) = 'ANCHORTST'

Если M_LABEL не уникален, то вышеописанное не сработает - спасибо комментаторам за указание на это!

...