Как в 1С 7.7 при использовании SQLiteBase (1sqlite) связать таблицы по разноформатным идентификаторам документов

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

При разработке прямых запросов к базе данных 1С:Предприятие 7.7 с использованием замечательной внешней компоненты 1sqlite (SQLiteBase) разработчики часто сталкиваются с проблемой несовместимости форматов хранения ссылок на объекты. Типичный сценарий: необходимо объединить таблицу справочника, содержащую реквизит типа «Документ» (неопределенного вида), и таблицу конкретного документа. Прямое сравнение полей через оператор JOIN не дает результатов, так как форматы идентификаторов в этих таблицах различаются.

В рамках данной статьи мы подробно разберем, почему возникает эта проблема, рассмотрим несколько способов приведения данных к единому формату и проанализируем, как эти решения влияют на производительность запросов и использование индексов СУБД.

Выясняем причину: почему форматы ИД различаются?

Прежде чем перейти к написанию кода, давайте разберем физическую структуру хранения ссылок в СУБД 1С 7.7 (будь то DBF или SQL-версия). Формат записи идентификатора документа напрямую зависит от типизации реквизита в конфигураторе:

  1. Строгая типизация: Если реквизит справочника (или другого документа) имеет жестко заданный тип (например, Документ.СчетФактураЛК), то в базе данных сохраняется только «чистый» уникальный идентификатор документа. Его длина составляет 9 символов (например, " 1LS "). Этот формат полностью соответствует полю IDDOC в физической таблице самого документа.
  2. Нестрогая типизация (общий тип «Документ»): Если реквизит может принимать значение любого документа (неопределенный тип), системе необходимо знать не только уникальный номер документа, но и его вид (чтобы понимать, к какой физической таблице обращаться). В этом случае 1С записывает строку длиной 13 символов. Первые 4 символа — это 36-ричный идентификатор вида документа (IDDocDef, например, " 56D"), а последующие 9 символов — это сам IDDOC (например, " 1LS "). Получается склеенная строка вида " 56D 1LS ".

Когда мы пытаемся выполнить связь таблиц по условию ON Бланки.Документ = ДокСф.IDDoc, СУБД пытается сравнить строку из 13 символов со строкой из 9 символов. Разумеется, такое сравнение всегда возвращает ложь (False), и мы получаем пустой результат запроса.

Рассмотрим два основных способа решения этой проблемы с помощью средств SQLite.

Решение 1. Использование оператора конкатенации (||) в SQLite

Самый простой и очевидный путь — привести 9-символьный идентификатор документа к 13-символьному формату прямо в условии связи. Для этого нам нужно приклеить слева к полю IDDOC таблицы документов 4-символьный маркер вида документа.

В SQLite для конкатенации (склеивания) строк используется специальный оператор двойного пайпа ||. Обратите внимание, что привычный для многих языков программирования и других диалектов SQL оператор плюс + в SQLite предназначен исключительно для арифметического сложения (при его использовании строки будут неявно приведены к числам, что даст неверный результат).

Разберем по шагам практический пример запроса с использованием конкатенации:


текстЗапроса = "
|SELECT 
|    Бланки.DESCR, 
|    Бланки.Документ [ДокСпр :Документ], 
|    ДокСф.IDDOC [ДокДок :Документ.СчетФактураЛК] 
|FROM Бланки 
|INNER JOIN ДокСф 
|    ON :ВидДокумента.СчетФактураЛК || ДокСф.IDDoc = Бланки.Документ
|WHERE ДокСф.БланкНалоговой = :ПустойИД;
|";

В данном запросе конструкция :ВидДокумента.СчетФактураЛК — это типизированный типизатор метаданных 1С, который компонента 1sqlite автоматически преобразует в реальный 4-символьный идентификатор вида документа (в нашем случае ' 56D'). К нему присоединяется 9-символьное значение IDDOC, в результате чего получается полноценный 13-символьный идентификатор, который успешно сопоставляется с полем Бланки.Документ.

Решение 2. Использование функции substr для обрезания строки (Рекомендуемое)

Хотя первый метод прост в реализации, он имеет существенный недостаток с точки зрения производительности на больших базах данных. Когда мы оборачиваем индексируемое поле таблицы (в данном случае ДокСф.IDDoc) в любые операции или функции (включая конкатенацию ||), оптимизатор SQLite лишается возможности использовать индекс по этому полю. СУБД будет вынуждена выполнять полный перебор (Full Table Scan) таблицы документов для каждого сопоставления.

Чтобы избежать падения производительности, мы можем пойти обратным путем: не увеличивать поле документа, а наоборот — «отрезать» лишние 4 символа вида документа от 13-символьного поля справочника. Для этого в SQLite применяется стандартная функция substr.

Давайте проанализируем этот подход подробнее. Мы знаем, что IDDOC начинается с 5-го символа в 13-символьной строке. Таким образом, функция substr(Бланки.Документ, 5) вернет нам чистый 9-символьный идентификатор.

Перепишем наше условие связи:


текстЗапроса = "
|SELECT 
|    Бланки.DESCR, 
|    Бланки.Документ [ДокСпр :Документ], 
|    ДокСф.IDDOC [ДокДок :Документ.СчетФактураЛК] 
|FROM Бланки 
|INNER JOIN ДокСф 
|    ON ДокСф.IDDoc = substr(Бланки.Документ, 5)
|WHERE ДокСф.БланкНалоговой = :ПустойИД;
|";

Почему этот вариант значительно быстрее? В данном случае левая часть выражения ДокСф.IDDoc остается чистой, без применения функций. СУБД SQLite вычислит значение substr(...) для текущей записи справочника всего один раз, а затем мгновенно найдет соответствующий документ в таблице ДокСф по индексу первичного ключа (индексы по полям IDDOC всегда создаются системой автоматически).

Дополнительные возможности оптимизации в 1sqlite

При работе со строками и идентификаторами в 1С 7.7 через SQLite полезно знать еще несколько важных механизмов, которые помогут сделать ваши запросы быстрыми и надежными:

Подводя итог, для быстрого и правильного связывания таблиц в 1С 7.7 через прямые SQL-запросы всегда оценивайте объемы данных. Если таблицы содержат десятки тысяч записей, отдавайте предпочтение функции substr для сохранения работоспособности индексов.

← На главную