Упрощение удаления данных из нескольких таблиц динамически с использованием pl / sql - PullRequest
1 голос
/ 03 ноября 2011

Мне нужно написать процедуру оракула pl / sql для удаления данных из нескольких таблиц. Критерии выбора данные для удаления различны для каждой таблицы.

Для простоты, если у меня есть 3 таблицы, для каждой таблицы будет один запрос на выборку. т.е.

select id from table_a where product_type='A';
select name from table_b where category='student';
select phone_name from table_c where name='nokia';

Пара правил

- I can only delete 1000 records at a time for each table. 
- I should issue a commit after every 1000 records
- Each row is processed first before deleting (in all tables)

Самое простое решение - иметь что-то вроде этого

commit_limit:=1000;

counter:=0;
recordsDeleted:=0;
For i in (select rowid,id from table_a where product_type='A') loop

    Delete from table_a where rowid=i.rowid;
    recordsDeleted:=SQL%rowcount;
    counter:=counter++;

    if(counter>=commit_limit) then
        commit;
        counter:=0;
        >log to file that commit has been issued. 
    end if;
End loop;

counter:=0;
recordsDeleted:=0;
For i in (select rowid,name from table_b where category='student') loop

    Delete from table_b where rowid=i.rowid;
    recordsDeleted:=SQL%rowcount;
    counter:=counter++;

    if(counter>=commit_limit) then
        commit;
        counter:=0;
        >log to file that commit has been issued. 
    end if;
End loop;

counter:=0;
recordsDeleted:=0;
For i in (select rowid,phone_name from table_c where name='nokia') loop

    Delete from table_c where rowid=i.rowid;
    recordsDeleted:=SQL%rowcount;
    counter:=counter++;

    if(counter>=commit_limit) then
        commit;
        counter:=0;
        >log to file that commit has been issued and log value of 'recordsDeleted'. 
    end if;
End loop;

> log to file total records deleted. 

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

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

function delete_by_colid (table_name in varchar(35), column_name in varchar(150), select_criteria in varchar(200)) return number
Is
begin
    counter:=0;
    recordsDeleted:=0;

    For i in (<<<select_criteria>>>) loop

        Delete from <<<table_name>>> where <<<column_name>>>=i.rowid;
        recordsDeleted:=SQL%rowcount;
        counter:=counter++;

        if(counter>=commit_limit) then
            commit;
            counter:=0;
            >log to file that commit has been issued and log value of 'recordsDeleted'. 
        end if;
    End loop;
    return recordsDeleted;
End;

Проблема, с которой я столкнулся, не уверена в следующем

  • Как включить select_criteria в цикл for? Будет ли работать явный курсор вместо этого? Как бы я определил это динамически?
  • Как мне решить, какие размеры подходят для table_name и особенно для select_criteria, когда они передаются в функцию?
  • Если я использую execute немедленно, чтобы выполнить динамически построенный оператор SQL. Как я могу получить значение sql% rowcount?

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

Редактировать

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

Существуют и другие более важные процессы deamon, которые будут выполняться одновременно. Это означает, что процесс, который удаляет строки, должен быть защищен от блокировки таблиц / строк в результате удаления. Для этого мы намерены отправить процесс в спящий режим на несколько секунд после каждой фиксации, чтобы другие процессы могли продолжаться. Это означает, что я не могу просто удалить, используя один оператор «DELETE».

Ответы [ 2 ]

3 голосов
/ 03 ноября 2011

Ваш псевдокод будет работать очень плохо.Это также может привести к ошибкам ORA-1555.

Лучшим подходом было бы:

delete from table_a
where product_type='A'
/
delete from table_b 
where category='student' 
/
delete from table_c 
where name='nokia'
/

Я не понимаю, почему вы считаете, что это нужно упростить.


"Пакетное задание будетобработка миллионов строк, а это означает, что для завершения может потребоваться несколько часов. "

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

"Это означает, что процессу, который удаляет строки, необходимо запретить блокировку таблиц / строк в результате удаления."

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

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

Лучший способ минимизировать ресурсы, потребляемые вашей обработкой удаления, - это эффективно выполнять ее.Это означает выполнение чисто SQL-оператора, а не сложного цикла PL / SQL.Обработка на основе множеств значительно быстрее и легче, чем процессы RBAR.Это факт.

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

delete from table_a
where product_type = 'A'
and rownum <= 1000;
log.write('records deleted from table_a = '||SQL%rowcount);
commit;

Получите этозапускается демоном опроса ОС или cron или чем-то еще.Не используйте DBMS_LOCK.SLEEP(), чтобы приостановить его: это просто завязка процессора без всякой на то причины.

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

0 голосов
/ 11 июня 2012

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

Вы можете просто явно заблокировать таблицу и отложить эту операцию, выполнив «ожидание» для этой таблицы.

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

Oracle 10g документов http://docs.oracle.com/cd/B14117_01/server.101/b10759/statements_9015.htm

Oracle 11g документы http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9015.htm

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