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

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

При разработке прикладных решений на платформе 1С часто возникает задача отобрать элементы справочника или документы по условию, затрагивающему одновременно реквизит шапки и данные одной из строк табличной части. Например, требуется найти всех контрагентов с определенным статусом в шапке и конкретным контактным лицом в табличной части, либо найти элементы справочника по сочетанию реквизитов Фамилия (в шапке) и Город (в табличной части).

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

Способ 1. Явное внутреннее соединение основной таблицы и табличной части

Проанализируем первый классический вариант. Табличная часть любого объекта в платформе 1С физически хранится в отдельной таблице базы данных. Связь между основной таблицей объекта и таблицей его табличной части осуществляется по полю Ссылка.

Рассмотрим пример запроса, где мы явно объединяем таблицу справочника с его табличной частью с помощью конструкции ВНУТРЕННЕЕ СОЕДИНЕНИЕ:


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

Разберем по шагам ключевые элементы этого запроса:

  1. Связь таблиц: Мы явно указываем условие ПО Спр.Ссылка = СпрТЧ.Ссылка. Это гарантирует сопоставление строк табличной части именно с тем элементом, которому они принадлежат.
  2. Ключевое слово РАЗЛИЧНЫЕ: Обратите внимание на ВЫБРАТЬ РАЗЛИЧНЫЕ. Если у одного элемента справочника в табличной части окажется несколько строк, подходящих под условие (или если в выборку не включены уникальные реквизиты самой строки), то без слова РАЗЛИЧНЫЕ ссылка на объект продублируется в результате столько раз, сколько совпадений найдено в его табличной части.
  3. Условия фильтрации: В секции ГДЕ мы свободно комбинируем поля шапки и табличной части с помощью логического оператора И.

Способ 2. Запрос напрямую к табличной части с разыменованием ссылки

Рассмотрим более компактный синтаксический вариант, когда базовой таблицей источника выступает сама табличная часть справочника, а к реквизиту шапки мы обращаемся «через точку»:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ТЧ.Ссылка КАК Ссылка,
    ТЧ.Город КАК Город,
    ТЧ.Ссылка.Фамилия КАК Фамилия
ИЗ
    Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ТЧ
ГДЕ
    ТЧ.Город = &ПараметрГород
    И ТЧ.Ссылка.Фамилия = &ПараметрФамилия

Выясним причину споров о производительности данного подхода. При обращении через точку (ТЧ.Ссылка.Фамилия) платформа 1С на уровне СУБД автоматически генерирует неявное соединение (LEFT OUTER JOIN) с таблицей справочника. Если реквизит не является составным типом данных, платформа строит соединение ровно с одной таблицей.

Тем не менее, явное внутреннее соединение считается более предсказуемым для оптимизатора СУБД, так как левое соединение в сложных запросах может приводить к построению неоптимального плана выполнения, если СУБД не сможет вовремя свернуть его до внутреннего.

Способ 3. Оптимальный поиск без дублирования строк через подзапрос (Semi-Join)

Если в результате запроса нам требуется получить только список самих элементов справочника (только ссылки), без вывода конкретных строк табличной части, наиболее эффективным с точки зрения архитектуры СУБД является использование конструкции В (ВЫБРАТЬ ...):


ВЫБРАТЬ
    Спр.Ссылка КАК Ссылка,
    Спр.Фамилия КАК Фамилия
ИЗ
    Справочник.ФизическиеЛица КАК Спр
ГДЕ
    Спр.Фамилия = &ПараметрФамилия
    И Спр.Ссылка В
        (ВЫБРАТЬ
            ТЧ.Ссылка
        ИЗ
            Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ТЧ
        ГДЕ
            ТЧ.Город = &ПараметрГород)

Посмотрим на преимущества этого решения:

  1. Отсутствие дублей: Запрос физически не может размножить строки шапки, поэтому нам не требуется использовать тяжелую для больших объемов операцию ВЫБРАТЬ РАЗЛИЧНЫЕ или СГРУППИРОВАТЬ ПО.
  2. Оптимизация на стороне СУБД: Платформа и современные СУБД (MS SQL Server, PostgreSQL) транслируют такой запрос в полусоединение (SEMI JOIN или оператор EXISTS), выполняя поиск по индексу и прекращая сканирование строк ТЧ для текущего объекта сразу после первого совпадения.

Важные рекомендации по индексации и производительности

При работе с большими базами данных выбор структуры запроса должен сопровождаться правильной настройкой метаданных:

  1. Индексация реквизитов ТЧ: По умолчанию в физической таблице табличной части кластерный индекс сформирован по полям ссылки и номера строки. Поиск по реквизиту табличной части без установленного индекса гарантированно вызовет полное сканирование таблицы (Table Scan). Если поиск выполняется регулярно, в конфигураторе для реквизита табличной части необходимо обязательно установить свойство Индексировать.
  2. Индексация реквизитов шапки: Для реквизита шапки, по которому накладывается жесткий отбор, также должно быть включено индексирование, особенно если этот реквизит обладает высокой избирательностью (селективностью).
← На главную