Как правильно связать таблицы по строковому представлению и почему не стоит использовать ПОДОБНО?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

В практике разработки на платформе 1С часто возникают задачи, когда необходимо отследить цепочку документов. Например, когда одна строка накладной (Реализации товаров и услуг) сформирована на основании нескольких Заказов клиента. У начинающих разработчиков возникает соблазн сохранить список документов-оснований в одну строковую ячейку (например, "Заказ №1, Заказ №2") и затем пытаться связать эти данные в запросе.

В этой статье мы подробно разберем, почему попытка использовать оператор ПОДОБНО для связи таблиц является грубой ошибкой, как технически работают спецсимволы в запросах, и какое архитектурное решение является единственно верным для таких задач.

Суть проблемы: связь "Один ко многим" в строке

Рассмотрим ситуацию, описанную в обсуждении. У нас есть два набора данных:

  1. Таблица с документами (например, Заказы клиентов), у которых есть реквизит Представление.
  2. Таблица (или документ РТУ), где в строковом реквизите перечислены представления документов-оснований через запятую.

Задача разработчика — написать запрос, который соединит эти таблицы. Интуитивное, но ошибочное решение выглядит так:


ВЫБРАТЬ
    РТУ.Ссылка,
    Заказы.Ссылка
ИЗ
    Документ.РеализацияТоваровУслуг КАК РТУ
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
        ПО РТУ.СтрокаОснования ПОДОБНО "%" + Заказы.Представление + "%"

Автор вопроса столкнулся с проблемой синтаксиса: как корректно обработать спецсимволы, если они встречаются в номере документа или представлении? Однако проблема лежит гораздо глубже синтаксиса.

Почему использование ПОДОБНО в соединениях — это плохая практика

Давайте проанализируем, почему опытные разработчики категорически не рекомендуют использовать такую конструкцию.

1. Катастрофическое падение производительности

СУБД (SQL Server или PostgreSQL) не может использовать индексы, если условие соединения содержит оператор LIKE (в 1С — ПОДОБНО), особенно если шаблон начинается с символа процента %. Это означает, что для каждой строки из первой таблицы система будет полностью перебирать вторую таблицу (Full Table Scan). На малых объемах данных это может сработать, но как только база вырастет, такой запрос "повесит" сервер.

2. Виртуальность поля "Представление"

В базе данных 1С физически не существует поля, в котором хранится строка "Заказ клиента №001 от 01.01.2023". Поле Представление формируется динамически в момент выполнения запроса. Попытка использовать его в условии соединения (JOIN) заставляет сервер вычислять это представление для каждой записи перед сравнением, что является очень тяжелой операцией.

3. Ненадежность данных

Строковое представление документа может измениться. Например:

Как только строка меняется, ваша связь, построенная на тексте, разрывается. Данные становятся недостоверными.

Синтаксис спецсимволов (Если очень нужно)

Хотя мы выяснили, что архитектурно это решение неверное, давайте разберем вопрос автора про спецсимволы для общего развития. В языке запросов 1С есть служебные символы для оператора ПОДОБНО:

Если в вашем тексте, который вы ищете (например, в номере заказа), встречаются эти знаки, их нужно "экранировать". Для этого используется ключевое слово СПЕЦСИМВОЛ (или ESCAPE).

Пример запроса, где мы ищем строку, содержащую реальный знак процента:


ВЫБРАТЬ
    Номенклатура.Ссылка
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Наименование ПОДОБНО "%Скидка 10#% по акции%" СПЕЦСИМВОЛ "#"

В данном примере символ # указывает, что следующий за ним знак (в нашем случае %) нужно воспринимать как обычный текст, а не как команду "любой символ".

Правильное архитектурное решение

Вернемся к задаче автора: как хранить информацию о том, что одна строка Реализации создана на основании двух разных Заказов?

Нарушение, которое допускает автор, называется нарушением Первой нормальной формы баз данных: значения в колонке должны быть атомарными. Хранить список через запятую — это ошибка проектирования.

Рассмотрим два правильных способа реализации.

Способ 1: Использование дополнительной табличной части (Рекомендуемый)

Если вам нужно, чтобы строка документа (например, строка №1 с товаром "Стол") ссылалась на несколько документов-оснований, необходимо реализовать связь "Один ко многим".

Шаг 1. Добавление идентификатора строки

В основную табличную часть Товары добавьте реквизит КлючСтроки (тип Число или УникальныйИдентификатор). Он будет уникальным идентификатором конкретной строки внутри документа. При добавлении строки этот ключ должен автоматически генерироваться.

Шаг 2. Создание подчиненной табличной части

Создайте новую табличную часть, например, ДокументыОснования. В ней создайте реквизиты:

Преимущества:

Способ 2: Использование Регистра Сведений

Если менять структуру документа нежелательно (например, документ типовой и находится на поддержке), можно использовать независимый Регистр Сведений.

Структура регистра СвязиСтрокДокументов:

Этот подход сложнее в обслуживании (нужно следить за удалением записей при отмене проведения документа), но он не требует изменения структуры самого документа.

Как вывести данные пользователю?

Автор упоминал, что строковое поле нужно для "отображения в форме списка". Если мы перешли на правильную архитектуру (хранение ссылок), как показать пользователю строку "Заказ 1, Заказ 2"?

Это задача интерфейса, а не базы данных. Формируйте эту строку в момент чтения данных.

Вариант для Динамического списка:

В запросе динамического списка можно использовать вложенный запрос или соединение для получения данных, но склеивать строки в языке запросов 1С сложно (функция СтрСоединить доступна только в СКД или коде, но не в обычном запросе списка).

Поэтому лучше использовать событие ПриПолученииДанных (для обычных форм) или рассчитывать это поле при чтении документа и сохранять в отдельный реквизит "КомментарийОснования" (чисто текстовый, для глаз), но логику строить только на табличных частях.

Пример правильного запроса при наличии ТЧ

Представим, что мы реализовали Способ 1. Теперь, чтобы получить все заказы для конкретной строки реализации, запрос будет выглядеть так:


ВЫБРАТЬ
    РТУТовары.Номенклатура,
    РТУТовары.Количество,
    ЕСТЬNULL(РТУОснования.ДокументОснование, ЗНАЧЕНИЕ(Документ.ЗаказКлиента.ПустаяСсылка)) КАК Заказ
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РТУТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.ДокументыОснования КАК РТУОснования
        ПО РТУТовары.Ссылка = РТУОснования.Ссылка
        И РТУТовары.КлючСтроки = РТУОснования.КлючСтроки
ГДЕ
    РТУТовары.Ссылка = &СсылкаНаДокумент

Этот запрос отработает мгновенно, использует индексы и вернет надежные данные, которые можно использовать для дальнейшей обработки.

Выводы

  1. Никогда не используйте оператор ПОДОБНО для соединения таблиц (JOIN). Это убивает производительность и ведет к ошибкам.
  2. Поле Представление — виртуальное и ненадежное, на него нельзя опираться в алгоритмах.
  3. Если одной строке документа соответствует несколько документов-оснований, используйте подчиненную табличную часть со ссылками на объекты.
  4. Для отображения пользователю собирайте строковое представление программно, но храните данные в нормализованном виде (ссылками).
← На главную