Цикл через DELETE в коде против хранимой процедуры - PullRequest
2 голосов
/ 07 ноября 2011

Я боролся с удалением огромного количества старых данных из базы данных. Каждая из 5 разных таблиц содержит до 50 миллионов строк, которые необходимо удалить. Ни один оператор удаления не может обработать такое количество данных, поэтому мне приходится последовательно удалять несколько записей одновременно. Мой вопрос заключается в том, есть ли заметное увеличение производительности зацикливания внутри хранимой процедуры вместо зацикливания в коде приложения. Теперь для конкретики я использую DB2 (9.7 CE) и пишу на C #. Для моей хранимой процедуры я использую:

--#SET TERMINATOR ;
DROP PROCEDURE myschema.purge_orders_before;
--#SET TERMINATOR @
CREATE PROCEDURE myschema.purge_orders_before (IN before_date TIMESTAMP)
    DYNAMIC RESULT SETS 1
P1: BEGIN
    DECLARE no_data SMALLINT DEFAULT 0;
    DECLARE deadlock_encountered SMALLINT DEFAULT 0;
    DECLARE deadlock_condition CONDITION FOR SQLSTATE '40001';

    DECLARE CONTINUE HANDLER FOR NOT FOUND
        SET no_data = 1;

    -- The deadlock_encountered attribute is throw-away,
    -- but a continue handler needs to do something,
    -- i.e., it's not enough to just declare a handler,
    -- it has to have an action in its body.
    DECLARE CONTINUE HANDLER FOR deadlock_condition
        SET deadlock_encountered = 1;

    WHILE (no_data = 0 ) DO
        DELETE FROM 
            (SELECT 1 FROM myschema.orders WHERE date < before_date FETCH FIRST 100 ROWS ONLY );
        COMMIT;
    END WHILE;
END P1
@
--#SET TERMINATOR ;

Чей подход был бесцеремонно снят с этой темы . Мой программный подход заключается в следующем:

public static void PurgeOrdersBefore( DateTime date ) {
    using ( OleDbConnection connection = DatabaseUtil.GetInstance( ).GetConnection( ) ) {
        connection.Open( );
        OleDbCommand command = new OleDbCommand( deleteOrdersBefore, connection );
        command.Parameters.Add( "@Date", OleDbType.DBTimeStamp ).Value = date;
        int rows = 0;
        int loopRows = 0;
        int loopIterations = 0;

        log.Info( "starting PurgeOrdersBefore loop" );
        while ( true ) {
            command.Transaction = connection.BeginTransaction( );
            loopRows = command.ExecuteNonQuery( );
            command.Transaction.Commit( );
            if ( loopRows <= 0 ) {
                break;
            }
            if ( log.IsDebugEnabled ) log.Debug( "purged " + loopRows + " in loop iteration " + loopIterations );

            loopIterations++;
            rows += loopRows;
        }
        if ( log.IsInfoEnabled ) log.Info( "purged " + rows + " orders in " + loopIterations + " loop iterations" );
    }
}

Я выполнил примитивный тест ОЧЕНЬ , в котором я напечатал метку времени в начале и в конце и вышел из цикла после 10000 в каждом. Результатом указанного теста было то, что хранимой процедуре потребовалось чуть более 6 минут, чтобы удалить 10000 строк, а программный подход занял чуть менее 5 минут. Будучи столь же примитивным, как и раньше, я думаю, что единственный вывод, который я могу сделать, состоит в том, что на практике они, скорее всего, будут иметь минимальное различие, а сохранение цикла в коде C # обеспечивает гораздо более динамичный мониторинг.

Все это говорит, кто-нибудь еще имеет какие-либо материалы по этому вопросу? Не могли бы вы объяснить, какие скрытые выгоды я мог бы получить, если бы использовал подход хранимых процедур? В частности, если Серж Риелау присматривает за этим сайтом, мне бы хотелось услышать, что вы скажете (похоже, он - ниндзя, на которого все остальные ссылаются, когда речь заходит о бессмыслице DB2, подобной этой ...)

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

Как насчет экспорта какого-либо вида, за которым следует ЗАМЕНА ЗАГРУЗКИ? Кто-нибудь делал это раньше? Есть ли пример, которому я мог бы следовать? Какие последствия это будет иметь?

Ответы [ 2 ]

1 голос
/ 13 ноября 2011

Попробуйте использовать команду TOP. Я предполагаю, что у вас есть проблемы с размером файла журнала (поэтому вы не можете просто использовать команду Удалить из таблицы).

Чтобы вы могли написать свой запрос так:

DELETE TOP 10000
FROM myschema.orders
WHERE date < before_date

Затем выполните цикл по этой команде, пока удаленные строки не будут = 0;

0 голосов
/ 05 января 2013

Если количество удаляемых записей составляет большую часть от общего количества, может быть дешевле скопировать записи good во временную таблицу, очистить исходную таблицу и скопировать временную таблицу обратно.Оптимальный способ сделать это не согласован для RDBMS;например, некоторые поддерживают TRUNCATE, а другие нет.

...