При разработке сложных алгоритмов обработки данных в 1С часто возникает задача сравнения двух наборов данных (таблиц). Это может быть сверка остатков между разными регистрами, поиск изменений при обмене данными или валидация результатов перед записью. Основной вопрос, который встает перед разработчиком: какой метод выбрать с точки зрения производительности и корректности результата? В этой статье мы подробно разберем два основных подхода, ориентируясь на быстрый анализ данных и способы упростить работу разработчика при написании запросов.
Представим, что у нас есть две таблицы с одинаковой структурой (назовем их Таблица А и Таблица Б). Нам необходимо выяснить, идентичны ли они, или же найти расхождения. Казалось бы, задача тривиальная, но дьявол кроется в деталях: как запрос поведет себя при наличии дублей? Как будут обрабатываться NULL значения? Какую нагрузку это создаст на СУБД?
Давайте проанализируем два основных метода, предложенных в профессиональном сообществе, и выясним их плюсы и минусы.
Этот метод часто называют "математическим" или методом симметричной разности. Суть его заключается в том, что мы не пытаемся соединить строки двух таблиц друг с другом, а сваливаем их в одну кучу, предварительно пометив принадлежность к источнику.
Разберем алгоритм по шагам:
1.-1.ОБЪЕДИНИТЬ ВСЕ (Union All), чтобы получить единую таблицу.ИМЕЮЩИЕ): сумма индикатора не равна 0.Посмотрим на пример кода запроса, реализующего этот алгоритм (отладить который поможет совместимая консоль запросов, подходящая для всех платформ 1С):
ВЫБРАТЬ
Т1.Номенклатура КАК Номенклатура,
Т1.Количество КАК Количество,
1 КАК Индикатор
ПОМЕСТИТЬ ВТ_Объединение
ИЗ
ТаблицаА КАК Т1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Т2.Номенклатура,
Т2.Количество,
-1
ИЗ
ТаблицаБ КАК Т2
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Объединение.Номенклатура,
ВТ_Объединение.Количество,
СУММА(ВТ_Объединение.Индикатор) КАК Разница
ИЗ
ВТ_Объединение КАК ВТ_Объединение
СГРУППИРОВАТЬ ПО
ВТ_Объединение.Номенклатура,
ВТ_Объединение.Количество
ИМЕЮЩИЕ
СУММА(ВТ_Объединение.Индикатор) <> 0
Преимущества этого метода:
2 + (-1) = 1. Условие <> 0 сработает, и мы увидим разницу. Многие другие методы пропустят этот момент.NULL значения в одну группу. Вам не нужно писать громоздкие конструкции ЕСТЬNULL для каждого поля.Index Scan или Sort. Это линейные операции, которые часто быстрее сложных алгоритмов соединения.Второй популярный подход — попытаться "склеить" таблицы по всем полям. Логика следующая: если запись есть в обеих таблицах, они соединятся. Если записи нет в одной из таблиц, мы получим NULL с одной стороны.
Для поиска различий мы должны выбрать записи, где есть NULL хотя бы с одной стороны.
Разберем пример кода:
ВЫБРАТЬ
ЕСТЬNULL(Т1.Номенклатура, Т2.Номенклатура) КАК Номенклатура,
ЕСТЬNULL(Т1.Количество, Т2.Количество) КАК Количество
ИЗ
ТаблицаА КАК Т1
ПОЛНОЕ СОЕДИНЕНИЕ ТаблицаБ КАК Т2
ПО Т1.Номенклатура = Т2.Номенклатура
И Т1.Количество = Т2.Количество
ГДЕ
(Т1.Номенклатура ЕСТЬ NULL
ИЛИ Т2.Номенклатура ЕСТЬ NULL)
Недостатки и подводные камни этого метода:
Давайте проанализируем критические уязвимости этого подхода, о которых нужно знать.
Проблема 1: Сравнение с NULL.
В языке запросов NULL = NULL дает результат FALSE. Если в ваших данных могут быть пустые значения, ПОЛНОЕ СОЕДИНЕНИЕ не сможет сопоставить такие строки. Они выпадут в результат как различные, хотя по факту они одинаковы. Чтобы этого избежать, рекомендуем изучить руководство по обработке пустых значений.
Проблема 2: Размножение строк (Декартово произведение).
Если в таблицах нет уникального ключа, и одна и та же строка встречается несколько раз, ПОЛНОЕ СОЕДИНЕНИЕ перемножит их количество. Это не только исказит результат, но и может привести к переполнению памяти tempdb. В таких случаях своевременный поиск неоптимальных запросов SQL при помощи DMV поможет выявить причины падения производительности на стороне сервера.
Бывают ситуации, когда таблицы очень "широкие". Сравнивать их по каждому полю через ОБЪЕДИНИТЬ ВСЕ накладно для процессора СУБД. Чтобы оптимизировать процесс, полезна проверка запросов на лишнюю выборку и полей во временных таблицах.
В этом случае эффективным решением будет предварительный расчет хеш-суммы строки:
ХешСтроки.Это радикально снижает нагрузку на операции сравнения строк, так как сравниваются не сотни байт, а короткие строки или числа.
В обсуждении была затронута важная тема: всегда ли нужно использовать запрос? Если объем данных невелик, накладные расходы на временные таблицы могут превысить время самого сравнения. Однако когда на запрос накладывается множество условий и производительность падает, иногда применяется циклическая вложенная фильтрация.
Для "микрообъемов" (до 1000 строк) можно использовать методы встроенного языка 1С:
ОбщегоНазначения.
Таблица1.Сортировать("Номенклатура, Количество");
Таблица2.Сортировать("Номенклатура, Количество");
// Далее один цикл по индексам для сравнения строк
Подводя итог нашему анализу, сформулируем правила выбора инструмента:
NULL — используйте Метод 1 (ОБЪЕДИНИТЬ ВСЕ + СГРУППИРОВАТЬ). Это самый надежный и универсальный способ.ПОЛНОЕ СОЕДИНЕНИЕ с выбором ПЕРВЫЕ 1.