При работе с внешними компонентами для 1С 7.7, такими как 1С++, программисты часто сталкиваются с необходимостью выполнения высокопроизводительных запросов к базе данных с использованием SQLite (движок dbeng). Одна из самых коварных проблем возникает при попытке найти объект по реквизиту, имеющему тип Неопределенный. В этой статье мы подробно разберем, как устроено хранение таких данных и какие инструменты предоставляет 1С++ для решения этой задачи.
Рассмотрим типичную ситуацию: в документе есть реквизит Основание с типом Неопределенный. В нем может храниться ссылка как на другой документ, так и на элемент справочника. При попытке выполнить прямой SQL-запрос через SQLite, передав туда объект 1С, мы часто получаем пустой результат, хотя визуально данные в базе присутствуют.
Проанализируем пример кода, который изначально не давал результата:
запрос.ВыполнитьЗапрос("create virtual table ДопДост using dbeng(Документ.ДоставкаДоп)");
запрос.Подставлять("ТекЭл", ТекущийЭлемент());
текст = "SELECT * FROM ДопДост WHERE Основание = :ТекЭл;";
тз = запрос.ВыполнитьЗапрос(текст);
Причина неудачи кроется в том, как 1С 7.7 хранит значения неопределенного типа на физическом уровне. В отличие от обычных ссылок, которые занимают 9 или 13 символов, значение типа Неопределенный представляет собой строку длиной 23 символа со специфическим префиксом.
Для того чтобы успешно строить запросы, нам нужно понимать, что именно SQLite «видит» в колонке Основание. Экспериментальным путем и через анализ структуры БД выяснено, что строка начинается с двухсимвольного префикса, определяющего базовый тип объекта:
Полная структура 23-символьной строки выглядит следующим образом: [ПрефиксТипа(2)][IDВида(4)][IDОбъекта(9)][Дополнение(8)]. Если мы передаем в запрос просто идентификатор объекта (например, 5Y4 2Q), SQLite сравнивает его со строкой B1 5Y4 2Q... и закономерно не находит совпадений.
Компонента 1С++ предоставляет мощный механизм модификаторов при использовании метода Подставлять(). Эти модификаторы управляют тем, в какой именно строковый вид будет преобразован объект 1С перед попаданием в SQL-текст. Рассмотрим их подробнее, чтобы выбрать правильный для нашей ситуации.
Пусть :Основание — это наш параметр. Посмотрим, как меняется результат преобразования в зависимости от количества символов ~ или использования *:
date_time_iddoc (23 символа), что используется в журналах и таблицах итогов.O1... или B1...).Выяснив, что модификатор * является ключом к решению задачи, перепишем наш запрос. Теперь он будет работать корректно для любых объектов, хранящихся в поле типа Неопределенный.
// Создаем виртуальную таблицу для доступа к данным документа
запрос.ВыполнитьЗапрос("create virtual table ШапкаДопДост using dbeng(Документ.ДоставкаДоп)");
// Подставляем элемент справочника, который является основанием
// Модификатор '*' автоматически добавит префикс 'B1' и нужную длину
запрос.Подставлять("ТекЭл", ТекущийЭлемент());
// Формируем текст запроса с использованием модификатора звездочка
текст = "SELECT * FROM ШапкаДопДост WHERE Основание = :ТекЭл*";
// Выполняем запрос и получаем результат в таблицу значений
тз = запрос.ВыполнитьЗапрос(текст);
Если тз.КоличествоСтрок() > 0 Тогда
Сообщить("Найдено документов: " + тз.КоличествоСтрок());
КонецЕсли;
Разберем ситуации, с которыми сталкивались разработчики до того, как был найден правильный модификатор. Это поможет лучше понять механику процесса.
Вариант с ручной конкатенацией префикса:
Иногда программисты пытаются вручную добавить B1 или O1 в тексте запроса. Это выглядит так:
текст = "SELECT * FROM ДопДост WHERE Основание = 'B1' || :ТекЭл~";
Хотя такой подход может сработать, он крайне нежелателен, так как завязывается на жесткие константы и требует написания условий (если справочник — добавляем B1, если документ — O1). Использование :Параметр* делает эту работу автоматически за вас на уровне движка 1С++.
Штатный метод 1С:
Если производительность SQLite в данном конкретном случае не критична, можно воспользоваться методом ВыбратьПоЗначению. На уровне ядра 1С сама умеет правильно обрабатывать типы:
Док = СоздатьОбъект("Документ.ДоставкаДоп");
Если Док.ВыбратьПоЗначению(,,"Основание", ТекущийЭлемент()) = 1 Тогда
Пока Док.ПолучитьДокумент() = 1 Цикл
// Обработка документа
КонецЦикла;
КонецЕсли;
Однако, если вам нужно соединить данные из нескольких таблиц или сделать сложную фильтрацию, SQLite остается незаменимым инструментом.
Мы проанализировали специфику работы с типом Неопределенный в среде 1С 7.7 при использовании SQLite. Главный вывод: всегда используйте модификатор * при передаче параметров в запрос, если поле в базе данных имеет длину 23 символа и неопределенный тип. Это обеспечит корректную подстановку префиксов типа и позволит избежать «фантомных» пустых результатов запроса.
Также помните, что использование virtual table ... using dbeng позволяет вам работать с данными 1С в режиме «read-only», используя мощь SQL-синтаксиса, при этом сохраняя работоспособность на DBF-версиях баз данных, что является огромным преимуществом для поддержки старых конфигураций.