|  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, 16:44 | #4 | 
| Ищущий знания... | 
			
			а перед 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 | 
| 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 | #6 | 
| Участник | 
			
			И даже тогда можно не отказываться от преимуществ конструкции 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 | 
| Участник | 
			
			Сложно то как   Да. Но брать порциями по 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:00 | #8 | 
| Administrator | Цитата: Конечно брать тупо по 10 (20,30) тыс. записей - тоже не вариант - объем записей бывает разный. В общем - надо искать "золотую" середину. 
				__________________ Возможно сделать все. Вопрос времени | 
|  | |
| За это сообщение автора поблагодарили: mazzy (2). | |
|  22.04.2010, 19:02 | #9 | 
| Участник | Цитата: причем в сообщении автора условие по этом полю и было. вполне возможно, что проблема с отсутствии индекса и в Full Table Scan. | 
|  | 
|  22.04.2010, 19:10 | #10 | 
| Участник | 
			
			Зато универсально. Таким способом можно удалить любую другую выборку. Цитата: На сколько я понял условие задачи. Удаляють нужно не все записи из таблицы, а только за определённый месяц. Причём даже за один месяц число записей > 600 000; Зачем? Или это шутка юмора такая?   | 
|  | 
|  22.04.2010, 19:16 | #11 | 
| Участник | Цитата: Но может быть просто поискать другое поле? Которое и с индексом, и дает относительно небольшое число записей? Например, не месяц, а дату. Нет. Если база установлена в Recovery Model = Full, то разницы действительно не будет. Если же база установлена в Recovery Model = Simple, то после окончания каждой транзакции transaction log будет очищаться. вполне возможно, что у автора transaction log сильно растет, а на диске места свободного мало. Вот и мучается SQL, затрачивая каждый раз много времени на увеличение transaction log. | 
|  | 
|  22.04.2010, 19:20 | #12 | 
| Участник | Цитата: И да, естественно, включать индекс по RecId будет необходимо. Это понятно, я просто рассчитывал, что конструкция delete_from cамостоятельно открывает и закрывает транзакцию. | 
|  | |
| За это сообщение автора поблагодарили: mazzy (2). | |
|  22.04.2010, 19:21 | #13 | 
| Участник | 
			
			Э-э-э... По-моему, нет. Хотя могу ошибаться.
		 | 
|  | 
|  23.04.2010, 10:46 | #14 | 
| Участник | 
			
			В течение полутора лет ежедневно выполняется аналогичная задача, общее количество удаляемых записей сейчас порядка 500 тыс.ежедневно, удаление идет блоками с фиксированным числом записей точно так как выше написал sukhanchik. Попробовал сделать через delete_from c отключением skipDatabaseLog(true), skipDeleteActions(true), skipDeleteMethod(true) как написал lev - стало удаляться быстрее примерно в 4-5 раз. Отличный совет, респект. Блокировок и зависаний нет, т.к. делается не один delete_from на весь массив записей, а отдельными транзакциями по складам магазинов (около 40).
		 | 
|  | 
|  23.04.2010, 11:33 | #15 | 
| Ищущий знания... | 
			
			к своему совету ещё хочется добавить... тут главное помнить ещё и про бизнес логику... например в методе delete() может быть реализована какая то очень важная процедура, без которой может нарушиться бизнес логика. то же самое и про DeleteActions, возможно при удалении таблицы, обязательно должны вычищаться ещё какие либо связанные таблицы... Т.е. я хочу сказать, не следует использовать скипы в слепую, возможно необходимо будет дописать такое же удаление подчиненных таблиц, и добавить что-то важное из метода delete()... 
				__________________ "Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем | 
|  | |
| За это сообщение автора поблагодарили: mazzy (2). | |
|  23.04.2010, 12:20 | #16 | 
| Участник | Цитата: 
		
			общее количество удаляемых записей сейчас порядка 500 тыс.ежедневно
		
	 | 
|  | 
|  23.04.2010, 12:38 | #17 | 
| Участник | 
			
			Было часа два. Стало минут 25-30.
		 | 
|  | 
|  23.04.2010, 12:39 | #18 | 
| Модератор | 
			
			Хороший метод устранить проблему - просто не заполнять "проблемные" данные. Перекрываете insert на таблице, пишете insert(boolean _doInsert = false), если в метод приходит false, то ничего не далаете. Тогда по-умолчанию в таблицу данные не попадут. А если надо вставить, находите откуда их необходимо заполнять и указываете xxxx.insert(true),перекрестные ссылки Вм в помощь. Удачи! С Уважением, Георгий | 
|  | 
|  23.04.2010, 13:19 | #19 | 
| Участник | 
			
			У меня данные не проблемные. На момент создания они нужны. А по прошествии строго определенного времени они становятся не нужны и их можно безболезненно чистить. Специфика конкретного решения.
		 | 
|  | 
|  23.04.2010, 13:25 | #20 | 
| Участник | |
|  | 
| Теги | 
| ax2009, ccadoconnection, delete_from, оптимизация, удаление | 
|  | 
| 
 |