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

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

В практике разработки на платформе 1С:Предприятие нередко возникает задача сравнения содержимого документов. Одной из самых интересных и технически сложных вариаций этой задачи является проверка условия: «Входит ли вся табличная часть документа А в табличную часть документа Б?». Это может потребоваться при анализе цепочек документов, проверке соответствия заказов и накладных или при поиске дублирующих операций.

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

Метод 1: Принцип «от противного» (Поиск отсутствующих строк)

Проанализируем самый логичный с точки зрения теории множеств подход. Чтобы доказать, что табличная часть документа А полностью содержится в документе Б, нам нужно убедиться, что в документе А нет ни одной строки, которой бы не было в документе Б.

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

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

  1. Выбираем уникальные комбинации ключевых полей (например, Номенклатура) из документа А.
  2. Соединяем их левым соединением с уникальными комбинациями полей из документа Б.
  3. Устанавливаем условие в ГДЕ, проверяющее отсутствие данных на правой стороне.

Пример кода запроса для этого метода:


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

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

Если данный запрос не вернул ни одной строки, значит, все товары из первого документа присутствуют во втором. Однако стоит помнить, что этот простой метод не учитывает количество товара — только сам факт наличия позиции.

Метод 2: Сравнение мощности множеств (Количество строк)

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

Рассмотрим подробнее логику:

  1. Подсчитаем количество различных номенклатурных позиций в исходном документе А.
  2. Выполним внутреннее соединение табличных частей документов А и Б по номенклатуре.
  3. Подсчитаем количество различных позиций, которые удалось соединить.
  4. Если итоговое количество совпадает с исходным количеством в документе А, значит, все позиции из А найдены в Б.

Проанализируем пример реализации с использованием функции КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...):


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

Важный момент: в языке запросов 1С функция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) игнорирует значения NULL. Это позволяет нам эффективно использовать левое соединение. Если КолСовпадений меньше чем КолА, значит, часть строк из левой таблицы не нашла соответствия в правой.

Метод 3: Учет дублей и суммирование количеств

Выясним, как поступить, если в документах одна и та же номенклатура может встречаться в нескольких строках. В этом случае простое соединение по номенклатуре приведет к «раздуванию» результата (декартову произведению), и стандартные подсчеты строк станут неверными. Кроме того, часто вхождение подразумевает, что во втором документе товара не меньше, чем в первом.

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

  1. Сначала сгруппируем табличные части обоих документов по ключевым полям, просуммировав поле Количество.
  2. Поместим эти данные во временные таблицы ВТ_ГруппировкаА и ВТ_ГруппировкаБ.
  3. Выполним левое соединение этих временных таблиц.
  4. Проверим два условия: строка из А должна найти соответствие в Б, и СуммаКоличество_А должна быть меньше или равна СуммаКоличество_Б.

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


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

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    1
ИЗ
    ВТ_ДанныеА КАК ТЧ_А
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДанныеБ КАК ТЧ_Б
        ПО ТЧ_А.Номенклатура = ТЧ_Б.Номенклатура
ГДЕ
    (ТЧ_Б.Номенклатура ЕСТЬ NULL) ИЛИ (ТЧ_А.Количество > ТЧ_Б.Количество)

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

Особенности сравнения сложных данных

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

При соединении таблиц обязательно включайте в условие ПО следующие поля (если они используются в документе):

Оптимизация производительности

Сравнение табличных частей «всех со всеми» — это крайне тяжелая операция для базы данных, имеющая сложность O(N^2). Рассмотрим, как минимизировать нагрузку на систему:

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

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

← На главную