2015-09-12 4 views
0

У меня есть сценарий, который занимает много времени, чтобы обновить 60 миллионов записей. Вот запрос:commit для любой записи в oracle sql

UPDATE baseinformation w 
SET good_item_id = (SELECT id FROM item e WHERE e.id = w.id) 

Моя проблема: Я хочу зафиксировать любую запись, когда этот запрос запущен. Я хотел бы зафиксировать любые измененные записи в SQL с другого сеанса. Моя проблема решена в PLSQL на LOOP. Есть ли решение или команда в чистом Oracle SQL для моей цели?

+0

Как долго? У вас есть индекс в обеих таблицах 'id'? –

+0

Вам определенно нужны индексы для item.id и baseinformation.good_item_id для вышеупомянутого, чтобы иметь достойную производительность. – John

+0

PLSQL здесь не правильный выбор. Когда вы сможете сделать это в одном SQL, вы должны сделать это в одном SQL. Правильное решение - понять, почему SQL работает медленно и настраивает его. Являются ли статистика на обеих таблицах актуальной? Есть ли спорные вопросы?Любые индексы, триггеры в таблице 'baseinformation', которые могут замедлить обновление и т. Д., – toddlermenot

ответ

-2

Причина, по которой требуется время, может быть указателем на столбцы (столбцы), которые будут потреблять больше времени. Если вы попытаетесь удалить индексы и выполнить, это может сработать. Также, как только вы начнете выполнять блокировку записи, вы будете удерживать ее до сеанса до фиксации. Таким образом, вы не можете выполнить оператор в другом сеансе.

0

Вы должны понимать, что оператор update, который вы используете, является атомарным. Это может быть только commit ted после завершения операции в целом, т. Е. Обновление таблицы baseinformation в вашем случае. A commit не может быть выпущен для каждой обновленной записи. Это необходимо для обеспечения согласованности в базе данных, чтобы другие пользователи не видели плохие записи при выполнении команды update. Если во время процесса возникает ошибка, транзакция откатывается.

0

Вы должны быть в состоянии сделать это, добавив в sql условие ограничения. Запрос должен возвращать количество обновленных строк. Настройка времени цикла в коде вызова и вызов запроса с предельным пунктом, пока она не возвращает 0. Что-то вроде этого (это с верхней частью моей головы и не может быть точным синтаксисом для Oracle)

update baseinformation w 
set good_item_id = 
    (select id from item e where e.id = w.id 
    and e.id != w.id 
    and ROWNUM <= 1000); 
+0

Два предложения 'WHERE' в одном выражении SELECT недействительны. –

-1

Вам действительно нужно обновлять каждую запись каждый раз? Вы могли бы сделать что-то вроде этого:

update baseinformation w 
set good_item_id = (select id from item e where e.id = w.id 
and e.id != w.id) 
+0

Этот SQL недействителен (как вы получаете доступ к псевдониму «e» вне подзапроса?). – toddlermenot

3

Несколько из нас советовали вам in your previous question, что вы должны попытаться решить проблему производительности путем настройки оператора SQL, а не стремясь подорвать целостность транзакции. Теперь вы разместили обновление, которое мы можем вам помочь.

Дело в UPDATE заключается в том, что это всего лишь вариант SELECT: база данных должна найти строки, прежде чем она сможет их изменить. Поэтому мы часто можем сократить время выполнения, настроив путь доступа. Например, мы можем запустить EXPLAIN PLAN для операторов обновления:

explain plan for 
    UPDATE baseinformation w 
    SET good_item_id = (SELECT id FROM item e WHERE e.id = w.id) 
/

без доступа к вашему Explain Plan, статистике и данные перекосы, трудно предложить жесткий совет. На лицевой стороне вашего заявления будет выполнено одно из следующих сообщений: item для каждая строка в baseinformation. Даже если доступ является INDEX UNIQUE SCAN, 60 миллионов из них много читаются. Если есть строки в baseinformation, которые не имеют соответствия id в item, он становится еще дороже. Как отметил @DavidAldridge, база данных все же может реализовать более эффективный путь, но если ваше заявление работает в течение трех дней, похоже, оно не приняло правильного решения.

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

Другое дело - преобразовать эти индивидуальные запросы в заданную операцию, что должно обеспечить более эффективный путь доступа.Ваш SQL как представленные обновления baseinformation.good_item_id до item.id если item.id соответствует baseinformation.id. Таким образом, это утверждение логически эквивалентно ...

update baseinformation w 
set w.good_item_id = w.id 
where w.id in (select e.id from item e) 
/

... как это один ...

update baseinformation w 
set w.good_item_id = w.id 
where exists (select e.id from item e where e.id = w.id) 
/

Если baseinformation имеет огромное количество строк и item имеет крошечное количество совпадающих строк то более предпочтительным является формулировка WHERE ... IN. Если baseinformation является крошечным и item огромен, тогда ГДЕ СУЩЕСТВУЕТ ... формулировка, вероятно, будет работать лучше. Если обе таблицы примерно эквивалентны в количестве строк, чем любой подход может быть лучше, в зависимости от индексов, кластеризации и т. Д., Поэтому вам нужно будет сравнить оба.

Другим подходом было бы выполнить объединение двух таблиц. Вы можете сделать это с MERGE заявлением ...

merge into baseinformation w 
    using (select id from item) e 
    on (e.id = w.id) 
when matched then 
    update 
    set w.good_item_id = e.id 
/

... или с обновляемым видом на линии ...

update (
     select 
      e.id as item_id 
      , w.good_item_id 
     from baseinformation w 
      join item e 
      on (e.id = w.id) 
) 
set good_item_id = item_id 
/

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


На самом деле ответ на свой вопрос :)

"есть решение или команды в чистом Oracle SQL"

No. заявление SQL является атомным. Невозможно зафиксировать прирост инструкции UPDATE. Вы можете ограничить UPDATE искусственным подмножеством строк, например.

update baseinformation w 
set w.good_item_id = w.id 
where w.id in (select e.id from item e) 
and w.good_item_id is null 
and rownum <= 20000 
/

Мы могли бы использовать такой оператор в блоке PL/SQL для обновления и фиксации кусков в двадцать тысяч строк. Однако это приведет к более длительному истекшему времени, потому что вы будете постоянно запускать ПОЛНЫЙ ТАБЛИЦЫ СКАНИРОВАНИЯ baseinformation. Кроме того, этот подход вызывает проблемы с последовательностью транзакций: любой запрос сеанса baseinformation на основе good_item_id получит потенциально обманчивый набор результатов.

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

Что касается совершения с другой сессии, это невозможно. Oracle накладывает уровень изоляции Read Committed. Это означает, что сеансы могут видеть только измененные записи после того, как изменения сессии совершили изменения. В Oracle нет грязных чтений. Это означает, что один сеанс не может фиксировать изменения в другом сеансе. Find out more.

+0

"ваш оператор будет выполнять одно чтение по элементу для каждой строки в baseinformation" - вы уверены? Он может быть преобразован в внешнее соединение и реализован как хэш-соединение. –

+0

@DavidAldridge - вы правы, я не уверен. В предыдущем вопросе OP говорилось, что запрос выполнялся в течение трех дней, из чего я догадался, что у них очень субоптимальный путь доступа. Я отредактирую свой ответ, чтобы уточнить это. – APC

Смежные вопросы