|
22.04.2010, 16:01 | #1 |
Участник
|
Массовое удаление записей через CCADOConnection
Всем доброго времени суток!
Задача: удалить большое количество записей из таблицы. Т.к. while select и delete_from сильно тормозили, решил использовать CCADOConnection: X++: CCADOConnection connection; CCADOCommand command; str sql; ; command = new CCADOCommand(); connection = new CCADOConnection(); connection.connectionString(new SqlSystem().loginConnectString()); connection.open(); command.activeConnection(connection); command.commandType(1); sql = strfmt("delete from SALESTABLE_TONTRA40478 where SALESTABLE_TONTRA40478.month = '%1'", date2str(01\03\2010, 321, 2, 3, 2, 3, 4)); command.commandText(sql); command.execute(); connection.close(); Метод "execute" в COM-объекте класса "ADODB.Command" возвратил код ошибки 0x80040E31 (<неизвестно>), который означает: [Microsoft][SQL Native Client]Query timeout expired. Можно ли это как-то победить? p.s. База - SQL Server 2005, и в 4.0 и в 2009 результат один и тот же Последний раз редактировалось Shirmin Oleg; 22.04.2010 в 16:05. |
|
22.04.2010, 16:14 | #2 |
Administrator
|
У класса CCADOCommand есть метод commandTimeout. Попробуйте поиграть с этим параметром (увеличив его). Тогда получится.
Но вообще - в целом - удаление столь большого кол-ва записей может занять продолжительное время (минут 30 к примеру). Связано это с тем - что информация об этих записях сначала копируется в лог (на случай отмены), а потом только удаляется. Поэтому - я бы рекомендовал отказаться от удаления такого кол-ва записей. Однако, иногда, при необходимости почистить заведомо большие таблицы (типа InventSettlement) можно создать копию таблицы, туда перелить нужные данные, а старую таблицу удалить. Возможно, в Вашем случае - эту идею также можно применить
__________________
Возможно сделать все. Вопрос времени |
|
22.04.2010, 16:31 | #3 |
Участник
|
Цитата:
У класса CCADOCommand есть метод commandTimeout. Попробуйте поиграть с этим параметром (увеличив его).
Цитата:
удаление столь большого кол-ва записей может занять продолжительное время (минут 30 к примеру). Связано это с тем - что информация об этих записях сначала копируется в лог (на случай отмены), а потом только удаляется. Поэтому - я бы рекомендовал отказаться от удаления такого кол-ва записей
Это периодическая операция - из внешней базы берем данные за период, если за этот же период данные уже были закачаны, то их нужно удалить. Самое интересное, что пару раз у меня этот запрос отрабатывал, и удаление занимало примерно 15-20с (против 2-3 часов при использовании других методов) Цитата:
можно создать копию таблицы, туда перелить нужные данные, а старую таблицу удалить.
|
|
22.04.2010, 17:07 | #4 |
Administrator
|
Цитата:
Сообщение от Shirmin Oleg
От безысходности параметр выставлял аж в значение 1000000, визуально вообще ничего не изменилось (падало за то же время, что и без него)
... Отказаться, к сожалению, невозможно, надо удалять. ... Удаляемая часть данных - малая толика от всей таблицы, боюсь в данном случае времени на перекачку данных в новую таблицу уйдет еще больше, чем при удалении через delete_from Т.е. написать что-то типа этого: X++: Counter i; ; ttsbegin; while select forupdate mytable { mytable.dodelete(); i++; if (i > 10000) // Число подобрать надо { break; } } ttscommit;
__________________
Возможно сделать все. Вопрос времени |
|
22.04.2010, 18:40 | #5 |
Участник
|
И даже тогда можно не отказываться от преимуществ конструкции delete_from. 'Порционного' удаления можно добиться при помощи фильтрации.
X++: SALESTABLE_TONTRA40478 Table; date month = 01\03\2010; RecId minRecId, maxRecId, fromRecId, toRecId; int partSize = 10000 // Число подобрать надо; select minof(RecId) from Table where Table.month == month; fromRecId = Table.RecId; select maxof(RecId) from Table where Table.month == month; toRecId = Table.RecId; for(fromRecId = minRecId; fromRecId <= maxRecId; fromRecId += partSize) { toRecId = fromRecId + partSize; Table.skipDatabaseLog(true); Table.skipDeleteActions(true); Table.skipDeleteMethod(true) delete_from Table where Table.month == month && Table.RecId >= fromRecId && Table.RecId <= toRecId; } |
|
22.04.2010, 18:47 | #6 |
Участник
|
Сложно то как
Да. Но брать порциями по PartSize - бесполезно. В интервале может быть и 10тыс записей, и ни одной. Достаточно просто сузить диапазон. А главное - обрамить tts-скобками X++: SALESTABLE_TONTRA40478 Table; date month; while(true) { select minof(month),minof(recid) from Table; if( !Table.recid ) break; month = Table.month; ttsbegin; Table.skipDatabaseLog(true); Table.skipDeleteActions(true); Table.skipDeleteMethod(true) delete_from Table where Table.month == month; ttscommit; } |
|
22.04.2010, 19:10 | #7 |
Участник
|
Зато универсально. Таким способом можно удалить любую другую выборку.
Цитата:
На сколько я понял условие задачи. Удаляють нужно не все записи из таблицы, а только за определённый месяц. Причём даже за один месяц число записей > 600 000; Зачем? Или это шутка юмора такая? |
|
22.04.2010, 19:00 | #8 |
Administrator
|
Цитата:
Конечно брать тупо по 10 (20,30) тыс. записей - тоже не вариант - объем записей бывает разный. В общем - надо искать "золотую" середину.
__________________
Возможно сделать все. Вопрос времени |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
22.04.2010, 19:02 | #9 |
Участник
|
Цитата:
причем в сообщении автора условие по этом полю и было. вполне возможно, что проблема с отсутствии индекса и в Full Table Scan. |
|
22.04.2010, 16:44 | #10 |
Ищущий знания...
|
а перед delete_from делали skipDatabaseLog(true), skipDeleteActions(true), skipDeleteMethod(true) ?
на сколько я знаю если всё выше указанное выполнить, то delete_from должно удалить данные одним запросом к базе, т.е. делает тоже самое, что Вы делает через CCADOConnection.
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
|
За это сообщение автора поблагодарили: mazzy (2), Zabr (4), Pustik (2), S.Kuskov (1). |
23.04.2010, 11:33 | #11 |
Ищущий знания...
|
к своему совету ещё хочется добавить...
тут главное помнить ещё и про бизнес логику... например в методе delete() может быть реализована какая то очень важная процедура, без которой может нарушиться бизнес логика. то же самое и про DeleteActions, возможно при удалении таблицы, обязательно должны вычищаться ещё какие либо связанные таблицы... Т.е. я хочу сказать, не следует использовать скипы в слепую, возможно необходимо будет дописать такое же удаление подчиненных таблиц, и добавить что-то важное из метода delete()...
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
23.04.2010, 12:20 | #12 |
Участник
|
Цитата:
общее количество удаляемых записей сейчас порядка 500 тыс.ежедневно
|
|
23.04.2010, 12:38 | #13 |
Участник
|
Было часа два. Стало минут 25-30.
|
|
23.04.2010, 12:39 | #14 |
Модератор
|
Хороший метод устранить проблему - просто не заполнять "проблемные" данные. Перекрываете insert на таблице, пишете insert(boolean _doInsert = false), если в метод приходит false, то ничего не далаете. Тогда по-умолчанию в таблицу данные не попадут. А если надо вставить, находите откуда их необходимо заполнять и указываете xxxx.insert(true),перекрестные ссылки Вм в помощь.
Удачи! С Уважением, Георгий |
|
23.04.2010, 13:19 | #15 |
Участник
|
У меня данные не проблемные. На момент создания они нужны. А по прошествии строго определенного времени они становятся не нужны и их можно безболезненно чистить. Специфика конкретного решения.
|
|
23.04.2010, 13:25 | #16 |
Участник
|
|
|
23.04.2010, 13:36 | #17 |
Модератор
|
Да просто давныыым-давно сталкивался именно с подобной проблемой. Например, при сводном планировании нужны InventSumLogTTS (много времени прошло, могу не помнить точно), а после - ну что с ними далать? а они расползались...
Короче, подобным способом и решили проблему. Или периодическую очистку делали? С Уважением, Георгий |
|
23.04.2010, 14:13 | #18 |
Administrator
|
Цитата:
Тут другая ситуация. Грузят данные. Они актуальны (например данные с магазинов). Потом (например, в течении дня) присылают откорректированные данные (кто-то ошибся). Старые данные еще не обрабатывали (они автоматически закачались, но их обработка будет к примеру на след. день). Соотв - старые данные нужно стереть, а новые закачать. Я с подобной потребностью сталкивался при импорте строк выписки. Сотрудник заказывает предварительную (по операциям текущего дня) выписку и получает ее (включая автозагрузку в АХ). Банк кстати вполне может пойти навстречу по такой услуге, особенно если это свой банк . Затем (на след. день) выписка приходит уже окончательная. Соотв - предварительная выписка в АХ не обрабатывается в тот же день. Куда деть предварительную выписку? Стереть. А теперь считаем, что фирма (холдинг) у нас состоит не из одного юрлица и не одного расчетного счета... Да и операций у нас много.... В общем - объемно получается
__________________
Возможно сделать все. Вопрос времени |
|
23.04.2010, 14:41 | #19 |
Модератор
|
Да подожди ты таблицы резать
Надо сначала найти источник проблемы. 1. Понять какие данные нужны. 2. Когда они устаревают. 3. На что эти данные влияют? Не пригодяться ли удаленные данные впоследствии? Связи. 4. Можно ли это решить настройками. Или, например переносить в другой журнал и удалять. 5. Если нельзя программно, то сколько времени займет доработка. 6. Можно ли запускать данную процедуру на периодической основе. А программистам сразу бы drop table Георгий |
|
23.04.2010, 16:46 | #20 |
Участник
|
Переделал на удаление через delete_from по частям (по 100000 записей)
Удаление 600000 записей - 1 час, удаление 1200000 записей - 2 часа Удаление 600000 записей напрямую в SQL Server Management Studio - 16 минут. Собственно, почему я не хотел использовать delete_from, надеялся, что будет работать быстрее, но, похоже, не судьба... |
|
Теги |
ax2009, ccadoconnection, delete_from, оптимизация, удаление |
|
|