AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 22.04.2010, 16:01   #1  
Shirmin Oleg is offline
Shirmin Oleg
Участник
 
89 / 35 (2) +++
Регистрация: 26.03.2004
Массовое удаление записей через 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();
Проблема в том, что при небольшом кол-ве строк в таблице (порядка 20000) все прекрасно работает, но если записей много (около 600000), падает со след. ошибкой:

Метод "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  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,323 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
У класса CCADOCommand есть метод commandTimeout. Попробуйте поиграть с этим параметром (увеличив его). Тогда получится.
Но вообще - в целом - удаление столь большого кол-ва записей может занять продолжительное время (минут 30 к примеру). Связано это с тем - что информация об этих записях сначала копируется в лог (на случай отмены), а потом только удаляется. Поэтому - я бы рекомендовал отказаться от удаления такого кол-ва записей.

Однако, иногда, при необходимости почистить заведомо большие таблицы (типа InventSettlement) можно создать копию таблицы, туда перелить нужные данные, а старую таблицу удалить.
Возможно, в Вашем случае - эту идею также можно применить
__________________
Возможно сделать все. Вопрос времени
Старый 22.04.2010, 16:31   #3  
Shirmin Oleg is offline
Shirmin Oleg
Участник
 
89 / 35 (2) +++
Регистрация: 26.03.2004
Цитата:
У класса CCADOCommand есть метод commandTimeout. Попробуйте поиграть с этим параметром (увеличив его).
От безысходности параметр выставлял аж в значение 1000000, визуально вообще ничего не изменилось (падало за то же время, что и без него)
Цитата:
удаление столь большого кол-ва записей может занять продолжительное время (минут 30 к примеру). Связано это с тем - что информация об этих записях сначала копируется в лог (на случай отмены), а потом только удаляется. Поэтому - я бы рекомендовал отказаться от удаления такого кол-ва записей
Отказаться, к сожалению, невозможно, надо удалять.
Это периодическая операция - из внешней базы берем данные за период, если за этот же период данные уже были закачаны, то их нужно удалить.
Самое интересное, что пару раз у меня этот запрос отрабатывал, и удаление занимало примерно 15-20с (против 2-3 часов при использовании других методов)

Цитата:
можно создать копию таблицы, туда перелить нужные данные, а старую таблицу удалить.
Удаляемая часть данных - малая толика от всей таблицы, боюсь в данном случае времени на перекачку данных в новую таблицу уйдет еще больше, чем при удалении через delete_from
Старый 22.04.2010, 16:44   #4  
lev is offline
lev
Ищущий знания...
Аватар для lev
Oracle
MCBMSS
Axapta Retail User
 
1,723 / 491 (20) +++++++
Регистрация: 18.01.2005
Адрес: Москва
а перед delete_from делали skipDatabaseLog(true), skipDeleteActions(true), skipDeleteMethod(true) ?

на сколько я знаю если всё выше указанное выполнить, то delete_from должно удалить данные одним запросом к базе, т.е. делает тоже самое, что Вы делает через CCADOConnection.
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с)
С Уважением,
Елизаров Артем
За это сообщение автора поблагодарили: mazzy (2), Zabr (4), Pustik (2), S.Kuskov (1).
Старый 22.04.2010, 17:07   #5  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,323 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Цитата:
Сообщение от Shirmin Oleg Посмотреть сообщение
От безысходности параметр выставлял аж в значение 1000000, визуально вообще ничего не изменилось (падало за то же время, что и без него)
...
Отказаться, к сожалению, невозможно, надо удалять.
...
Удаляемая часть данных - малая толика от всей таблицы, боюсь в данном случае времени на перекачку данных в новую таблицу уйдет еще больше, чем при удалении через delete_from
Тогда могу лишь порекомендовать либо последовать совету lev, либо ... последовать его совету, но попытаться бить на отдельные транзакции удаляемые записи.
Т.е. написать что-то типа этого:
X++:
Counter i;
;
ttsbegin;
while select forupdate mytable
{
      mytable.dodelete();
      i++;
      if (i > 10000) // Число подобрать надо
      {
            break;
      }
}
ttscommit;
И вызывать этот метод несколько раз. Общее время может увеличиться, но зато не будет "зависаний" надолго, как и уменьшится вероятность блокировок. Плюс еще прогресс-бар можно вставить.
__________________
Возможно сделать все. Вопрос времени
Старый 22.04.2010, 18:40   #6  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от sukhanchik Посмотреть сообщение
попытаться бить на отдельные транзакции
И даже тогда можно не отказываться от преимуществ конструкции 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   #7  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Сложно то как
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
'Порционного' удаления можно добиться при помощи фильтрации.
Да. Но брать порциями по 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;
}
__________________
полезное на axForum, github, vk, coub.
Старый 22.04.2010, 19:00   #8  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,323 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
И даже тогда можно не отказываться от преимуществ конструкции delete_from. 'Порционного' удаления можно добиться при помощи фильтрации.
У фильтрации есть тот минус, что по фильтруемому полю нужно иметь адекватный индекс, дабы фильтрация состоялась в ожидаемое время, а не произошел бы Full Table Scan. А далеко не всегда существует индекс в котором бы было единственное поле - RecID.
Конечно брать тупо по 10 (20,30) тыс. записей - тоже не вариант - объем записей бывает разный.
В общем - надо искать "золотую" середину.
__________________
Возможно сделать все. Вопрос времени
За это сообщение автора поблагодарили: mazzy (2).
Старый 22.04.2010, 19:02   #9  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от sukhanchik Посмотреть сообщение
У фильтрации есть тот минус, что по фильтруемому полю нужно иметь адекватный индекс, дабы фильтрация состоялась в ожидаемое время, а не произошел бы Full Table Scan.
Тоже верно!
причем в сообщении автора условие по этом полю и было. вполне возможно, что проблема с отсутствии индекса и в Full Table Scan.
__________________
полезное на axForum, github, vk, coub.
Старый 22.04.2010, 19:10   #10  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от mazzy Посмотреть сообщение
Сложно то как
Зато универсально. Таким способом можно удалить любую другую выборку.

Цитата:
Сообщение от mazzy Посмотреть сообщение
Да. Но брать порциями по PartSize - бесполезно. В интервале может быть и 10тыс записей, и ни одной.
Идея в том что их гарантированно не будет больше 10тыс. Те таблица если и будет заблокированна то не на долго. А если цикл прогонит пару итераций в холостую, что ж с того?

Цитата:
Сообщение от mazzy Посмотреть сообщение
Достаточно просто сузить диапазон
На сколько я понял условие задачи. Удаляють нужно не все записи из таблицы, а только за определённый месяц. Причём даже за один месяц число записей > 600 000;

Цитата:
Сообщение от mazzy Посмотреть сообщение
А главное - обрамить tts-скобками
Зачем? Или это шутка юмора такая?
Старый 22.04.2010, 19:16   #11  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Идея в том что их гарантированно не будет больше 10тыс. Те таблица если и будет заблокированна то не на долго. А если цикл прогонит пару итераций в холостую, что ж с того?
Может быть и так...
Но может быть просто поискать другое поле? Которое и с индексом, и дает относительно небольшое число записей? Например, не месяц, а дату.

Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Зачем? Или это шутка юмора такая?
Нет.
Если база установлена в Recovery Model = Full, то разницы действительно не будет.
Если же база установлена в Recovery Model = Simple, то после окончания каждой транзакции transaction log будет очищаться.

вполне возможно, что у автора transaction log сильно растет, а на диске места свободного мало. Вот и мучается SQL, затрачивая каждый раз много времени на увеличение transaction log.
__________________
полезное на axForum, github, vk, coub.
Старый 22.04.2010, 19:20   #12  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от sukhanchik Посмотреть сообщение
далеко не всегда существует индекс в котором бы было единственное поле - RecID
Уточню. Необходим индекс, где поле - RecID стоит на первом месте.

И да, естественно, включать индекс по RecId будет необходимо.

Цитата:
Сообщение от mazzy Посмотреть сообщение
после окончания каждой транзакции transaction log будет очищаться
Это понятно, я просто рассчитывал, что конструкция delete_from cамостоятельно открывает и закрывает транзакцию.
За это сообщение автора поблагодарили: mazzy (2).
Старый 22.04.2010, 19:21   #13  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Это понятно, я просто рассчитывал, что конструкция delete_from cамостоятельно открывает и закрывает транзакцию.
Э-э-э... По-моему, нет. Хотя могу ошибаться.
__________________
полезное на axForum, github, vk, coub.
Старый 23.04.2010, 10:46   #14  
Zabr is offline
Zabr
Участник
Axapta Retail User
 
1,202 / 345 (14) ++++++
Регистрация: 26.06.2002
Адрес: Москва
В течение полутора лет ежедневно выполняется аналогичная задача, общее количество удаляемых записей сейчас порядка 500 тыс.ежедневно, удаление идет блоками с фиксированным числом записей точно так как выше написал sukhanchik. Попробовал сделать через delete_from c отключением skipDatabaseLog(true), skipDeleteActions(true), skipDeleteMethod(true) как написал lev - стало удаляться быстрее примерно в 4-5 раз. Отличный совет, респект. Блокировок и зависаний нет, т.к. делается не один delete_from на весь массив записей, а отдельными транзакциями по складам магазинов (около 40).
Старый 23.04.2010, 11:33   #15  
lev is offline
lev
Ищущий знания...
Аватар для lev
Oracle
MCBMSS
Axapta Retail User
 
1,723 / 491 (20) +++++++
Регистрация: 18.01.2005
Адрес: Москва
к своему совету ещё хочется добавить...
тут главное помнить ещё и про бизнес логику...
например в методе delete() может быть реализована какая то очень важная процедура, без которой может нарушиться бизнес логика. то же самое и про DeleteActions, возможно при удалении таблицы, обязательно должны вычищаться ещё какие либо связанные таблицы...

Т.е. я хочу сказать, не следует использовать скипы в слепую, возможно необходимо будет дописать такое же удаление подчиненных таблиц, и добавить что-то важное из метода delete()...
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с)
С Уважением,
Елизаров Артем
За это сообщение автора поблагодарили: mazzy (2).
Старый 23.04.2010, 12:20   #16  
Shirmin Oleg is offline
Shirmin Oleg
Участник
 
89 / 35 (2) +++
Регистрация: 26.03.2004
Цитата:
общее количество удаляемых записей сейчас порядка 500 тыс.ежедневно
За какое время удаляются?
Старый 23.04.2010, 12:38   #17  
Zabr is offline
Zabr
Участник
Axapta Retail User
 
1,202 / 345 (14) ++++++
Регистрация: 26.06.2002
Адрес: Москва
Было часа два. Стало минут 25-30.
Старый 23.04.2010, 12:39   #18  
George Nordic is offline
George Nordic
Модератор
Аватар для George Nordic
Злыдни
 
4,479 / 1250 (50) ++++++++
Регистрация: 17.12.2003
Адрес: Moscow
Записей в блоге: 9
Хороший метод устранить проблему - просто не заполнять "проблемные" данные. Перекрываете insert на таблице, пишете insert(boolean _doInsert = false), если в метод приходит false, то ничего не далаете. Тогда по-умолчанию в таблицу данные не попадут. А если надо вставить, находите откуда их необходимо заполнять и указываете xxxx.insert(true),перекрестные ссылки Вм в помощь.

Удачи!

С Уважением,
Георгий
Старый 23.04.2010, 13:19   #19  
Zabr is offline
Zabr
Участник
Axapta Retail User
 
1,202 / 345 (14) ++++++
Регистрация: 26.06.2002
Адрес: Москва
Цитата:
Сообщение от George Nordic Посмотреть сообщение
Хороший метод устранить проблему - просто не заполнять "проблемные" данные.
У меня данные не проблемные. На момент создания они нужны. А по прошествии строго определенного времени они становятся не нужны и их можно безболезненно чистить. Специфика конкретного решения.
Старый 23.04.2010, 13:25   #20  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от George Nordic Посмотреть сообщение
Перекрываете insert на таблице, пишете insert(boolean _doInsert = false)
Радикально. Этакий антиспам фильтр получается . Но при такой трактовке задачи не лучше ли будет устранить сам источник "проблемных" данных?
Теги
ax2009, ccadoconnection, delete_from, оптимизация, удаление

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Задвоение RecId при вставке записей через COM коннектор db DAX: Программирование 1 23.04.2009 15:12
Класс OfficialsServ_RU - удаление записей из OfficialsTrans_RU Logger DAX: Программирование 3 14.09.2006 15:58
Запуск программы из Аксапты через 2-хзвенку Migel_84 DAX: Программирование 25 27.04.2006 09:41
Проблема с запуском business connector'a через удал доступ yooshi DAX: Программирование 1 07.11.2005 08:56
Удаление записей из InventDim и SalesTable DreamCreator DAX: Программирование 4 08.12.2004 17:23

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 12:42.