хранимая процедура db2 - проблемы с пакетированием операторов DELETE

Я пишу процедуры DB2 всего несколько дней, но пытаюсь выполнить "пакетное удаление" для данной таблицы. Моя ожидаемая логика:

  • открыть курсор
  • пройти через него до EOF
  • выдавать DELETE на каждой итерации

Для упрощения этого вопроса предположим, что я хочу выполнить только один COMMIT (из всех DELETE) после завершения цикла WHILE (т.е. после того, как курсор достигнет EOF). Итак, учитывая пример кода ниже:

CREATE TABLE tableA (colA INTEGER, ...)


CREATE PROCEDURE "SCHEMA"."PURGE_PROC"
(IN batchSize INTEGER)
LANGUAGE SQL
SPECIFIC SQL140207163731500
BEGIN

   DECLARE tempID             INTEGER;
   DECLARE eof_bool           INTEGER DEFAULT 0;
   DECLARE sqlString          VARCHAR(1000);
   DECLARE sqlStmt            STATEMENT;
   DECLARE myCurs             CURSOR WITH HOLD FOR sqlStmt;
   DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET eof_bool = 1;

   SET sqlString = 'select colA from TableA';
   PREPARE sqlStmt FROM sqlString;

   OPEN myCurs;
   FETCH myCurs INTO tempID;
   WHILE (eof_bool = 0) DO
        DELETE FROM TableA where colA = tempID;
        FETCH myCurs INTO tempID;
   END WHILE;

   COMMIT;
   CLOSE myCurs;

END

Примечание. В моем реальном сценарии:

  • Я удаляю не все записи из таблицы, а только определенные по каким-то дополнительным критериям; а также
  • Я планирую выполнять COMMIT каждые N# итераций цикла WHILE (скажем, 500 или 1000), а не весь беспорядок, как описано выше; а также
  • Я планирую УДАЛИТЬ несколько таблиц, а не только эту;

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

  • Я предварительно загружаю таблицу (скажем, 50 тыс.) Записями;
  • затем запустите хранимую процедуру очистки, которая занимает ~ 60 секунд;
  • в это время с другого клиента sql я постоянно «ВЫБЕРИТЕ СЧЁТ (*) ИЗ таблицы A» и вижу постепенное уменьшение счёта.

Если бы все DELETE были зафиксированы одновременно, я бы ожидал, что количество записей (*) упадет только с 0 до 0 в конце ~ 60 секунд. Это то, что я вижу с сопоставимыми SP, написанными для Oracle или SQLServer.

Это DB2 v9.5 на Win2003.

Любые идеи, что мне не хватает?


person MikeO    schedule 07.02.2014    source источник
comment
Ваш код делает коммит сразу, а не один за другим. Как узнать, что код фиксирует один за другим? a select WITH UR сообщит вам измененные строки, но не зафиксированные.   -  person AngocA    schedule 08.02.2014
comment
Я думаю, вы должны быть более пунктуальны со своим вопросом, потому что, похоже, это вопрос мнения, и здесь не место задавать такие вопросы. Вы должны рассказать о своей проблеме, показать, что вы сделали, и четко объяснить, что вы хотите получить.   -  person AngocA    schedule 08.02.2014
comment
Как я сказал Mustaccio ниже, я думаю, вы, ребята, правы, что он делает один коммит. Некоторая предыстория... У меня такая же структура базы данных в Oracle, SQLServer и DB2. У меня есть приложение очистки на основе jdbc, которое я могу запустить для всех 3. Сейчас я пишу альтернативу хранимой процедуре для каждой из 3 баз данных. Oracle и SQLServer SP показывают 10-кратное улучшение по сравнению с JDBC. DB2 SP показывает только двукратное улучшение. Эта удивительная медлительность, а также наблюдение (теперь я понимаю, что это были незафиксированные чтения) уменьшения количества записей 1 на 1 заставили меня подумать, что происходят дополнительные коммиты.   -  person MikeO    schedule 08.02.2014
comment
Хм... если возможно, я бы все же рекомендовал удалять на основе набора (т. е. не в курсоре), так как это должно еще больше ускорить процесс. Очевидно, вам все равно придется выдавать несколько DELETE, но есть способы справиться с этим — например, вставка идентификаторов во временную таблицу или что-то подобное. 60 секунд всего за 50 000 записей — это довольно медленно — вы выполняете прямое чтение таблицы (или, возможно, индекса), поэтому большую часть времени занимает циклическая логика и последовательное удаление строк.   -  person Clockwork-Muse    schedule 10.02.2014
comment
Я согласен с @Clockwork-Muse, для любой СУБД предпочтительнее использовать базовое решение. 50K не должно быть проблемой для одного коммита. 100К даже. 1M может быть проблемой.   -  person Charles    schedule 11.02.2014
comment
@Charles - Хотя одна транзакция, безусловно, может обрабатывать такой объем записей, это может иметь побочные эффекты в параллельной системе. В зависимости от количества сеансов/других вещей, вам нужны транзакции меньшего размера... тем более, что оптимизаторы оставляют за собой право заблокировать всю таблицу, если вы слишком держитесь много записей.   -  person Clockwork-Muse    schedule 11.02.2014


Ответы (2)


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

В DB2, в зависимости от параметров конфигурации сервера (например, DB2_SKIPDELETED) и/или второго уровня изоляции сеанса (например, незафиксированное чтение), он фактически может видеть (или не видеть) данные, затронутые транзакциями в процессе выполнения.

Если ваша бизнес-логика требует другой изоляции транзакций, поговорите со своим администратором баз данных.

person mustaccio    schedule 08.02.2014
comment
Спасибо за ответ. Думаю, я видел количество незафиксированных строк во втором сеансе... думаю, на самом деле это была одна фиксация. Я борюсь с инструментами управления (по сравнению с другими СУБД), я думаю, мне нужно посмотреть что-то на стороне сервера, чтобы отслеживать COMMIT. Я буду работать с администратором баз данных. - person MikeO; 08.02.2014
comment
Я думаю, вы не видели удаленную строку, хотя они все еще не были зафиксированы. По крайней мере, так следует из вашего описания. - person mustaccio; 08.02.2014
comment
Нет, вот что я бы увидел... Опять же, я загружаю таблицу с 50 тыс. записей, запускаю запись очистки, которая занимает около 60 секунд: - person MikeO; 08.02.2014
comment
(не обращайте внимания на предыдущий комментарий. Время редактирования истекло для меня) Нет, вот что я увижу... Опять же, я загружаю таблицу с 50 000 записей, запускаю запись очистки, которая занимает около 60 секунд, и вот что второй клиент/сеанс SQL будет отображаться с помощью простого выбора счетчика (*) из таблицы A: Время 0 с = 50 000 записей Время 20 с = 34 517 записей Время 40 с = 17 102 записей Время 60 с = 0 записей Это означает, что я был видеть удаленные строки, хотя они еще не были зафиксированы. - person MikeO; 08.02.2014
comment
34 517 – меньше 50 000 записей, поэтому вы не видите 16 483 записи, которые были удалены, хотя и не зафиксированы. Верно? - person mustaccio; 10.02.2014
comment
Да, ты прав. Думаю, семантическая путаница с моей стороны. Я не вижу записи, которые были удалены, но еще не зафиксированы == Я вижу количество записей, отражающее удаленные, но не зафиксированные строки. - person MikeO; 10.02.2014

Следует отметить, что вы удаляете «за пределами курсора».

Правильным способом удаления с помощью курсора будет использование «позиционированного удаления».

DELETE FROM tableA WHERE CURRENT OF myCurs;

Приведенное выше удаляет только что полученную строку.

person Charles    schedule 09.02.2014
comment
Спасибо, если я правильно понимаю, ваше предложение ускорит удаление этой единственной таблицы. Но если этот colA является ключом для нескольких других таблиц, которые также необходимо удалить в цикле (что является моим предельным сценарием), мне нужно будет использовать подход с временной переменной. Правильный? - person MikeO; 10.02.2014