В практике разработки на платформе 1С:Предприятие нередко возникает задача сравнения содержимого документов. Одной из самых интересных и технически сложных вариаций этой задачи является проверка условия: «Входит ли вся табличная часть документа А в табличную часть документа Б?». Это может потребоваться при анализе цепочек документов, проверке соответствия заказов и накладных или при поиске дублирующих операций.
Рассмотрим подробнее, как реализовать подобную проверку с помощью языка запросов, разберем основные методы, их преимущества и ограничения.
Проанализируем самый логичный с точки зрения теории множеств подход. Чтобы доказать, что табличная часть документа А полностью содержится в документе Б, нам нужно убедиться, что в документе А нет ни одной строки, которой бы не было в документе Б.
Выясним причину, почему это работает: если мы выполним ЛЕВОЕ СОЕДИНЕНИЕ таблицы А с таблицей Б и получим в результате хотя бы одну строку, где поля документа Б будут равны NULL, значит, вхождение не является полным.
Разберем по шагам алгоритм построения такого запроса:
Номенклатура) из документа А.ГДЕ, проверяющее отсутствие данных на правой стороне.Пример кода запроса для этого метода:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ТЧ_А.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ДокументА
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТЧ_А
ГДЕ
ТЧ_А.Ссылка = &СсылкаА;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
1
ИЗ
ВТ_ДокументА КАК ТЧ_А
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК ТЧ_Б
ПО ТЧ_А.Номенклатура = ТЧ_Б.Номенклатура
И ТЧ_Б.Ссылка = &СсылкаБ
ГДЕ
ТЧ_Б.Номенклатура ЕСТЬ NULL
Если данный запрос не вернул ни одной строки, значит, все товары из первого документа присутствуют во втором. Однако стоит помнить, что этот простой метод не учитывает количество товара — только сам факт наличия позиции.
Посмотрим на ситуацию с другой стороны. Мы можем использовать агрегатные функции для сравнения количества уникальных позиций. Этот метод часто оказывается быстрее, если нам не нужно знать конкретные недостающие позиции, а требуется только получить ответ «Да/Нет».
Рассмотрим подробнее логику:
Проанализируем пример реализации с использованием функции КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...):
ВЫБРАТЬ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ТЧ_А.Номенклатура) КАК КолА,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ТЧ_Б.Номенклатура) КАК КолСовпадений
ИЗ
Документ.ЗаказПокупателя.Товары КАК ТЧ_А
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК ТЧ_Б
ПО ТЧ_А.Номенклатура = ТЧ_Б.Номенклатура
И ТЧ_Б.Ссылка = &СсылкаБ
ГДЕ
ТЧ_А.Ссылка = &СсылкаА
Важный момент: в языке запросов 1С функция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) игнорирует значения NULL. Это позволяет нам эффективно использовать левое соединение. Если КолСовпадений меньше чем КолА, значит, часть строк из левой таблицы не нашла соответствия в правой.
Выясним, как поступить, если в документах одна и та же номенклатура может встречаться в нескольких строках. В этом случае простое соединение по номенклатуре приведет к «раздуванию» результата (декартову произведению), и стандартные подсчеты строк станут неверными. Кроме того, часто вхождение подразумевает, что во втором документе товара не меньше, чем в первом.
Посмотрим на правильный алгоритм решения задачи с учетом количественных показателей:
Количество.ВТ_ГруппировкаА и ВТ_ГруппировкаБ.СуммаКоличество_А должна быть меньше или равна СуммаКоличество_Б.Рассмотрим пример кода:
ВЫБРАТЬ
ТЧ.Номенклатура КАК Номенклатура,
СУММА(ТЧ.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_ДанныеА
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
ГДЕ
ТЧ.Ссылка = &СсылкаА
СГРУППИРОВАТЬ ПО
ТЧ.Номенклатура;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТЧ.Номенклатура КАК Номенклатура,
СУММА(ТЧ.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_ДанныеБ
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
ГДЕ
ТЧ.Ссылка = &СсылкаБ
СГРУППИРОВАТЬ ПО
ТЧ.Номенклатура;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
1
ИЗ
ВТ_ДанныеА КАК ТЧ_А
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДанныеБ КАК ТЧ_Б
ПО ТЧ_А.Номенклатура = ТЧ_Б.Номенклатура
ГДЕ
(ТЧ_Б.Номенклатура ЕСТЬ NULL) ИЛИ (ТЧ_А.Количество > ТЧ_Б.Количество)
В данном примере, если запрос возвращает хотя бы одну строку, условие полного вхождения нарушено. Либо товара нет совсем, либо его количество в целевом документе меньше, чем в исходном.
Проанализируем ситуацию, когда уникальность строки определяется не только номенклатурой. В современных конфигурациях (ERP, УТ 11, КА) необходимо учитывать множество дополнительных измерений. Если мы их пропустим, запрос даст ложноположительный результат.
При соединении таблиц обязательно включайте в условие ПО следующие поля (если они используются в документе):
Сравнение табличных частей «всех со всеми» — это крайне тяжелая операция для базы данных, имеющая сложность O(N^2). Рассмотрим, как минимизировать нагрузку на систему:
ПОМЕСТИТЬ) обязательно индексируйте поле Номенклатура (или составной ключ), по которому будет идти соединение. Это значительно ускорит работу СУБД.Количество()) или общую сумму документа. Если в документе Б строк меньше, чем в А, то полное вхождение заведомо невозможно (при условии отсутствия дублей).Таким образом, мы разобрали основные способы решения задачи. Выбор конкретного метода зависит от требований к точности (нужен ли учет количеств) и объема данных, которые необходимо обработать в запросе.