Как ограничить набор записей регистра: Отбор.Установить, запросы и СКД

· 9 мин

Содержание

Отбор в наборе записей регистра устанавливают методом Установить() у элемента отбора: Набор.Отбор.Регистратор.Установить(Документ) или Набор.Отбор.Номенклатура.Установить(Товар). После этого Прочитать() загрузит только подходящие записи, а Записать() заменит в базе ровно их. Это механизм платформы 1С:Предприятие 8.3, от конфигурации он не зависит.

Набор записей понимает только условие «равно» и только по полям, которые платформа разрешает для этого вида регистра. Всё сложнее — интервалы, списки, «не равно» — делается запросом или отбором системы компоновки данных. Ниже разобраны все три способа и случаи, где каждый уместен.

Как устроен отбор набора записей и чем он отличается от условия в запросе

У объекта РегистрСведенийНаборЗаписей, РегистрНакопленияНаборЗаписей и других наборов есть свойство Отбор. Это коллекция элементов, по одному на каждое разрешённое поле. Элемент хранит три вещи:

  • 🔹 Значение — с чем сравнивать;
  • 🔹 Использование — включено ли условие;
  • 🔹 ВидСравнения — всегда «равно», изменить его нельзя.

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

Какие поля доступны в отборе

Вид регистраПоля отбора набора
Регистр сведений, подчинённый регистраторуРегистратор
Регистр сведений, независимый, периодическийПериод и все измерения
Регистр сведений, независимый, непериодическийВсе измерения
Регистр накопленияРегистратор
Регистр бухгалтерииРегистратор
Регистр расчётаРегистратор

Отсюда частое разочарование: у регистра накопления нельзя написать Набор.Отбор.Склад.Установить(...). Такого элемента в коллекции нет, будет ошибка «Поле объекта не обнаружено». Выбрать остатки по складу можно только запросом.

Когда что использовать

ЗадачаИнструмент
Перезаписать движения документаОтбор набора по Регистратор
Изменить записи независимого регистра сведений по измерениямОтбор набора по измерениям и Период
Прочитать данные с условиями «больше», «в списке», «в иерархии»Запрос с ГДЕ или параметрами виртуальной таблицы
Дать пользователю фильтр в отчёте или спискеОтбор СКД

Установка отбора в наборе записей программно

Порядок всегда одинаковый: создать набор, установить отбор, прочитать или заполнить, записать.

Независимый регистр сведений

В примере — периодический регистр сведений ЦеныТоваров с измерениями Товар и ВидЦены и ресурсом Цена. Задача: установить новую цену на дату.

Набор = РегистрыСведений.ЦеныТоваров.СоздатьНаборЗаписей();
Набор.Отбор.Период.Установить(ДатаЦены);
Набор.Отбор.Товар.Установить(Товар);
Набор.Отбор.ВидЦены.Установить(ВидЦены);

Запись = Набор.Добавить();
Запись.Период  = ДатаЦены;
Запись.Товар   = Товар;
Запись.ВидЦены = ВидЦены;
Запись.Цена    = НоваяЦена;

Набор.Записать(); // заменит только запись с этими датой, товаром и видом цены

Значения полей новой записи должны совпадать с отбором. Если в записи окажется другой товар, платформа откажет в записи: запись не соответствует отбору набора.

Чтение и изменение существующих записей

Набор = РегистрыСведений.ЦеныТоваров.СоздатьНаборЗаписей();
Набор.Отбор.Товар.Установить(Товар);
Набор.Отбор.ВидЦены.Установить(ВидЦены);
Набор.Прочитать();

Для Каждого Запись Из Набор Цикл
    Запись.Цена = Окр(Запись.Цена * 1.1, 2);
КонецЦикла;

Набор.Записать();

Здесь Период не задан, поэтому набор прочитает всю историю цен товара по виду цены. Если цена 1000 руб. на 01.03 и 1200 руб. на 01.06, после записи будет 1100 и 1320 руб.

Движения документа

Для регистров с регистратором набор фильтруют по ссылке на документ:

Набор = РегистрыНакопления.ОстаткиТоваров.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
Набор.Прочитать();
// корректировка записей
Набор.Записать();

В модуле проведения писать это вручную не нужно. Наборы в коллекции Движения уже отобраны по текущему документу: достаточно заполнить Движения.ОстаткиТоваров и установить Движения.ОстаткиТоваров.Записывать = Истина.

Полезные методы коллекции Отбор

  • 🔸 Набор.Отбор.Товар.Установить(Значение, Ложь) — задать значение, но не включать условие;
  • 🔸 Набор.Отбор.Сбросить() — снять все условия;
  • 🔸 Набор.Отбор.Найти("Товар") — получить элемент по имени, если имя поля приходит строкой;
  • 🔸 Набор.Отбор.Товар.Использование = Ложь — отключить одно условие, не трогая значение.

Вариант с Найти нужен в универсальном коде, который обходит измерения из метаданных:

Для Каждого Измерение Из Метаданные.РегистрыСведений.ЦеныТоваров.Измерения Цикл
    ЭлементОтбора = Набор.Отбор.Найти(Измерение.Имя);
    Если ЭлементОтбора <> Неопределено Тогда
        ЭлементОтбора.Установить(ИсходнаяЗапись[Измерение.Имя]);
    КонецЕсли;
КонецЦикла;

Несколько наборов в одной транзакции

Если меняете цены сразу по нескольким видам цен, пишите все наборы в одной транзакции. Тогда при ошибке на третьем наборе первые два откатятся, и в регистре не останется половины изменений.

НачатьТранзакцию();
Попытка
    Для Каждого ВидЦены Из СписокВидовЦен Цикл
        Набор = РегистрыСведений.ЦеныТоваров.СоздатьНаборЗаписей();
        Набор.Отбор.Период.Установить(ДатаЦены);
        Набор.Отбор.Товар.Установить(Товар);
        Набор.Отбор.ВидЦены.Установить(ВидЦены);
        Запись = Набор.Добавить();
        Запись.Период  = ДатаЦены;
        Запись.Товар   = Товар;
        Запись.ВидЦены = ВидЦены;
        Запись.Цена    = НоваяЦена;
        Набор.Записать();
    КонецЦикла;
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

Набор создаётся заново на каждой итерации. Если переиспользовать один объект и только менять значение отбора, не забудьте Набор.Очистить(): иначе в наборе останутся записи прошлого вида цены, и запись упадёт с ошибкой несоответствия отбору.

Перед вызовом Записать()
Выполнено: 0 / 5

Записать() и Записать(Ложь): что произойдёт с данными

Метод Записать() принимает параметр Замещение, по умолчанию Истина.

ВызовЧто делает в базе
Набор.Записать()Удаляет все записи, подходящие под отбор, и пишет содержимое набора
Набор.Записать(Ложь)Добавляет записи набора к существующим, ничего не удаляя
Набор.Записать() при пустом набореПросто удаляет записи по отбору

Последний вариант — штатный способ удалить записи регистра сведений по измерению или движения документа:

Набор = РегистрыСведений.ЦеныТоваров.СоздатьНаборЗаписей();
Набор.Отбор.Товар.Установить(Товар);
Набор.Записать(); // вся история цен товара удалена

Набор без отбора при Записать() очищает весь регистр. Если опечатка в условии Если пропустила строку с Установить, после записи в регистре останутся только записи из набора. Проверяйте такой код на копии базы.

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

Условия отбора в конструкторе запросов

Когда набору записей не хватает возможностей, данные выбирают запросом. Конструктор запросов открывается в конфигураторе из контекстного меню модуля: Конструктор запроса....

Вкладка «Условия»

  1. На вкладке «Таблицы и поля» выберите таблицу регистра.
  2. Перейдите на вкладку «Условия».
  3. Перетащите поле, например Склад, в нижнюю часть окна.
  4. Конструктор подставит условие Склад = &Склад и создаст параметр.
  5. Для «больше», «В», «ПОДОБНО» отметьте флажок «Произвольное» и напишите выражение вручную.

Условия с этой вкладки попадают в секцию ГДЕ.

Параметры виртуальной таблицы

Для виртуальных таблиц Остатки, Обороты, СрезПоследних отбор лучше задавать не в ГДЕ, а в их параметрах. В конструкторе выделите таблицу в списке выбранных и нажмите «Параметры виртуальной таблицы». В поле «Условие» впишите отбор.

ВЫБРАТЬ
    Остатки.Товар,
    Остатки.КоличествоОстаток
ИЗ
    РегистрНакопления.ОстаткиТоваров.Остатки(&НаДату, Склад = &Склад) КАК Остатки

Разница существенная. Условие в параметрах применяется до расчёта остатков. Условие в ГДЕ применяется к уже рассчитанной таблице по всем складам — на базе с двадцатью складами это лишняя работа в десятки раз.

В параметрах виртуальной таблицы используйте только измерения регистра. Ресурсы там недоступны, а условие по ресурсу КоличествоОстаток > 0 ставьте в ГДЕ.

Программное выполнение запроса

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Остатки.Товар,
|    Остатки.КоличествоОстаток
|ИЗ
|    РегистрНакопления.ОстаткиТоваров.Остатки(&НаДату, Склад = &Склад) КАК Остатки
|ГДЕ
|    Остатки.КоличествоОстаток > 0";
Запрос.УстановитьПараметр("НаДату", КонецДня(ТекущаяДатаСеанса()));
Запрос.УстановитьПараметр("Склад", Склад);

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Строка(Выборка.Товар) + ": " + Формат(Выборка.КоличествоОстаток, "ЧГ=0"));
КонецЦикла;

Связку «запрос + набор» применяют, когда нужно изменить записи независимого регистра сведений по неравенству. Запрос находит ключи нужных записей, а для каждого ключа создаётся свой набор с отбором по равенству.

Отбор через систему компоновки данных

Отбор СКД — третий вариант. Он работает в отчётах, динамических списках и везде, где есть компоновщик настроек. Виды сравнения здесь широкие: «равно», «в списке», «в иерархии», «больше», «содержит», «заполнено».

Отбор в отчёте

Отбор = КомпоновщикНастроек.Настройки.Отбор;
Элемент = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных("Склад");
Элемент.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
Элемент.ПравоеЗначение = Склад;
Элемент.Использование  = Истина;

Имя в ПолеКомпоновкиДанных — это путь к полю набора данных схемы, а не имя измерения в метаданных. Если в схеме поле переименовано, используйте новое имя.

Отбор динамического списка

У динамического списка на форме тоже есть Отбор, устроенный так же. В типовых конфигурациях на БСП удобнее вызвать готовую процедуру:

ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
    Список, "Склад", Склад, ВидСравненияКомпоновкиДанных.Равно, , ЗначениеЗаполнено(Склад));

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

Отбор по периоду в регистрах сведений и накопления

С периодом проще всего ошибиться, поэтому разберём его отдельно.

Периодический регистр сведений

Платформа приводит период записи к началу периода регистра. У регистра с периодичностью «Месяц» запись на 15.03 сохранится с датой 01.03. Отбор же сравнивает значение буквально. Поэтому для месячного регистра устанавливайте приведённую дату:

Набор.Отбор.Период.Установить(НачалоМесяца(Дата));

С датой 15.03 в отборе Прочитать() вернёт пустой набор, хотя запись за март есть.

Отбор по Период ставится только на одну дату. Нужен интервал — используйте запрос к таблице регистра с условием Период МЕЖДУ &Начало И &Конец или виртуальную таблицу СрезПоследних(&НаДату, ...).

Регистры накопления

У набора регистра накопления отбора по периоду нет вообще. Период задаётся у каждой записи, а набор связан с документом. Для выборки за интервал есть виртуальная таблица Обороты(&Начало, &Конец, , Склад = &Склад) и ОстаткиИОбороты.

Граница интервала включается в расчёт по моменту времени. Для конца дня передавайте КонецДня(Дата), иначе движения того дня после полуночи не попадут.

Производительность и типичные ошибки

Как не замедлить базу

  • ✅ Ставьте отбор в параметры виртуальной таблицы, а не в ГДЕ.
  • ✅ Пишите набор один раз после всех изменений, а не после каждой строки.
  • ✅ Ограничивайте Прочитать() полным отбором: набор без отбора по большому регистру загрузит в память всё.
  • ✅ В циклах по сотням ключей выбирайте данные одним запросом, а не сотней наборов с Прочитать().
  • ✅ Перед изменением наборов в транзакции ставьте управляемую блокировку по тем же полям: БлокировкаДанных с ЭлементБлокировки.УстановитьЗначение("Товар", Товар).

Частые ошибки

Диагностика: выберите симптом

Частые вопросы

Можно ли поставить в отбор набора записей список значений?
Нет. Отбор набора поддерживает только равенство одному значению. Для списка создайте отдельный набор на каждое значение или выберите данные запросом с условием В (&Список).
Чем отличается набор записей от менеджера записи?
Менеджер записи работает с одной записью независимого регистра сведений по полному ключу. Набор с отбором обрабатывает любое число записей и есть у всех видов регистров.
Нужен ли отбор, если набор взят из коллекции Движения?
Нет. Наборы в Движения уже отобраны по регистратору — текущему документу. Перезаписывать отбор не нужно.
Как удалить одну запись регистра сведений кодом?
Установите отбор по периоду и всем измерениям этой записи и вызовите Записать() у пустого набора. Удалится только она.
Почему у регистра сведений нет отбора по Регистратор?
У независимого регистра сведений регистратора нет. Отбор по Регистратор появляется, только если в свойствах регистра выбран режим записи «Подчинение регистратору».