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. Базирам това на следния тест:

  • Предварително зареждам таблицата с (да речем 50k) записи;
  • след това изпълнете purge storedProc, което отнема ~60 секунди за изпълнение;
  • през това време от друг sql клиент непрекъснато „ИЗБЕРЕТЕ БРОЙ(*) ОТ таблицаA“ и виждам, че броят намалява постепенно.

Ако всички DELETE бяха извършени наведнъж, бих очаквал да видя броя на записите (*) да спадне от 0 само в края на ~60 секунди. Това е, което виждам при сравними SP, написани за Oracle или SQLServer.

Това е DB2 v9.5 на Win2003.

Някакви идеи какво пропускам?


person MikeO    schedule 07.02.2014    source източник
comment
Вашият код прави ангажимент наведнъж, а не 1 по 1. Как можете да знаете, че кодът се ангажира един по един? изберете WITH UR ще ви каже модифицираните редове, но не и ангажираните.   -  person AngocA    schedule 08.02.2014
comment
Мисля, че трябва да сте по-точен с въпроса си, защото изглежда, че е въпрос на мнение и тук не е мястото да задавате такива въпроси. Трябва да разкажете проблема си, да покажете какво сте направили и да обясните ясно какво искате да получите.   -  person AngocA    schedule 08.02.2014
comment
Както казах на Mustaccio по-долу, предполагам, че сте прави, че прави единичен ангажимент. Малко предистория... Имам същата db структура в Oracle, SQLServer и DB2. Имам базирано на jdbc приложение за прочистване, което мога да стартирам срещу всичките 3. Сега пиша алтернатива на storedProcedure за всеки от 3-те dbs. Oracle и SQLServer SP показват 10x подобрение спрямо JDBC. DB2 SP показва само 2 пъти подобрение. Тази изненадваща бавност, плюс виждането (това, което сега осъзнавам, че са неангажирани четения), броят на записите намалява 1 по 1, ме накара да мисля, че се появяват допълнителни ангажименти.   -  person MikeO    schedule 08.02.2014
comment
Хм... ако е възможно, все пак бих препоръчал изтриване по начин, базиран на набор (т.е. не в курсор), тъй като това би трябвало да помогне за ускоряване на нещата. Очевидно все още трябва да издадете множество DELETEs, но има начини да се справите с това - като вмъкване на идентификатори във временна таблица или подобни. 60s само за 50k записа е доста бавен - вие правите директно четене през таблица (или евентуално индекс), така че по-голямата част от времето ви се отнема от логиката на цикъла и изтриването ред по агонизиращ ред.   -  person Clockwork-Muse    schedule 10.02.2014
comment
Бих се съгласил с @Clockwork-Muse, зададено базово решение би било предпочитано за всяка RDBMS. 50K не би трябвало да са проблем за единичен ангажимент. 100K даже. 1M може да е проблем.   -  person Charles    schedule 11.02.2014
comment
@Charles - Въпреки че една транзакция със сигурност може да успее да се справи с този обем от записи, това може да има странични ефекти в едновременна система. В зависимост от броя на сесиите/други неща, вие искате по-малки транзакции... особено след като оптимизаторите си запазват правото да заключат цялата таблица, ако и вие държите много записи.   -  person Clockwork-Muse    schedule 11.02.2014


Отговори (2)


Липсва ви разликата в изпълнението на контрола на паралелността между различните машини на бази данни. В база данни на Oracle друга сесия ще види данни, които са били ангажирани преди началото на нейната транзакция, тоест няма да види никакви изтривания, докато първата сесия не се ангажира.

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

Ако вашата бизнес логика изисква различна изолация на транзакция, говорете с вашия DBA.

person mustaccio    schedule 08.02.2014
comment
Благодаря за отговора Предполагам, че виждах броя на необвързани редове с моята 2-ра сесия... предполагам, че всъщност правех единичен ангажимент. Затруднявам се с инструментите за управление (в сравнение с други RDBMS), мисля, че трябва да погледна нещо от страна на сървъра, за да наблюдавам COMMIT. Ще работя с DBA. - person MikeO; 08.02.2014
comment
Мисля, че не виждахте изтрития ред, въпреки че те все още не бяха ангажирани. Поне това следва от твоето описание. - person mustaccio; 08.02.2014
comment
Не, ето какво бих видял... Отново зареждам таблицата с 50k записа, стартирам purge rec, което отнема около 60 секунди, за да завърши: - person MikeO; 08.02.2014
comment
(пренебрегване на предишен коментар..времето за редактиране изтече при мен) Не, ето какво бих видял... Отново зареждам таблицата с 50k записа, стартирам изчистването, което отнема около 60 секунди, за да завърши, и ето какво вторият SQL клиент/сесия ще се покаже с просто избиране на брой(*) от tableA: Време 0s = 50 000 recs Време 20s = 34 517 recs Време 40s = 17 102 recs Време 60s = 0 recs Това би означавало, че бях виждане на изтрити редове, въпреки че те все още не са ангажирани. - person MikeO; 08.02.2014
comment
34517 записа са по-малко от 50000 записа, така че не виждате 16483 записа, които са били изтрити, но не са ангажирани. нали - person mustaccio; 10.02.2014
comment
Да ти си прав. Семантично объркване от моя страна предполагам. Не виждам recs, които са били изтрити, но не са ангажирани == Виждам броя на rec, отразяващ изтрити, но не ангажирани редове. - 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