Как выбрать документы с услугами, но без товаров в запросе 1С

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

В практике разработчика 1С часто возникает задача фильтрации документов по специфическим условиям наполнения их табличных частей. Типичный пример — необходимость найти документы (например, РеализацияТоваровУслуг), в которых заполнена только табличная часть Услуги, а табличная часть Товары пуста. На первый взгляд задача кажется простой, но она имеет несколько вариантов решения, каждый из которых обладает своими преимуществами и особенностями производительности. Разберем эти подходы подробно.

Способ 1. Использование левого соединения

Проанализируем самый распространенный и классический метод — использование механизма ЛЕВОЕ СОЕДИНЕНИЕ. Суть метода заключается в том, что мы берем все записи из таблицы услуг и пытаемся найти соответствующие им записи в таблице товаров по ссылке на документ. Если в таблице товаров записей нет, результатом соединения в полях правой таблицы будет значение NULL.

Рассмотрим пример кода:


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

В этом запросе мы используем ключевое слово РАЗЛИЧНЫЕ, так как в табличной части услуг может быть множество строк для одного документа, и без этой инструкции мы получим дубликаты ссылок. Условие ГДЕ ДокументТовары.Ссылка ЕСТЬ NULL отсекает те документы, для которых нашлось хотя бы одно совпадение в таблице товаров.

Способ 2. Использование оператора НЕ В (Подзапросы)

Выясним, как решить задачу через подзапрос. Этот метод часто считается более читабельным и логичным с точки зрения человеческого языка: "Выбрать услуги там, где ссылка документа не входит в список ссылок из таблицы товаров".

Посмотрим на пример реализации:


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

Важно понимать, что платформа 1С транслирует конструкцию НЕ В в SQL-запрос NOT EXISTS или NOT IN. Для большинства современных СУБД (MS SQL Server, PostgreSQL) производительность этого метода будет сопоставима с левым соединением, но текст запроса выглядит чище.

Способ 3. Оптимизация через проверку номера строки

Проанализируем ситуацию с точки зрения производительности. Если база данных содержит миллионы строк в табличных частях, полное сканирование таблиц для соединения может быть долгим. Мы можем оптимизировать запрос, используя тот факт, что для проверки "пустоты" или "наличия" нам достаточно знать о существовании хотя бы одной строки.

В 1С каждая табличная часть имеет поле НомерСтроки. Если в документе есть хотя бы одна строка, то строка с номером 1 существует всегда. Мы можем использовать это в условии:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    тчУслуги.Ссылка
ИЗ
    Документ.МойДокумент.Услуги КАК тчУслуги
ГДЕ
    тчУслуги.НомерСтроки = 1
    И тчУслуги.Ссылка НЕ В (
        ВЫБРАТЬ 
            т.Ссылка 
        ИЗ 
            Документ.МойДокумент.Товары КАК т 
        ГДЕ 
            т.НомерСтроки = 1
    )

Почему это работает быстрее? При использовании условия НомерСтроки = 1 СУБД может эффективнее использовать индексы. Вместо того чтобы перебирать все строки документа в ТЧ Товары, поиск прекращается сразу после нахождения первой строки. Это значительно снижает нагрузку на дисковую подсистему и сокращает время выполнения запроса.

Способ 4. Метод весов (ОБЪЕДИНИТЬ ВСЕ)

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

Разберем алгоритм:

  1. Выбираем все ссылки из ТЧ Услуги с маркером 1.
  2. Выбираем все ссылки из ТЧ Товары с маркером -100.
  3. Группируем результат по ссылке и суммируем маркеры.
  4. Если сумма больше 0, значит, услуги в документе есть, а товаров точно нет.

ВЫБРАТЬ
    ВложенныйЗапрос.Ссылка
ИЗ
    (ВЫБРАТЬ
        ТЧ.Ссылка КАК Ссылка,
        1 КАК Вес
    ИЗ
        Документ.МойДокумент.Услуги КАК ТЧ
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        ТЧ.Ссылка,
        -100
    ИЗ
        Документ.МойДокумент.Товары КАК ТЧ) КАК ВложенныйЗапрос

СГРУППИРОВАТЬ ПО
    ВложенныйЗапрос.Ссылка

ИМЕЮЩИЕ
    СУММА(ВложенныйЗапрос.Вес) > 0

Преимущество этого метода: он использует только операцию сканирования и группировки, что для SQL-сервера зачастую проще, чем выполнение сложных Join-операций с проверкой на NULL.

Особенности современных конфигураций (ERP, УТ 11, КА 2)

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

В этом случае задача решается еще проще, с помощью группировки по одной таблице:


ВЫБРАТЬ
    ТЧ.Ссылка
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
СГРУППИРОВАТЬ ПО
    ТЧ.Ссылка
ИМЕЮЩИЕ
    МАКСИМУМ(ВЫБОР КОГДА ТЧ.Номенклатура.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Услуга) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) = 1
    И МАКСИМУМ(ВЫБОР КОГДА ТЧ.Номенклатура.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Товар) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) = 0

В данном примере мы проверяем наличие хотя бы одной услуги через МАКСИМУМ и одновременно гарантируем отсутствие товаров в этом же документе.

Рекомендации по написанию запроса

Для того чтобы ваш запрос был не только правильным, но и надежным, придерживайтесь следующих правил:

Таким образом, выбор конкретного метода зависит от вашей конфигурации и объема данных. Для небольших баз отлично подойдет ЛЕВОЕ СОЕДИНЕНИЕ или НЕ В, а для высоконагруженных систем стоит рассмотреть оптимизацию через НомерСтроки = 1 или метод объединения весов.

← На главную