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

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

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

Способ 1. Классический запрос с группировкой и условием ИМЕЮЩИЕ

Простейший и наиболее очевидный способ заключается в обращении напрямую к табличной части документа с последующей группировкой по ссылке. Для фильтрации результата по количеству строк используется секция ИМЕЮЩИЕ.

Рассмотрим синтаксис и логику такого запроса:

  1. Обращаемся к физической таблице табличной части документа (например, Документ.РеализацияТоваровУслуг.Товары).
  2. Выбираем поле Ссылка.
  3. Группируем строки по полю Ссылка с помощью конструкции СГРУППИРОВАТЬ ПО.
  4. Накладываем отбор на количество строк группы с помощью конструкции ИМЕЮЩИЕ КОЛИЧЕСТВО(*) = 1.

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


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

Разберем важные нюансы этого запроса:

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

Способ 2. Оптимизированный запрос без тяжелой агрегации (через проверку второй строки)

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

Это дает возможность выполнить точечную выборку без тяжелой группировки. Мы можем выбрать все строки с НомерСтроки = 1 и левым соединением проверить, что строка с НомерСтроки = 2 отсутствует в документе.

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


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

Выясним причину высокой эффективности этого метода:

  1. СУБД использует существующий индекс по номерам строк и мгновенно отсекает документы, не выполняя операцию группировки GROUP BY по всей таблице.
  2. Если в документе есть вторая строка (НомерСтроки = 2), он автоматически отсеивается условием ТЧ2.Ссылка ЕСТЬ NULL.

Способ 3. Оптимизация при наличии отборов по шапке (Период, Организация, Проведение)

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

Разберем по шагам построение запроса:

  1. В первом запросе пакета выбираем ссылки из шапки документа с нужными фильтрами и помещаем их во временную таблицу, обязательно индексируя поле Ссылка.
  2. Во втором запросе соединяем временную таблицу с табличной частью документа и накладываем условие на количество строк.

Посмотрим на пример пакета запросов:


ВЫБРАТЬ
    ДокументШапка.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ОтфильтрованныеДокументы
ИЗ
    Документ.РеализацияТоваровУслуг КАК ДокументШапка
ГДЕ
    ДокументШапка.Проведен = ИСТИНА
    И ДокументШапка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
    И ДокументШапка.Организация = &Организация

ИНДЕКСИРОВАТЬ ПО
    Ссылка
;

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

Способ 4. Выборка при наличии нескольких табличных частей

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

Рассмотрим пример запроса с конструкцией ОБЪЕДИНИТЬ ВСЕ:


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

Способ 5. Проверка в объектной модели без запроса

Если объект документа уже получен в контексте выполнения программного кода (например, в модуле формы или при проведении в переменной ЭтотОбъект / ДокументОбъект), выполнять отдельный запрос к базе данных не требуется. Достаточно обратиться к свойству коллекции табличной части.

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


Если ДокументОбъект.Товары.Количество() = 1 Тогда
    // Выполняем действия для документа с одной строкой
КонецЕсли;

Важно: Если у вас есть только переменная типа ДокументСсылка, не следует считывать весь объект целиком через Ссылка.ПолучитьОбъект() только ради проверки количества строк — для ссылок всегда используйте точечный запрос к базе данных.

← На главную