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