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