Как найти документ через SQLite в 1С 7.7, если реквизит имеет тип Неопределенный

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При работе с внешними компонентами для 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-текст. Рассмотрим их подробнее, чтобы выбрать правильный для нашей ситуации.

Пусть :Основание — это наш параметр. Посмотрим, как меняется результат преобразования в зависимости от количества символов ~ или использования *:

  1. :Основание (без модификаторов) — преобразуется во внутренний идентификатор (ID) длиной 9 символов. Подходит для поиска по реквизитам с жестко заданным типом (например, только справочник конкретного вида).
  2. :Основание~ (одна тильда) — возвращает ID + Вид (13 символов). Это стандарт для типизированных полей типа «Документ» или «Справочник» в SQL-таблицах 1С.
  3. :Основание~~ (две тильды) — возвращает только 4-символьный код вида объекта.
  4. :Основание~~~ (три тильды) — формирует позицию документа в формате date_time_iddoc (23 символа), что используется в журналах и таблицах итогов.
  5. :Основание* (звездочка или модификатор -1) — именно то, что нам нужно! Значение преобразуется в полную 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-версиях баз данных, что является огромным преимуществом для поддержки старых конфигураций.

← На главную