В практике разработчика 1С часто возникает задача сравнения двух наборов данных. Это может быть проверка корректности динамически сформированного запроса пользователем относительно эталонного шаблона, контроль идентичности данных при переносе из одной базы в другую или автоматизация тестирования. Важно понимать, что текст запроса может различаться (наличие лишних пробелов, разный регистр, порядок условий), но результат при этом будет абсолютно идентичным. Поэтому сравнивать нужно именно результаты выполнения или логическую структуру самого запроса.
Рассмотрим несколько эффективных подходов, которые позволяют решить эту задачу программно, начиная от использования готовых инструментов, таких как управляемая консоль запросов с функцией сравнения, и заканчивая низкоуровневым сопоставлением таблиц значений.
Если ваша конфигурация разработана на основе «Библиотеки стандартных подсистем» (БСП), самый простой и надежный способ — использовать готовую функцию общего модуля. Это избавит вас от написания велосипедов и обработки частных случаев сравнения типов.
Разберем работу функции ДанныеСовпадают. Она умеет рекурсивно сравнивать структуры, массивы и, что самое важное для нас, таблицы значений. Посмотрим на пример кода:
// Выполняем первый запрос (эталонный)
Запрос1 = Новый Запрос(ТекстЭталона);
Результат1 = Запрос1.Выполнить().Выгрузить();
// Выполняем второй запрос (пользовательский)
Запрос2 = Новый Запрос(ТекстПользователя);
Результат2 = Запрос2.Выполнить().Выгрузить();
// Сравниваем результаты через БСП
Если ОбщегоНазначения.ДанныеСовпадают(Результат1, Результат2) Тогда
Сообщить("Результаты запросов идентичны!");
Иначе
Сообщить("Результаты различаются.");
КонецЕсли;
Проанализируем нюансы: этот метод чувствителен к порядку строк. Если два запроса возвращают одни и те же данные, но в разном порядке, функция вернет Ложь. Чтобы этого избежать, перед сравнением обе таблицы необходимо отсортировать по всем колонкам.
Если использование БСП невозможно или вы хотите реализовать собственную логику сравнения с детальным выводом различий, проанализируем алгоритм ручного сопоставления двух объектов ТаблицаЗначений.
Выясним основные этапы проверки:
Для ускорения написания подобных алгоритмов можно использовать консоль кода с ИИ-помощником. Рассмотрим пример реализации функции сравнения:
Функция ТаблицыИдентичны(Таблица1, Таблица2)
// 1. Проверяем количество колонок
Если Таблица1.Колонки.Количество() <> Таблица2.Колонки.Количество() Тогда
Возврат Ложь;
КонецЕсли;
// 2. Проверяем количество строк
Если Таблица1.Количество() <> Таблица2.Количество() Тогда
Возврат Ложь;
КонецЕсли;
// Составляем список колонок для сортировки
СтрокаКолонок = "";
Для Каждого Колонка Из Таблица1.Колонки Цикл
СтрокаКолонок = СтрокаКолонок + ?(СтрокаКолонок = "", "", ",") + Колонка.Имя;
КонецЦикла;
// 3. Сортируем обе таблицы
Таблица1.Сортировать(СтрокаКолонок);
Таблица2.Сортировать(СтрокаКолонок);
// 4. Построчное сравнение
Для Индекс = 0 По Таблица1.Количество() - 1 Цикл
Строка1 = Таблица1[Индекс];
Строка2 = Таблица2[Индекс];
Для Каждого Колонка Из Таблица1.Колонки Цикл
Если Строка1[Колонка.Имя] <> Строка2[Колонка.Имя] Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Возврат Истина;
КонецФункции
Иногда задача стоит иначе: нужно понять, не возвращают ли запросы одинаковые данные, а одинакова ли их логическая структура. Например, если пользователь просто добавил лишний пробел или изменил регистр ключевого слова ВЫБРАТЬ на выбрать. Прямое сравнение строк здесь не поможет.
Для этих целей в платформе 8.3 предусмотрен объект СхемаЗапроса. Он позволяет разобрать текст запроса в древовидную структуру объектов. Проанализируем, как это работает:
Схема1 = Новый СхемаЗапроса;
Схема1.УстановитьТекст(ТекстЗапроса1);
Схема2 = Новый СхемаЗапроса;
Схема2.УстановитьТекст(ТекстЗапроса2);
// Теперь можно программно сравнить части запроса
// Например, количество выбираемых полей в первом пакете
Если Схема1.ПакетЗапросов[0].ВыбираемыеПоля.Количество() = Схема2.ПакетЗапросов[0].ВыбираемыеПоля.Количество() Тогда
// Продолжаем проверку источников данных, условий ГДЕ и т.д.
КонецЕсли;
Преимущество этого метода в том, что он полностью абстрагирован от форматирования текста. Если два запроса логически эквивалентны (выбирают одни и те же поля из одних и тех же таблиц), их схемы будут идентичны по составу коллекций.
Для очень больших наборов данных выгрузка в ТаблицаЗначений может занять много памяти. В этом случае эффективно переложить задачу сравнения на саму СУБД. Мы можем объединить два запроса и проверить, есть ли между ними разница.
Рассмотрим алгоритм «контрольной суммы» записей:
ВТ_Данные1, добавив поле Счетчик со значением 1.ВТ_Данные2, добавив поле Счетчик со значением -1.ОБЪЕДИНИТЬ ВСЕ для обеих таблиц.СУММА(Счетчик).Если данные идентичны, то для каждой строки 1 и -1 взаимоуничтожатся, и итоговый результат запроса будет пустым. Если же в результате появилась строка, значит, в одном из запросов данных больше или они отличаются.
ТекстСравнения =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Все ИЗ (
| ВЫБРАТЬ Т1.*, 1 КАК Счетчик ИЗ ВТ_Данные1 КАК Т1
| ОБЪЕДИНИТЬ ВСЕ
| ВЫБРАТЬ Т2.*, -1 КАК Счетчик ИЗ ВТ_Данные2 КАК Т2
|) КАК Совмещенные;
|
|ВЫБРАТЬ * ИЗ ВТ_Все
|СГРУППИРОВАТЬ ПО [ВсеПоляЗапроса]
|ИМЕЮЩИЕ СУММА(Счетчик) <> 0";
Если вам нужно быстро проверить равенство таблиц без детального обхода, можно воспользоваться механизмом сериализации. Мы превращаем таблицу значений в строку и сравниваем две строки. Это работает быстрее, чем ручной обход в цикле на стороне встроенного языка.
Важно: этот способ также требует предварительной сортировки таблиц и совпадения типов колонок.
// Используем внутреннее строковое представление
СтрокаТаблицы1 = ЗначениеВСтрокуВнутр(Результат1);
СтрокаТаблицы2 = ЗначениеВСтрокуВнутр(Результат2);
Если СтрокаТаблицы1 = СтрокаТаблицы2 Тогда
Сообщить("Данные полностью совпадают");
КонецЕсли;
Также можно использовать СериализаторXDTO для выгрузки в XML или JSON, что позволит игнорировать некоторые специфические системные свойства объектов, сосредоточившись только на данных.
При сравнении результатов запросов всегда помните о следующих моментах:
Сумма * 0.15) может отличаться в 15-м знаке после запятой. Перед сравнением рекомендуется использовать функцию ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) в самом запросе.NULL там, где нет данных (например, при левом соединении), и это нужно учитывать при сопоставлении с эталонными объектами.Выбор конкретного метода зависит от ваших условий: используете ли вы БСП, насколько велик объем данных и нужно ли вам понимать, в какой именно строке возникло расхождение. Для простых задач идеально подойдет ОбщегоНазначения.ДанныеСовпадают, а для высоконагруженных систем — метод исключения на уровне запроса.