v8: Охота на Com-ов Ключевые слова: COM, OLE, значение перечисления, подключить удаленную базу.
 
 
 Часть первая. Жажда скорости.Глава первая. Широкозакрытые глаза.
 Чтение. Начнем с очевидных вещей. Первым шагом для существенного повышения скорости работы чтения через Внешнее Соединение считаю полный отказ от использования выборок/ссылок внешней базы.
 Про конструкции вида
 Выборка=V8.Справочники.Склады.Выбрать();
Пока Выборка.Следующий() Цикл
КонецЦикла;
 советую вспоминать только в страшных ночных кошмарах. Ибо при обращении к любому реквизиту, не принадлежащему к основным данным объекта произойдет чтение всех реквизитов объекта. Если мы попытаемся получить реквизит основных данных - то произойдет чтение только основных данных, что тоже не хорошо. Использование же запроса позволит читать только то, что вам действительно нужно. Скажем так:
 Запрос=V8.NewObject("Запрос");
Запрос.Текст=
"ВЫБРАТЬ
|    Склады.Код,
|    Склады.Наименование,
|    Склады.ВидСклада.Порядок
|ИЗ
|    Справочник.Склады КАК Склады";
Выборка=Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
КонецЦикла;
позволит вам прочесть только то, что нужно... Также хочу вам сказать, что просто забудьте о таких запросах, как:
 Запрос=Бухгалтерия.NewObject("Запрос");
Запрос.Текст=
"ВЫБРАТЬ
|    Склады.Ссылка, //Вот здесь оно, это поле
|    Склады.Код,
|    Склады.Наименование,
|    Склады.ВидСклада.Порядок
|ИЗ
|    Справочник.Склады КАК Склады";
Выборка=Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
КонецЦикла;
ибо ссылка на объект внешней базу в локальной базе БЕСПОЛЕЗНА (в большинстве случаев). Наличие этой ссылки может приводить к конструкции вида:
 Пока Выборка.Следующий() Цикл
НашОбъект=ПоискПоКоду(Выборка.Ссылка.Код);
КонецЦикла;
 которая циклично читает объект, а значит сводит на нет весь смысл использование запроса. Поверьте моему опыту, вы этого и не заметите, пока вам бухгалтер не скажет, что - же так долго выгрузка ползет...
 Также не рекомендую использовать конструкцию вида:
 Пока Выборка.Следующий() Цикл
Сообщить("Производится выгрузка склада "+V8.String(Выборка.Ссылка));
КонецЦикла;
И хотя данный код меннее критичен (считываются только основные данные объекта), лучше использовать функцию ПРЕДСТАВЛЕНИЕ()/ПРЕДСТАВЛЕНИЕССЫЛКИ() в запросе, или на крайняк Наименование вытащите. Есть и еще одна причина, о которой расскажу чуть позже.
 Хочу отметить, что указанные особенности справедливы не только для Внешнего Соединения, но и для локальных баз. Не пренебрегайте ими. Дабы не быть голословным, приведу тесты на скорость/выносливость/быстроту в таблице1.
 | Время выборки 10000 элементов в секундах | | Тип соединения | Выборка | Запрос |  | ComConnector | 0,583412 | 0,192428 |  | Application | 1,178783 | 0,782243 |  
  
 Запись. Ну что тут сказать.. Тут нечего сказать. Запросы не используются для записи. А то ежели дала бы 1С одинесникам INSERT, DELETE, UPDATE, CREATE TABLE, то про "Доступно и Всерьез" можно было бы только мечтать, глядя, как одинесники UPDATE-тят виртуальные таблицы.... Единственное, что могу посоветовать - это использование транзакций. Не могу теоритически обосновать, почему так, это просто эмпирика. Конструкция вида:
 НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый;
СправочникМенеджер=Бухгалтерия.Справочники.Склады;
Бухгалтерия.НачатьТранзакцию();
Для i=1 по 1000 Цикл
НовыйОбъект=СправочникМенеджер.СоздатьЭлемент();
НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор());
НовыйОбъект.ВидСклада=НовыйВидСклада;
НовыйОбъект.Записать();
КонецЦикла;
Бухгалтерия.ЗафиксироватьТранзакцию();
 будет производительней, чем
 НовыйВидСклада=Бухгалтерия.Перечисления.ВидыСкладов.Оптовый;
СправочникМенеджер=Бухгалтерия.Справочники.Склады;
Для i=1 по 1000 Цикл
НовыйОбъект=СправочникМенеджер.СоздатьЭлемент();
НовыйОбъект.Наименование=Строка(Новый УникальныйИдентификатор());
НовыйОбъект.ВидСклада=НовыйВидСклада;
НовыйОбъект.Записать();
КонецЦикла;
 Смотрим таблицу, короче
 | Время записи 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 |  
 
 Часть два. Шозанах???
 Глава один. Все то, о чем вы так долго мечтали, но не решались спросить.
 Конечно перечисление. Как же его получить? Просто, как две кнопочки в Дельфишнике на форму набросать... Конструкцией вида:
 СоответствиеВидаСклада=Новый Соответствие;
СоответствиеВидаСклада.Вставить(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 - напишите..
  |