Книга знаний

1С:Предприятие / v8 / Приемы программирования / OLE, COM

v8: Охота на Com-ов

Оптимизация работы с COM. Сравнительный анализ различных методов.Автор статьи: H A D G E H O G s | Редакторы:
Последняя редакция №10 от 25.06.08 | История
URL: http://kb.mista.ru/article.php?id=678

Ключевые слова: COM, OLE, значение перечисления, подключить удаленную базу.


Часть первая. Жажда скорости.

Глава первая. Широкозакрытые глаза.


Чтение. Начнем с очевидных вещей. Первым шагом для существенного повышения скорости работы чтения через Внешнее Соединение считаю полный отказ от использования выборок/ссылок внешней базы.
Про конструкции вида
Выборка=V8.Справочники.Склады.Выбрать();
Пока Выборка.Следующий() Цикл
КонецЦикла;
советую вспоминать только в страшных ночных кошмарах. Ибо при обращении к любому реквизиту, не принадлежащему к основным данным объекта произойдет чтение всех реквизитов объекта. Если мы попытаемся получить реквизит основных данных - то произойдет чтение только основных данных, что тоже не хорошо. Использование же запроса позволит читать только то, что вам действительно нужно. Скажем так:
Запрос=V8.NewObject("Запрос");
Запрос.Текст=
"ВЫБРАТЬ
|    Склады.Код,
|    Склады.Наименование,
|    Склады.ВидСклада.Порядок
|ИЗ
|    Справочник.Склады КАК Склады";
Выборка=Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл

КонецЦикла;
позволит вам прочесть только то, что нужно... Также хочу вам сказать, что просто забудьте о таких запросах, как:
Запрос=Бухгалтерия.NewObject("Запрос");
Запрос.Текст=
"ВЫБРАТЬ
|    Склады.Ссылка, //Вот здесь оно, это поле
|    Склады.Код,
|    Склады.Наименование,
|    Склады.ВидСклада.Порядок
|ИЗ
|    Справочник.Склады КАК Склады";
Выборка=Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл

КонецЦикла;
ибо ссылка на объект внешней базу в локальной базе БЕСПОЛЕЗНА (в большинстве случаев). Наличие этой ссылки может приводить к конструкции вида:
Пока Выборка.Следующий() Цикл
НашОбъект=ПоискПоКоду(Выборка.Ссылка.Код);
КонецЦикла;
которая циклично читает объект, а значит сводит на нет весь смысл использование запроса. Поверьте моему опыту, вы этого и не заметите, пока вам бухгалтер не скажет, что - же так долго выгрузка ползет...
Также не рекомендую использовать конструкцию вида:
Пока Выборка.Следующий() Цикл
Сообщить("Производится выгрузка склада "+V8.String(Выборка.Ссылка));
КонецЦикла;
И хотя данный код меннее критичен (считываются только основные данные объекта), лучше использовать функцию ПРЕДСТАВЛЕНИЕ()/ПРЕДСТАВЛЕНИЕССЫЛКИ() в запросе, или на крайняк Наименование вытащите. Есть и еще одна причина, о которой расскажу чуть позже.
Хочу отметить, что указанные особенности справедливы не только для Внешнего Соединения, но и для локальных баз. Не пренебрегайте ими. Дабы не быть голословным, приведу тесты на скорость/выносливость/быстроту в таблице1.

Время выборки 10000 элементов в секундах
Тип соединенияВыборкаЗапрос
ComConnector0,5834120,192428
Application1,1787830,782243

Запись. Ну что тут сказать.. Тут нечего сказать. Запросы не используются для записи. А то ежели дала бы 1С одинесникам INSERT, DELETE, UPDATE, CREATE TABLE, то про "Доступно и Всерьез" можно было бы только мечтать, глядя, как одинесники UPDATE-тят виртуальные таблицы.... Единственное, что могу посоветовать - это использование транзакций. Не могу теоритически обосновать, почему так, это просто эмпирика. Конструкция вида:
НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый;
СправочникМенеджер=Бухгалтерия.Справочники.Склады;
Бухгалтерия.НачатьТранзакцию();
Для i=1 по 1000 Цикл
НовыйОбъект=СправочникМенеджер.СоздатьЭлемент();
НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор());
НовыйОбъект.ВидСклада=НовыйВидСклада;
НовыйОбъект.Записать();
КонецЦикла;
Бухгалтерия.ЗафиксироватьТранзакцию();
будет производительней, чем
НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый;
СправочникМенеджер=Бухгалтерия.Справочники.Склады;
Для i=1 по 1000 Цикл
НовыйОбъект=СправочникМенеджер.СоздатьЭлемент();
НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор());
НовыйОбъект.ВидСклада=НовыйВидСклада;
НовыйОбъект.Записать();
КонецЦикла;
Смотрим таблицу, короче

Время записи 1000 элементов в секундах
Тип соединенияПростаяТранзакция
ComConnector2,5948161,558324
Application8,4828527,455433

Глава два. Для сильных духом мужчин.


Речь пойдет про методику оптимизации работы с объектами удаленной базы. Отмечу, что работа с методами/свойствами Com объектов в 1С осуществляется через позднее связывание и требует значительных затрат. Поэтому, чем меньше вы делаете вызовов методов/свойств Com объекта - тем более быстро все работает. Например, конструкция вида:
Для i=1 по 1000 Цикл
НовыйОбъект=Бухгалтерия.Справочники.Склады.СоздатьЭлемент();
НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор());
НовыйОбъект.ВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый;
НовыйОбъект.Записать();
КонецЦикла;
гораздо менее оптимальна, чем конструкция вида:
НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый;
СправочникМенеджер=Бухгалтерия.Справочники.Склады;
Для i=1 по 1000 Цикл
НовыйОбъект=СправочникМенеджер.СоздатьЭлемент();
НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор());
НовыйОбъект.ВидСклада=НовыйВидСклада;
НовыйОбъект.Записать();
КонецЦикла;
ибо во втором случае используется гораздо меньше вызовов свойств Com-объектов. Ну и наконец решение для фанатов - основной код должен быть внутри конфигурации внешней базы, скажем в процедурах общего модуля, и вызываться из локальной базы в передачей параметров, например, как:
НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый;
Для i=1 по 1000 Цикл
Бухгалтерия.СоздатьСклад(Строка(Новый УникальныйИдентификатор()),НовыйВидСклада);
КонецЦикла;
где
Функция СоздатьСклад(Наименование,ВидСклада) Экспорт
НовыйОбъект=Справочники.Склады.СоздатьЭлемент();
НовыйОбъект.Наименование=Наименование;
НовыйОбъект.ВидСклада=ВидСклада;
НовыйОбъект.Записать();
КонецФункции
это функция общего модуля внешней базы...
Однако этот прием затруднительно использовать и отлаживать, кроме того он почти не имеет смысла для COMConnector-а.
Сводный отчет о производительности представлен в таблице 3.

Время записи 1000 элементов в секундах
Тип соединенияПростаяПредопредел.СправочникМенеджерВнутренняя
ComConnector5,127782,494832,29044
Application57,528078,3378082,395056

Часть два. Шозанах???


Глава один. Все то, о чем вы так долго мечтали, но не решались спросить.


Конечно перечисление. Как же его получить? Просто, как две кнопочки в Дельфишнике на форму набросать... Конструкцией вида:
СоответствиеВидаСклада=Новый Соответствие;
СоответствиеВидаСклада.Вставить(0,Перечисления.ВидыСкладов.Оптовый);
СоответствиеВидаСклада.Вставить(1,Перечисления.ВидыСкладов.Розничный);
СоответствиеВидаСклада.Вставить(2,Перечисления.ВидыСкладов.НеавтоматизированнаяТорговаяТочка);
Запрос=Бухгалтерия.NewObject("Запрос");
Запрос.Текст=
"ВЫБРАТЬ
|    Склады.Ссылка,
|    Склады.Наименование,
|    Склады.ВидСклада.Порядок как ПорядокВидаСклада
|ИЗ
|    Справочник.Склады КАК Склады";
Выборка=Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НовыйОбъект=Справочники.Склады.СоздатьЭлемент();
НовыйОбъект.Наименование=Выборка.Наименование;
НовыйОбъект.ВидСклада=СоответствиеВидаСклада.Получить(Выборка.ПорядокВидаСклада);
НовыйОбъект.Записать();
КонецЦикла;
получаем следующее:
Мы выбираем из удаленной базы НЕ ССЫЛКУ на элемент перечисления, которая возвратится как COM-объект, а его ПОРЯДКОВЫЙ НОМЕР в конфигурации, который также позволяет однозначно идентифицировать значение перечисления. Ну а конструкция
СоответствиеВидаСклада=Новый Соответствие;
СоответствиеВидаСклада.Вставить(0,Перечисления.ВидыСкладов.Оптовый);
СоответствиеВидаСклада.Вставить(1,Перечисления.ВидыСкладов.Розничный);
СоответствиеВидаСклада.Вставить(2,Перечисления.ВидыСкладов.НеавтоматизированнаяТорговаяТочка);
устанавливает соответстивие между порядковым номером перечисления в удаленной базе и его значением в локальной базе. Сделана эта конструкция для удобства кода... :-)
Ах да, еще же есть Метаданные() у COM-объекта Перечисления. Так вот, извращенцы идут лесом, ибо смотрим главу "Для сильных духом мужчин". Даже писать про Метаданные() не буду...

Глава два. Если что-то хочется, но нельзя, то немного можно.. Но не со мной.


Вот есть у вас задача сделать обмен через COM. И будут там обмениваться ну скажем... Поступления. Что я делаю в первую очередь? А в первую очередь я в модуле обработки прописываю функции
ПолучитьНоменклатуру()
ПолучитьСклад()
ПолучитьКонтрагента()
вот так.
Разберем теперь код следующего вида:
Перем СоответствиеВидовСкладов Экспорт;
Перем ЗапросВнешнейБазы Экспорт;
Перем ВнешняяБаза Экспорт;

Процедура Подключиться()  Экспорт
    ВнешняяБаза=Новый COMОбъект("V81.COMConnector");
    Попытка
        ВнешняяБаза=ВнешняяБаза.Connect(База.СтрокаПодключения);
    Исключение
        Предупреждение("Ошибка открытия базы!!!");
        Сообщить(ОписаниеОшибки());
        ВнешняяБаза=Неопределено;
        Возврат;
    КонецПопытки;
    ЗапросВнешнейБазы=ВнешняяБаза.NewObject("Запрос");
    СоответствиеВидовСкладов=Новый Соответствие;
    СоответствиеВидовСкладов.Вставить(0,Перечисления.ВидыСкладов.Оптовый);
    СоответствиеВидовСкладов.Вставить(1,Перечисления.ВидыСкладов.Розничный);
    СоответствиеВидовСкладов.Вставить(2,Перечисления.ВидыСкладов.НеавтоматизированнаяТорговаяТочка);
КонецПроцедуры

Функция ПолучитьСклад(КодВнешнейБазы,ПолучатьГруппу=Ложь)Экспорт
    Если (КодВнешнейБазы=Неопределено)ИЛИ(КодВнешнейБазы=NULL)ИЛИ(КодВнешнейБазы="") Тогда
        Возврат Справочники.Склады.ПустаяСсылка();
    КонецЕсли;
    Склад=Справочники.Склады.НайтиПоРеквизиту("КодВнешнейБазы",КодВнешнейБазы);
    Если Не Склад.Пустая() Тогда
        Возврат Склад; //Он создан уже раньше, вернем ссылку на него
    Иначе
        Если ПолучатьГруппу Тогда
            ЗапросВнешнейБазы.Текст=
            "ВЫБРАТЬ
            |    Склады.Наименование,
            |    Склады.Родитель.Код
            |ИЗ
            |    Справочник.Склады КАК Склады
            |ГДЕ
            |    Склады.Код = &Код";
            ЗапросВнешнейБазы.УстановитьПараметр("Код",КодВнешнейБазы);
            Выборка=ЗапросВнешнейБазы.Выполнить().Выбрать();
            Выборка.Следующий();
            Склад=Справочники.Склады.СоздатьГруппу();
            Склад.Наименование=Выборка.Наименование;
            Склад.КодВнешнейБазы=КодВнешнейБазы;
            Склад.Родитель=ПолучитьСклад(Выборка.РодительКод,Истина);
            Склад.Записать();
        Иначе
            ЗапросВнешнейБазы.Текст=
            "ВЫБРАТЬ
            |    Склады.Наименование,
            |    Склады.ВидСклада.Порядок,
            |    Склады.Родитель.Код
            |ИЗ
            |    Справочник.Склады КАК Склады
            |ГДЕ
            |    Склады.Код = &Код";
            ЗапросВнешнейБазы.УстановитьПараметр("Код",КодВнешнейБазы);
            Выборка=ЗапросВнешнейБазы.Выполнить().Выбрать();
            Выборка.Следующий();
            Склад=Справочники.Склады.СоздатьЭлемент();
            Склад.Наименование=Выборка.Наименование;
            Склад.ВидСклада=СоответствиеВидовСкладов.Получить(Выборка.ВидСкладаПорядок);
            Склад.КодВнешнейБазы=КодВнешнейБазы;
            Склад.Комментарий="Создан "+ТекущаяДата()+" во время выгрузки";
            Склад.Родитель=ПолучитьСклад(Выборка.РодительКод,Истина);
            Склад.Записать();
        КонецЕсли;
        Возврат Склад.Ссылка;
    КонецЕсли;
КонецФункции
Первым делом мы подключаемся к удаленной базе и сразу создаем объект запроса в удаленной базе (“Для сильных духом”). Затем создаем соответствие для перечисления по порядковому номеру. Все это храним в экспортных переменных модуля объекта.
   Допускаем, что синхронизация осуществляется по коду объекта удаленной базы, который хранится в реквизите объекта локальной базы (не забывайте про длину реквизита, иначе будут дубли!).
   Ну а функция ПолучитьСклад() – примитивна, разбирайтесь товарищи сами.
Плюсы:
1)    Быстродействие
2)    Формирование полной иерархии
3)    Возможность быстро получить элемент, например, при перегрузке документа.

Дальше будет статья про Сомов, позднее вязание, и как это все варится.. Никакой практики, так, теория и мои мысли.. Ждем-с.
P.S.
Тут еще графики были, но их не разместишь.
P.S.S. Тесты делал правильно, базу очищал, перезапускал.. База файловая.
P.S.S.S. Кто может научить форматировать таблицы на этом тарабарском HTML - напишите..

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2025 | Mista.ru

Яндекс.Метрика