Как решить проблему с получением NULL в запросах через COM-соединение в 1С?

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При интеграции различных баз данных 1С программисты часто прибегают к использованию COMОбъект (COM-соединение). Это проверенный временем инструмент, однако он таит в себе множество нюансов, связанных с передачей данных между процессами (маршалингом). Одной из самых коварных проблем является некорректная интерпретация значения NULL, когда при выполнении сложного запроса с объединениями к удаленной базе вместо ожидаемых чисел приходят пустые значения. В этой статье мы подробно разберем, почему так происходит и как правильно составить запрос, чтобы данные передавались без потерь.

Анализ проблемы: почему данные превращаются в NULL

Рассмотрим ситуацию: нам необходимо выгрузить из удаленной базы сальдо и обороты по контрагентам. Мы используем запрос к регистру бухгалтерии Хозрасчетный, соединяя таблицы остатков и оборотов. Когда нам требуется добавить еще одну таблицу (например, остатки по счету 10.07 "Материалы, переданные в переработку") через оператор ОБЪЕДИНИТЬ ВСЕ, мы можем столкнуться с тем, что в результирующей выборке на стороне вызывающей базы колонки новой таблицы оказываются пустыми (содержат NULL).

Проблема заключается в том, что COM-соединение чувствительно к типам данных в колонках результата запроса. Если в одной части объединения колонка содержит число, а в другой — NULL, то при передаче этой выборки через COM-интерфейс механизм преобразования типов может дать сбой. Для 1С внутри одной базы это нормальная ситуация, но для внешнего соединения это создает неопределенность.

Разбор типичного «неправильного» запроса

Проанализируем фрагмент кода, который часто приводит к ошибкам. Обратите внимание на использование NULL в качестве заглушек для колонок во второй части объединения:


Запрос = Connection.NewObject("Запрос");
Текст = 
"ВЫБРАТЬ
|    ЕСТЬNULL(ХозрасчетныйОстатки.СуммаОстатокДт, 0) КАК НамДолжны,
|    NULL КАК Сальдо_1007
|ИЗ
|    РегистрБухгалтерии.Хозрасчетный.Остатки(&НаДату, Счет В ИЕРАРХИИ (&СписокСчетов), , ) КАК ХозрасчетныйОстатки
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|    NULL,
|    ЕСТЬNULL(Хозрасчетный_1007.СуммаОстатокДт, 0)
|ИЗ
|    РегистрБухгалтерии.Хозрасчетный.Остатки(&НаДату, Счет = &Счет_1007, , ) КАК Хозрасчетный_1007";

В данном примере мы пытаемся в первой части запроса зарезервировать место под поле Сальдо_1007 с помощью литерала NULL. На стороне вызывающей базы при обходе выборки через Выборка.Следующий() обращение к Выборка.Сальдо_1007 может вернуть NULL даже для тех строк, где во второй части запроса явно есть число. Это происходит из-за того, что колонка не получила жесткой типизации «число» в первой части объединения.

Решение проблемы: явная типизация через замену NULL на 0

Чтобы гарантировать корректную передачу данных через COM, мы должны привести все числовые показатели к числу в каждой части объединения. Вместо NULL используем 0. Это позволит COM-обертке однозначно определить тип колонки как числовой.

Рассмотрим правильный вариант формирования текста запроса:


Текст = 
"ВЫБРАТЬ
|    Хозрасчетный_1007.Субконто1.Наименование КАК КонтрагентНаименование,
|    Хозрасчетный_1007.Субконто1.Родитель.Наименование КАК РодительНаименование,
|    Хозрасчетный_1007.Субконто1.ИНН КАК ИНН,
|    0 КАК НамДолжны,
|    0 КАК МыДолжны,
|    0 КАК Оплата,
|    ЕСТЬNULL(Хозрасчетный_1007.СуммаОстатокДт, 0) КАК Сальдо_1007
|ИЗ
|    РегистрБухгалтерии.Хозрасчетный.Остатки(&НаДату, Счет = &Счет_1007, , ) КАК Хозрасчетный_1007";

Заменив NULL на 0, мы избавляемся от неопределенности типа. Теперь при объединении с основной таблицей (где эти поля также числовые) выборка будет иметь четкую структуру, понятную для COM-соединения.

Оптимизация работы с данными через точку

При работе через COM каждое обращение к свойствам объекта «через точку» (например, Выборка.Контрагент.Наименование) инициирует отдельный вызов к удаленной базе. Если в вашей выборке тысячи строк, это приведет к катастрофическому замедлению работы.

Рекомендуется:

  1. Получать все строковые представления (Наименование, ИНН, Код) непосредственно в тексте запроса.
  2. Использовать функцию ПРЕДСТАВЛЕНИЕ() для ссылочных полей.
  3. Если нужно сопоставить объекты, передавать их уникальные идентификаторы (GUID).

Пример получения ссылок и идентификаторов через COM

Если нам все же необходимо работать со ссылками, проанализируем вспомогательные функции, которые помогут правильно обработать данные на принимающей стороне. Рассмотрим метод получения уникального идентификатора объекта из удаленной базы:


Функция ПолучитьГУИД_ЭлементаCOM(COM_Ссылка, БазаИсточник)
    Попытка
        // Преобразуем УникальныйИдентификатор в строку на стороне источника
        Возврат Строка(БазаИсточник.String(COM_Ссылка.УникальныйИдентификатор()));
    Исключение
        Возврат "";
    КонецПопытки;
КонецФункции

Имея строковый ГУИД, мы можем найти соответствующий объект в нашей локальной базе. Это гораздо надежнее и быстрее, чем пытаться передать саму ссылку через COM и сравнивать её напрямую.

Поиск контрагента по ИНН и КПП при работе с COM

Часто задача сводится к тому, чтобы найти контрагента в текущей базе по данным из удаленной. Посмотрим на алгоритм, который минимизирует ошибки сопоставления:


Функция НайтиКонтрагентаПоДаннымCOM(COM_Контрагент)
    ИНН = СокрЛП(COM_Контрагент.ИНН);
    КПП = СокрЛП(COM_Контрагент.КПП);
    
    Если ПустаяСтрока(ИНН) Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("ИНН", ИНН);
    Запрос.УстановитьПараметр("КПП", КПП);
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    Контрагенты.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    |    Контрагенты.ИНН = &ИНН
    |    И Контрагенты.КПП = &КПП";
    
    Результат = Запрос.Выполнить();
    Если Результат.Пустой() Тогда 
        Возврат Неопределено;
    КонецЕсли;
    
    Выборка = Результат.Выбрать();
    Выборка.Следующий();
    Возврат Выборка.Ссылка;
КонецФункции

Итоговые рекомендации

Подведем итог нашему исследованию работы с COM-запросами:

  1. Всегда типизируйте колонки: В запросах с ОБЪЕДИНИТЬ ВСЕ заменяйте NULL на 0 для чисел и на пустую строку для текстовых полей.
  2. Минимизируйте обращения через точку: Вытаскивайте Наименование и ИНН сразу в запросе. Помните, что Субконто1.Наименование в запросе — это хорошо, а Выборка.Субконто1.Наименование в цикле — это очень медленно.
  3. Используйте примитивные типы: Передавайте через границу COM строки, числа и даты. Ссылки лучше преобразовывать в ГУИД (строку).
  4. Проверяйте параметры: При установке параметров запроса через COM (например, Запрос.УстановитьПараметр("Счет", ...)), убедитесь, что объект «Счет» получен именно из той же Connection, в которой выполняется запрос.

Соблюдение этих правил позволит вам создавать стабильные и быстрые механизмы интеграции, которые не будут «терять» данные при передаче из одной базы 1С в другую.

← На главную