Автор статьи: [[user>H A D G E H O G s]]\\ исходник: http://kb.mista.ru/article.php?id=678 В статье описываются приемы оптимизация работы с прикладными объектами платформы через COM-соединение. Так же в статье приведены статистические данные замеров производительности ((Тесты делал правильно, базу очищал, перезапускал. База файловая. [[user>H A D G E H O G s]])) и проведен сравнительный анализ различных методов работы с COM-объектами. ====== Охота на COMов ====== ===== Часть первая. Жажда скорости ===== ==== Глава первая. Широко закрытые глаза ==== === Чтение === Начнем с очевидных вещей. Первым шагом для существенного повышения скорости работы чтения через Внешнее Соединение считаю полный отказ от использования выборок/ссылок внешней базы. \\ Про конструкции вида: Выборка=V8.Справочники.Склады.Выбрать(); Пока Выборка.Следующий() Цикл //... КонецЦикла; рекомендуется вспоминать только в страшных ночных кошмарах. Ибо при обращении к любому реквизиту, не принадлежащему к основным данным объекта произойдет чтение всех реквизитов объекта. Если мы попытаемся получить реквизит основных данных - то произойдет чтение только основных данных, что тоже не хорошо. Использование же запроса позволит читать только то, что вам действительно нужно. Вот такой код: Запрос=V8.NewObject("Запрос"); Запрос.Текст= "ВЫБРАТЬ | Склады.Код, | Склады.Наименование, | Склады.ВидСклада.Порядок |ИЗ | Справочник.Склады КАК Склады"; Выборка=Запрос.Выполнить().Выбрать(); Пока Выборка.Следующий() Цикл КонецЦикла; позволит прочесть из базы только то, что действительно нужно. Также необходимо обратить внимание на то, что поле Ссылка в запросах, подобных приведенному ниже, в большинстве случаев бесполезна и даже вредна: Запрос=Бухгалтерия.NewObject("Запрос"); Запрос.Текст= "ВЫБРАТЬ | Склады.Ссылка, //Вот здесь оно, это поле | Склады.Код, | Склады.Наименование, | Склады.ВидСклада.Порядок |ИЗ | Справочник.Склады КАК Склады"; Выборка=Запрос.Выполнить().Выбрать(); Пока Выборка.Следующий() Цикл КонецЦикла; Ссылка бесполезна потому, что это ссылка на объект внешней базы, которая в локальной базе БЕСПОЛЕЗНА ((в большинстве случаев)). А вредна потому, что ее наличие может спровоцировать появление такого кода: Пока Выборка.Следующий() Цикл НашОбъект=ПоискПоКоду(Выборка.Ссылка.Код); КонецЦикла; которая циклично читает объект, а значит сводит на нет весь смысл использование запроса ((Поверьте моему опыту, вы этого и не заметите, пока вам бухгалтер не скажет: "Что же так долго выгрузка ползет?.." [[user>H A D G E H O G s]])). Также не рекомендуется использовать конструкцию вида: Пока Выборка.Следующий() Цикл Сообщить("Производится выгрузка склада "+V8.String(Выборка.Ссылка)); КонецЦикла; Хотя данный код и менее пагубно сказывается на быстродействии --- считываются только основные данные объекта --- считывание лишних данных через COM-соединение все же происходит. Лучше использовать функции ПРЕДСТАВЛЕНИЕ() иПРЕДСТАВЛЕНИЕССЫЛКИ() в запросе, или на крайняк Наименование вытащить в поля запроса ((Есть и еще одна причина, но о ней чуть позже)). Следует отметить, что указанные особенности справедливы не только для внешнего соединения, но и для локальных баз. Не пренебрегайте приведенными советами и рекомендациями.\\ В Таблице 1 приведены результаты тестов на скорость получения данных в зависимости от типа соединения и способа получения данных. Таблица 1. Время выборки 10000 элементов в секундах ^Тип соединения ^Выборка ^Запрос ^ |ComConnector |0,583412 |0,192428| |Application |1,178783 |0,782243| === Запись === Приведенные выше методики оптимизации подходят только для чтения данных, поскольку платформа 1С не предоставляет ни каких возможностей для записи данных при помощи языка запросов. По этому управлять количеством и способом получения данных объектов при записи практически не возможно.\\ Эмпирически путем было установлено, что транзакции при записи через COM-соединение позволяют ускорить процесс. На текущий момент теоретического обоснования этого феномена найти не удалось. Таблица 2 показывает, что конструкция вида: НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый; СправочникМенеджер=Бухгалтерия.Справочники.Склады; Бухгалтерия.НачатьТранзакцию(); Для i=1 по 1000 Цикл НовыйОбъект=СправочникМенеджер.СоздатьЭлемент(); НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор()); НовыйОбъект.ВидСклада=НовыйВидСклада; НовыйОбъект.Записать(); КонецЦикла; Бухгалтерия.ЗафиксироватьТранзакцию(); отрабатывает быстрее, чем такая конструкция: НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый; СправочникМенеджер=Бухгалтерия.Справочники.Склады; Для i=1 по 1000 Цикл НовыйОбъект=СправочникМенеджер.СоздатьЭлемент(); НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор()); НовыйОбъект.ВидСклада=НовыйВидСклада; НовыйОбъект.Записать(); КонецЦикла; Таблица 2. Время записи 1000 элементов в секундах в зависимости от использования транзакции ^Тип соединения ^Простая ^Транзакция^ |ComConnector |2,594816 |1,558324| |Application |8,482852 |7,455433| ==== Глава два. Для сильных духом мужчин ==== Во второй главе речь пойдет про методику оптимизации работы с объектами удаленной базы. Следует отметить, что работа с методами/свойствами Com объектов в 1С осуществляется через позднее связывание и требует значительных затрат. Поэтому, чем меньше вы делаете вызовов методов/свойств Com объекта - тем более быстро все работает.\\ Например, конструкция вида: Для i=1 по 1000 Цикл НовыйОбъект=Бухгалтерия.Справочники.Склады.СоздатьЭлемент(); НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор()); НовыйОбъект.ВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый; НовыйОбъект.Записать(); КонецЦикла; гораздо медленнее работает, чем вот такая: НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый; СправочникМенеджер=Бухгалтерия.Справочники.Склады; Для i=1 по 1000 Цикл НовыйОбъект=СправочникМенеджер.СоздатьЭлемент(); НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор()); НовыйОбъект.ВидСклада=НовыйВидСклада; НовыйОбъект.Записать(); КонецЦикла; ибо во втором случае используется гораздо меньше вызовов свойств Com-объектов. Ну и наконец решение для фанатов - основной код должен быть внутри конфигурации внешней базы, скажем в процедурах общего модуля, и вызываться из локальной базы в передачей параметров, например, как: НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый; Для i=1 по 1000 Цикл Бухгалтерия.СоздатьСклад(Строка(Новый УникальныйИдентификатор()),НовыйВидСклада); КонецЦикла; // здесь СоздатьСклад() - это функция общего модуля внешней базы: Функция СоздатьСклад(Наименование, ВидСклада) Экспорт НовыйОбъект=Справочники.Склады.СоздатьЭлемент(); НовыйОбъект.Наименование=Наименование; НовыйОбъект.ВидСклада=ВидСклада; НовыйОбъект.Записать(); КонецФункции Однако этот прием затруднительно использовать и отлаживать, кроме того он почти не имеет смысла для COMConnector-а. Таблица 3. Сводный отчет о производительности. Время записи 1000 элементов в секундах ^Тип соединения ^Простая ^Предопредел.СправочникМенеджер ^Внутренняя^ |ComConnector |5,12778 |2,49483 |2,29044| |Application |57,52807 |8,337808 |2,395056| ===== Часть два. Тонкости и трюки ===== ==== Глава один. Работа со значениями перечисления ==== Перечисления придуманы для того, чтобы обращаться к их значениям из кода через точку. Однако, во второй главе части первой написано про позднее связывание и как оно отражается на быстродействии. Для того, чтобы ускорить работу с перечислениями через COM, можно использовать следующий трюк. СоответствиеВидаСклада=Новый Соответствие; СоответствиеВидаСклада.Вставить(0,Перечисления.ВидыСкладов.Оптовый); СоответствиеВидаСклада.Вставить(1,Перечисления.ВидыСкладов.Розничный); СоответствиеВидаСклада.Вставить(2,Перечисления.ВидыСкладов.НеавтоматизированнаяТорговаяТочка); Запрос=Бухгалтерия.NewObject("Запрос"); Запрос.Текст= "ВЫБРАТЬ | Склады.Ссылка, | Склады.Наименование, | Склады.ВидСклада.Порядок как ПорядокВидаСклада |ИЗ | Справочник.Склады КАК Склады"; Выборка=Запрос.Выполнить().Выбрать(); Пока Выборка.Следующий() Цикл НовыйОбъект=Справочники.Склады.СоздатьЭлемент(); НовыйОбъект.Наименование=Выборка.Наименование; НовыйОбъект.ВидСклада=СоответствиеВидаСклада.Получить(Выборка.ПорядокВидаСклада); НовыйОбъект.Записать(); КонецЦикла; То есть из удаленной базы выбирается НЕ ССЫЛКА на элемент перечисления, которая возвратится как COM-объект, а его ПОРЯДКОВЫЙ НОМЕР в конфигурации, который также позволяет однозначно идентифицировать значение перечисления. Ну а конструкция: СоответствиеВидаСклада=Новый Соответствие; СоответствиеВидаСклада.Вставить(0,Перечисления.ВидыСкладов.Оптовый); СоответствиеВидаСклада.Вставить(1,Перечисления.ВидыСкладов.Розничный); СоответствиеВидаСклада.Вставить(2,Перечисления.ВидыСкладов.НеавтоматизированнаяТорговаяТочка); устанавливает соответстивие между порядковым номером перечисления в удаленной базе и его значением в локальной базе. Сделана эта конструкция для удобства кода. ==== Глава два. Если что-то хочется, но нельзя, то немного можно.. Но не со мной.==== Вот есть у вас задача сделать обмен через COM. И будут там обмениваться ну скажем... Поступления. Что я делаю в первую очередь? А в первую очередь я в модуле обработки прописываю функции\\ ПолучитьНоменклатуру()\\ ПолучитьСклад()\\ ПолучитьКонтрагента()\\ вот так. Разберем теперь код следующего вида: Перем СоответствиеВидовСкладов Экспорт; Перем ЗапросВнешнейБазы Экспорт; Перем ВнешняяБаза Экспорт; Процедура Подключиться() Экспорт ВнешняяБаза=Новый COMОбъект("V81.COMConnector"); Попытка ВнешняяБаза=ВнешняяБаза.Connect(База.СтрокаПодключения); Исключение Предупреждение("Ошибка открытия базы!!!"); Сообщить(ОписаниеОшибки()); ВнешняяБаза=Неопределено; Возврат; КонецПопытки; ЗапросВнешнейБазы=ВнешняяБаза.NewObject("Запрос"); СоответствиеВидовСкладов=Новый Соответствие; СоответствиеВидовСкладов.Вставить(0,Перечисления.ВидыСкладов.Оптовый); СоответствиеВидовСкладов.Вставить(1,Перечисления.ВидыСкладов.Розничный); СоответствиеВидовСкладов.Вставить(2,Перечисления.ВидыСкладов.НеавтоматизированнаяТорговаяТочка); КонецПроцедуры Функция ПолучитьСклад(КодВнешнейБазы,ПолучатьГруппу=Ложь)Экспорт Если (КодВнешнейБазы=Неопределено)ИЛИ(КодВнешнейБазы=NULL)ИЛИ(КодВнешнейБазы="") Тогда Возврат Справочники.Склады.ПустаяСсылка(); КонецЕсли; Склад=Справочники.Склады.НайтиПоРеквизиту("КодВнешнейБазы",КодВнешнейБазы); Если Не Склад.Пустая() Тогда Возврат Склад; //Он создан уже раньше, вернем ссылку на него Иначе Если ПолучатьГруппу Тогда ЗапросВнешнейБазы.Текст= "ВЫБРАТЬ | Склады.Наименование, | Склады.Родитель.Код |ИЗ | Справочник.Склады КАК Склады |ГДЕ | Склады.Код = &Код"; ЗапросВнешнейБазы.УстановитьПараметр("Код",КодВнешнейБазы); Выборка=ЗапросВнешнейБазы.Выполнить().Выбрать(); Выборка.Следующий(); Склад=Справочники.Склады.СоздатьГруппу(); Склад.Наименование=Выборка.Наименование; Склад.КодВнешнейБазы=КодВнешнейБазы; Склад.Родитель=ПолучитьСклад(Выборка.РодительКод,Истина); Склад.Записать(); Иначе ЗапросВнешнейБазы.Текст= "ВЫБРАТЬ | Склады.Наименование, | Склады.ВидСклада.Порядок, | Склады.Родитель.Код |ИЗ | Справочник.Склады КАК Склады |ГДЕ | Склады.Код = &Код"; ЗапросВнешнейБазы.УстановитьПараметр("Код",КодВнешнейБазы); Выборка=ЗапросВнешнейБазы.Выполнить().Выбрать(); Выборка.Следующий(); Склад=Справочники.Склады.СоздатьЭлемент(); Склад.Наименование=Выборка.Наименование; Склад.ВидСклада=СоответствиеВидовСкладов.Получить(Выборка.ВидСкладаПорядок); Склад.КодВнешнейБазы=КодВнешнейБазы; Склад.Комментарий="Создан "+ТекущаяДата()+" во время выгрузки"; Склад.Родитель=ПолучитьСклад(Выборка.РодительКод,Истина); Склад.Записать(); КонецЕсли; Возврат Склад.Ссылка; КонецЕсли; КонецФункции Первым делом мы подключаемся к удаленной базе и сразу создаем объект запроса в удаленной базе (“Для сильных духом”). Затем создаем соответствие для перечисления по порядковому номеру. Все это храним в экспортных переменных модуля объекта. \\ Допускаем, что синхронизация осуществляется по коду объекта удаленной базы, который хранится в реквизите объекта локальной базы (не забывайте про длину реквизита, иначе будут дубли!).\\ Ну а функция ПолучитьСклад() – примитивна, разбирайтесь товарищи сами. Плюсы: - Быстродействие - Формирование полной иерархии - Возможность быстро получить элемент, например, при перегрузке документа. {{tag>COM OLE быстродействие производительность значение_перечисления подключить_удаленную_базу}}