Как быстро проверить, пустой ли отбор в наборе записей регистра сведений 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с регистрами сведений в платформе «1С:Предприятие» разработчики часто сталкиваются с необходимостью анализа наборов записей. Одна из классических задач — быстро определить, установлены ли какие-либо фильтры (отборы) в наборе записей или же отбор пуст. На первый взгляд задача кажется тривиальной, однако стандартные методы вроде проверки количества элементов или сериализации в строку таят в себе неочевидные особенности платформы.

В этой статье мы подробно проанализируем поведение платформы 1С при работе с отборами, выясним, почему простые решения не работают, и разберем наиболее эффективные и надежные способы проверки и сравнения отборов.

Почему не работают стандартные методы?

Рассмотрим типичные ошибки, которые совершают разработчики при попытке быстро проверить активность отбора.

Попытка 1: Использование метода Отбор.Количество()

Казалось бы, если отбор не задан, то количество элементов в коллекции должно быть равно нулю. Однако в случае с набором записей регистра сведений это не так. Свойство Отбор набора записей всегда содержит фиксированное количество элементов, которое строго равно количеству измерений данного регистра (включая системные измерения, такие как Период для периодических регистров или Регистратор для подчиненных регистратору). Метод Отбор.Количество() всегда будет возвращать это число, независимо от того, установлены ли реальные значения фильтров или нет.

Попытка 2: Использование функции ЗначениеВСтрокуВнутр()

Попытка сериализовать отборы двух разных наборов записей во внутреннюю строку и сравнить полученные строки также обречена на провал. Платформа формирует одинаковое внутреннее строковое представление для объектов Отбор одного и того же регистра сведений, даже если в одном из них установлены жесткие фильтры по измерениям, а во втором — фильтры полностью отсутствуют. В результате такое сравнение всегда будет возвращать Истина, вводя разработчика в заблуждение.

Выяснив причины неработоспособности быстрых методов, перейдем к правильным и эффективным решениям этой задачи.

Способ 1. Проверка активности отбора через обход коллекции в цикле

Проанализируем самый надежный, производительный и рекомендуемый стандарт разработки способ — ручной обход элементов коллекции Отбор. Каждый элемент этой коллекции имеет тип ЭлементОтбора, у которого есть важнейшее булево свойство Использование. Если это свойство имеет значение Истина, значит, по данному измерению наложен активный фильтр.

Разберем по шагам, как написать универсальную функцию для проверки того, является ли отбор абсолютно пустым:


Функция ОтборПустой(НаборЗаписей) Экспорт
    
    Для Каждого ЭлементОтбора Из НаборЗаписей.Отбор Цикл
        Если ЭлементОтбора.Использование Тогда
            // Найден хотя бы один активный фильтр
            Возврат Ложь; 
        КонецЕсли;
    КонецЦикла;
    
    // Ни один из фильтров не используется
    Возврат Истина; 
    
КонецФункции

Этот алгоритм чрезвычайно быстр. Поскольку количество измерений у большинства регистров сведений в реальных конфигурациях редко превышает 5–10 полей, цикл отрабатывает мгновенно (за доли микросекунды) и не создает никакой избыточной нагрузки на сервер или клиент.

Способ 2. Быстрое сравнение двух отборов через XML или JSON

Что делать, если нам нужно не просто проверить отбор на пустоту, а быстро сравнить отборы двух разных наборов записей одного и того же регистра? Перебирать все измерения и попарно сопоставлять их значения и признаки использования в цикле — довольно громоздко.

Здесь на помощь приходит альтернативный системный механизм — сериализация в XML или JSON. В отличие от ЗначениеВСтрокуВнутр(), механизмы сериализации платформы выгружают в результирующую структуру только те элементы отбора, у которых свойство Использование установлено в значение Истина. Это позволяет нам легко сравнивать отборы как простые строки.

Посмотрим на пример функции сравнения отборов через сериализацию в JSON:


Функция ОтборыИдентичны(Набор1, Набор2) Экспорт
    
    ЗаписьJSON1 = Новый ЗаписьJSON;
    ЗаписьJSON1.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON1, Набор1.Отбор);
    СтрокаОтбора1 = ЗаписьJSON1.Закрыть();
    
    ЗаписьJSON2 = Новый ЗаписьJSON;
    ЗаписьJSON2.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON2, Набор2.Отбор);
    СтрокаОтбора2 = ЗаписьJSON2.Закрыть();
    
    Возврат СтрокаОтбора1 = СтрокаОтбора2;
    
КонецФункции

Если строки JSON полностью совпадают, это гарантирует, что у обоих наборов записей настроены абсолютно одинаковые активные фильтры с идентичными значениями.

Важный практический кейс: защита регистра от полной очистки

Очень часто задача проверки пустоты отбора возникает при необходимости перехватить критическую ситуацию — попытку полной очистки регистра сведений. Если в коде записать пустой набор записей (без строк) с пустым отбором и параметром замещения, равным Истина, платформа полностью сотрет все данные из таблицы этого регистра в базе данных.

Для предотвращения таких действий логично использовать предопределенный обработчик ПередЗаписью в модуле набора записей регистра. Однако здесь есть критически важный нюанс платформы, о котором должен знать каждый разработчик.

При штатной модификации или перезаписи существующих данных система часто производит операцию записи в два шага: сначала записывается пустой набор (очищаются старые данные по отбору), а затем сразу же записывается новый набор с актуальными строками. Если мы просто заблокируем запись любого пустого набора с пустым отбором, мы нарушим логику работы платформы и вызовем системные ошибки.

Чтобы отличить намеренное деструктивное затирание данных от промежуточного шага транзакции, мы должны анализировать свойство набора Модифицированность(). Рассмотрим пример правильного кода в модуле набора записей:


Процедура ПередЗаписью(Отказ, Замещение)
    
    // Проверяем, что происходит именно запись пустых данных (удаление) с замещением
    Если Замещение И (ЭтотОбъект.Количество() = 0) Тогда
        
        // Ключевая проверка: модифицирован ли набор непосредственно кодом
        Если ЭтотОбъект.Модифицированность() Тогда
            
            // Вызываем нашу функцию проверки активности отборов
            Если ОтборПустой(ЭтотОбъект) Тогда
                Отказ = Истина;
                ВызватьИсключение "Ошибка безопасности: запрещена полная очистка регистра сведений без указания отбора!";
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Как скопировать отбор в другой набор записей

Иногда при проверках в обработчике ПередЗаписью требуется прочитать из базы данных существующие записи по текущему отбору (например, для логирования удаляемых данных). Напрямую присвоить один объект Отбор другому нельзя, но мы можем быстро перенести настройки фильтра в новый вспомогательный набор записей.

Разберем по шагам, как правильно скопировать параметры активного отбора:


// Создаем вспомогательный набор записей для чтения старых данных
ВспомогательныйНабор = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();

// Копируем только активные элементы отбора
Для Каждого Элемент Из ЭтотОбъект.Отбор Цикл
    Если Элемент.Использование Тогда
        ЗаполнитьЗначенияСвойств(ВспомогательныйНабор.Отбор[Элемент.Имя], Элемент, "Значение, Использование, ВидСравнения");
    КонецЕсли;
КонецЦикла;

// Теперь вспомогательный набор готов к безопасному чтению данных из БД
ВспомогательныйНабор.Прочитать();

Использование метода ЗаполнитьЗначенияСвойств позволяет лаконично перенести ключевые параметры фильтрации, включая значение и вид сравнения, не загромождая код ручным присвоением каждого свойства.

← На главную