Подтвердить что ты не робот

Оптимальный способ УДАЛИТЬ указанные строки из Oracle

У меня есть проект, который иногда должен удалять несколько десятков тысяч строк из одной из шести таблиц разного размера, но между ними есть около 30 миллионов строк. Из-за структуры данных, которые мне были предоставлены, я не знаю, какая из шести таблиц содержит строку, которая должна быть удалена в ней, поэтому я должен запускать все удаления по всем таблицам. Я построил индекс INDEX против столбца ID, чтобы попытаться ускорить процесс, но его можно удалить, если это ускорит процесс.

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

7384 отдельных DELETE операторов заняли 203 секунды:

delete from TABLE1 where ID=1000001356443294;
delete from TABLE1 where ID=1000001356443296;
etc...

7384 отдельных SELECT операторов заняло 57 секунд:

select ID from TABLE1 where ID=1000001356443294
select ID from TABLE1 where ID=1000001356443296
etc...

7384 отдельных DELETE from (SELECT) операторов заняло 214 секунд:

delete from (select ID from TABLE1 where ID=1000001356443294);
delete from (select ID from TABLE1 where ID=1000001356443296);
etc...

1 SELECT, который содержит предложения 7384 OR, в которых принимал 127.4s:

select ID from TABLE1 where ID=1000001356443294 or ID = 1000001356443296 or ...

1 DELETE from (SELECT), в котором содержится 7384 OR предложений, в которых принималось 74.4s:

delete from (select ID from TABLE1 where ID=1000001356443294 or ID = 1000001356443296 or ...)

В то время как последний может быть самым быстрым, при дальнейшем тестировании его все еще очень медленно при масштабировании от таблицы строк 9000 до даже всего 200 000 строк строк (которые все еще составляют 1% от конечного размера табличного пакета), где тот же самый оператор выполняет 14 минут. В то время как > на 50% быстрее в строке, которая по-прежнему экстраполируется до примерно одного дня при запуске против полного набора данных. Я уверен, что часть программного обеспечения, которое мы использовали для выполнения этой задачи, может сделать это примерно за 20 минут.

Итак, мои вопросы:

  • Есть ли лучший способ удалить?
  • Должен ли я использовать раунд операторов SELECT (т.е., как и второй тест), чтобы узнать, в какой таблице находится какая-либо данная строка, а затем снимать запросы на удаление? Даже это выглядит довольно медленно, но...
  • Есть ли что-нибудь еще, чтобы ускорить удаление? У меня нет доступа или знаний на уровне DBA.
4b9b3361

Ответ 1

Прежде чем отвечать на мои вопросы, вот как я это сделаю:

Свести к минимуму количество заявлений и работу, которую они выдают в относительных терминах.

Все сценарии предполагают, что у вас есть таблица идентификаторов (PURGE_IDS) для удаления из TABLE_1, TABLE_2 и т.д.

Рассмотрите возможность использования CREATE TABLE AS SELECT для действительно больших удалений

Если нет одновременной активности, и вы удаляете 30% строк в одной или нескольких таблицах, не удаляйте; выполните create table as select с строками, которые вы хотите сохранить, и замените новую таблицу на старую таблицу. INSERT /*+ APPEND */ ... NOLOGGING удивительно дешево, если вы можете себе это позволить. Даже если у вас есть несколько одновременных действий, вы можете использовать переопределение Online Table для восстановления таблицы на месте.

Не запускать операторы DELETE, которые, как вы знаете, не будут удалять строки

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

CREATE TABLE TABLE1_PURGE NOLOGGING
AS 
SELECT ID FROM PURGE_IDS INNER JOIN TABLE_1 ON PURGE_IDS.ID = TABLE_1.ID;

DELETE FROM TABLE1 WHERE ID IN (SELECT ID FROM TABLE1_PURGE);

DELETE FROM PURGE_IDS WHERE ID IN (SELECT ID FROM TABLE1_PURGE);

DROP TABLE TABLE1_PURGE;

и повторите.

Управляйте Concurrency, если вам нужно

Другой способ - использовать PL/SQL-цикл для таблиц, выдавая ограничение на удаление строки. Это, скорее всего, подходит, если существует значительная вставка/обновление/удаление параллельной загрузки для таблиц, на которых выполняется удаление.

declare
  l_sql varchar2(4000);
begin
  for i in (select table_name from all_tables 
             where table_name in ('TABLE_1', 'TABLE_2', ...)
             order by table_name);
  loop
    l_sql := 'delete from ' || i.table_name || 
             ' where id in (select id from purge_ids) ' || 
             '   and rownum <= 1000000';
    loop
      commit;
      execute immediate l_sql;
      exit when sql%rowcount <> 1000000;  -- if we delete less than 1,000,000
    end loop;                             -- no more rows need to be deleted!
  end loop;
  commit;
end;

Ответ 2

Сохраните все удаляемые идентификаторы в таблицу. Тогда есть 3 способа. 1) проведите весь идентификатор в таблице, затем удалите одну строку за раз для интервала фиксации X. X может быть 100 или 1000. Он работает в среде OLTP, и вы можете управлять замками.

2) Используйте Oracle Bulk Delete

3) Используйте запрос коррелированного удаления.

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

Ответ 3

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

Попробуйте выполнить оператор MERGE INTO:
1) создать временную таблицу с идентификаторами и дополнительный столбец из таблицы 1 и протестировать со следующими

MERGE INTO table1 src
USING (SELECT id,col1
         FROM test_merge_delete) tgt
ON (src.id = tgt.id)
WHEN MATCHED THEN
  UPDATE
     SET src.col1 = tgt.col1
  DELETE
   WHERE src.id = tgt.id

Ответ 4

Я пробовал этот код, и он отлично работает в моем случае.

DELETE FROM NG_USR_0_CLIENT_GRID_NEW WHERE rowid IN
( SELECT rowid FROM
  (
      SELECT wi_name, relationship, ROW_NUMBER() OVER (ORDER BY rowid DESC) RN
      FROM NG_USR_0_CLIENT_GRID_NEW
      WHERE wi_name = 'NB-0000001385-Process'
  )
  WHERE RN=2
);