Разработка и Конфигурация · 12 мин
Как сравнить две таблицы значений запросом в 1С
Как сравнить две таблицы значений в 1С через запрос: помещение во временные таблицы, полное соединение, поиск различий и совпадений. Примеры кода и типичные ошибки.
Задача сравнить две таблицы значений возникает постоянно: сверить данные до и после обработки, найти расхождения между документом и регистром, определить, какие строки добавились или исчезли. Сравнение в цикле «для каждой строки ищем в другой таблице» работает медленно и порождает громоздкий код. Запрос решает ту же задачу декларативно и быстро, потому что вся работа выполняется на сервере СУБД.
Основной приём — поместить обе таблицы значений во временные таблицы менеджера временных таблиц, а затем объединить их полным соединением (FULL JOIN) или левым соединением с проверкой на NULL. Результат — выборка, где видно, какие строки совпадают, какие есть только в первой таблице и какие только во второй.
Ниже разобран полный сценарий: подготовка таблиц, текст запроса, интерпретация результата, типичные ошибки с типами колонок и альтернативные подходы для случаев, когда запрос не нужен.
Почему запрос эффективнее сравнения в цикле
Классический подход «в лоб» — вложенные циклы: для каждой строки первой таблицы вызывается Найти() или НайтиСтроки() по второй. На таблицах в несколько тысяч строк это даёт квадратичную сложность: десять тысяч строк против десяти тысяч — это до ста миллионов операций сравнения, и обработка занимает минуты.
Запрос переносит сравнение на уровень СУБД, где соединение выполняется оптимизированными алгоритмами (хеш-соединение, соединение слиянием). Те же десять тысяч строк сравниваются за доли секунды. Дополнительный плюс — код становится короче и читается как декларативное описание: «покажи строки, которых нет в другой таблице».
Запрос особенно оправдан, когда результат сравнения нужен для дальнейшей выборки из базы: например, найти расхождения и сразу дочитать по ним данные из регистров. Всё это делается одним пакетным запросом без лишних обращений к серверу.
Подготовка: помещение таблиц значений во временные таблицы
Чтобы таблица значений стала доступна в запросе, её нужно поместить во временную таблицу. Для этого в тексте запроса используется конструкция ВЫБРАТЬ ... ПОМЕСТИТЬ ИмяВременнойТаблицы ИЗ &Таблица КАК Таблица, а сама таблица передаётся как параметр запроса.
Обе таблицы можно поместить в одном пакетном запросе, разделив части точкой с запятой. Обязательное условие — у запроса должен быть назначен менеджер временных таблиц, иначе временные таблицы будут уничтожены сразу после выполнения пакета.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ТаблицаДо
|ИЗ &ТаблицаДо КАК Т
|;
|ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ТаблицаПосле
|ИЗ &ТаблицаПосле КАК Т";
Запрос.УстановитьПараметр("ТаблицаДо", ТЗДо);
Запрос.УстановитьПараметр("ТаблицаПосле", ТЗПосле);
Запрос.Выполнить();
После выполнения этого пакета в менеджере временных таблиц существуют две таблицы — ТаблицаДо и ТаблицаПосле. Дальнейшие запросы с тем же менеджером могут обращаться к ним как к обычным таблицам базы данных.
Основной запрос: полное соединение и поиск различий
Полное соединение (FULL JOIN) возвращает все строки обеих таблиц. Там, где пары не нашлось, поля противоположной стороны равны NULL. Именно по этому признаку определяются различия.
ЗапросСравнения = Новый Запрос;
ЗапросСравнения.МенеджерВременныхТаблиц = МенеджерВТ;
ЗапросСравнения.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(До.Номенклатура, После.Номенклатура) КАК Номенклатура,
| До.Количество КАК КоличествоДо,
| После.Количество КАК КоличествоПосле,
| ВЫБОР
| КОГДА До.Номенклатура ЕСТЬ NULL
| ТОГДА ""Добавлена""
| КОГДА После.Номенклатура ЕСТЬ NULL
| ТОГДА ""Удалена""
| КОГДА До.Количество <> После.Количество
| ТОГДА ""Изменена""
| ИНАЧЕ ""Совпадает""
| КОНЕЦ КАК Статус
|ИЗ ТаблицаДо КАК До
| ПОЛНОЕ СОЕДИНЕНИЕ ТаблицаПосле КАК После
| ПО До.Номенклатура = После.Номенклатура";
Результат = ЗапросСравнения.Выполнить().Выгрузить();
В колонке Статус каждая строка получает одну из четырёх меток. Если нужны только расхождения, добавьте в запрос условие ГДЕ До.Номенклатура ЕСТЬ NULL ИЛИ После.Номенклатура ЕСТЬ NULL ИЛИ До.Количество <> После.Количество — тогда совпадающие строки в результат не попадут.
Ключ соединения не обязан быть одной колонкой. Если строки идентифицируются комбинацией «Номенклатура + Склад + Серия», перечислите все поля в условии соединения через И. От состава ключа напрямую зависит корректность результата.
Запрос.Текст = "ВЫБРАТЬ До.Номенклатура ИЗ ТаблицаДо КАК До ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаПосле КАК После ПО До.Номенклатура = После.Номенклатура ГДЕ После.Номенклатура ЕСТЬ NULL";Варианты сравнения под разные задачи
Найти строки, которых нет во второй таблице
Если нужен односторонний результат — например, «что удалили из таблицы» — достаточно левого соединения с фильтром по NULL. Это дешевле полного соединения и проще читается.
"ВЫБРАТЬ До.Номенклатура
|ИЗ ТаблицаДо КАК До
| ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаПосле КАК После
| ПО До.Номенклатура = После.Номенклатура
|ГДЕ После.Номенклатура ЕСТЬ NULL"
Найти только совпадающие строки
Для пересечения таблиц используйте внутреннее соединение (ВНУТРЕННЕЕ СОЕДИНЕНИЕ). В результат попадут только строки, ключ которых присутствует в обеих таблицах.
Сравнение без учёта порядка строк и дублей
Запрос сравнивает множества по ключу, а не позиции строк — порядок в исходных таблицах значений роли не играет. Но если в одной таблице ключ повторяется, соединение перемножит строки, и вы получите дубли в результате. Перед сравнением сверните таблицы: либо методом Свернуть() в коде, либо группировкой СГРУППИРОВАТЬ ПО с агрегатными функциями прямо в запросе помещения во временную таблицу.
Сравнение вариантов
Подходит для полной сверки двух таблиц: видно добавленные, удалённые и изменённые строки. Ограничение — требует аккуратной обработки NULL в полях обеих сторон.
Подходит для одностороннего поиска отсутствующих строк. Дешевле полного соединения, но не показывает строки, появившиеся во второй таблице.
Подходит для поиска пересечения таблиц. Не показывает различия — только совпадения.
Подходит только для маленьких таблиц и простых проверок без обращения к СУБД. На больших объёмах работает на порядки медленнее запроса.
Требования к колонкам: типы, имена и состав
Платформа строго относится к структуре таблицы значений, передаваемой параметром запроса. Нарушение требований — самая частая причина ошибок вида «Неверные параметры в операции сравнения» или «Поле не найдено».
Основные правила:
- Колонки, участвующие в соединении, должны иметь совместимые типы в обеих таблицах. Если в одной таблице колонка имеет составной тип (например, справочник «Номенклатура» и строка), а в другой — чистый тип, сравнение может дать неожиданный результат или ошибку.
- Имена колонок в таблицах значений могут различаться — вы сами задаёте псевдонимы в запросе. Но внутри одной таблицы имена должны быть уникальны и соответствовать правилам имён переменных.
- Колонки типа «ХранилищеЗначения» нельзя использовать в соединениях и сравнениях — исключите их из выборки во временную таблицу.
- Числовые колонки с разной разрядностью сравниваются корректно, но строки с разной длиной лучше привести к одному виду через
ВЫРАЗИТЬ.
Если типы колонок-ключей различаются, приведите их явно в запросе помещения: ВЫРАЗИТЬ(Т.Код КАК СТРОКА(20)) КАК Код. Это снимает неоднозначность ещё до соединения.
Проверка результата и контрольные точки
После выполнения сравнения полезно убедиться, что результат согласуется с исходными данными. Простая арифметическая проверка: количество строк со статусом «Совпадает» плюс «Удалена» должно равняться числу строк первой таблицы (при отсутствии дублей по ключу), а «Совпадает» плюс «Добавлена» — числу строк второй.
Для отладки выгрузите результат в табличный документ или посмотрите выборку в консоли запросов. Если все строки неожиданно помечены как различия — почти наверняка проблема в типах ключевых колонок или в лишних пробелах в строковых ключах.
Как проверить результат
Совпадает + Удалена = числу строк первой таблицы; Совпадает + Добавлена = числу строк второй
Суммы не сходятся — вероятны дубли ключей или ошибка в условии соединения
Часть строк имеет статус «Совпадает» при заведомо похожих данных
Все строки различны при одинаковых данных — проверяйте типы и пробелы в ключевых колонках
Не превышает суммы строк обеих таблиц
Строк больше, чем сумма исходных — ключ соединения не уникален
Альтернативы запросу и границы применимости
Запрос не всегда обязателен. Для таблиц до нескольких сотен строк сравнение в коде через индексированный поиск (НайтиСтроки с предварительно построенным соответствием) вполне приемлемо и не требует обращения к СУБД. Ещё один вариант — метод Свернуть() с последующим объединением таблиц и группировкой: добавить в каждую таблицу служебную колонку-флаг, скопировать строки в одну общую таблицу и свернуть по ключу — расхождения видны по сумме флагов.
Запрос выигрывает, когда таблицы велики, когда результат сравнения нужно соединить с данными базы, или когда сравнение — часть большого пакетного запроса. В толстом клиенте на файловой базе разница менее заметна, но на клиент-серверной архитектуре преимущество запроса максимально.
Как выбрать действие
Что важно запомнить
Сравнение двух таблиц значений запросом строится на трёх шагах: поместить обе таблицы во временные таблицы через общий менеджер, выполнить полное соединение по ключу, классифицировать строки по признаку NULL. Следите за типами колонок и дублями ключей — это главные источники ошибок.
Частые вопросы
Можно ли сравнить таблицы значений без помещения во временные таблицы?
Технически обе таблицы можно передать параметрами прямо в один запрос с соединением двух источников-параметров, но на практике временные таблицы удобнее: их можно использовать в нескольких последующих запросах и соединять с данными базы. Прямая передача оправдана только для одноразового простого сравнения.
Почему запрос не находит совпадений, хотя значения визуально одинаковые?
Чаще всего причина в разных типах колонок (число против строки), лишних пробелах или неразрывных пробелах в строковых ключах, либо в различии ссылок разных справочников. Приведите ключи к одному типу через ВЫРАЗИТЬ и проверьте строки функцией СокрЛП при заполнении таблиц.
Как сравнить таблицы по нескольким колонкам одновременно?
Перечислите все ключевые колонки в условии соединения через И: ПО До.Номенклатура = После.Номенклатура И До.Склад = После.Склад. Для сравнения остальных (неключевых) колонок добавьте проверки в секцию ВЫБОР или в условие ГДЕ.
Что делать, если в таблицах есть строки с пустыми значениями ключа?
Пустые ссылки и NULL ведут себя по-разному: NULL не равен ничему, включая другой NULL, поэтому такие строки не соединятся. Замените возможные NULL на пустые ссылки через ЕСТЬNULL ещё на этапе помещения во временную таблицу или добавьте в условие соединения обработку NULL.
Есть ли ограничение на размер таблицы значений, передаваемой в запрос?
Жёсткого ограничения платформа не задаёт, но практический предел определяется памятью сервера и длиной пакета запроса. Таблицы в десятки тысяч строк обычно обрабатываются без проблем; при сотнях тысяч строк разумнее сравнивать порциями или перенести данные в регистр сведений.
Можно ли сравнить таблицу значений с данными регистра напрямую?
Да, это один из самых полезных сценариев. Поместите таблицу значений во временную таблицу и соедините её с виртуальной таблицей регистра (например, Остатки) в одном запросе. Так проверяют расхождения между документом и движениями или между учётными системами.