К статье
Практический материал

Разработка и Конфигурация · 13 мин

Как обойти регистр сведений в 1С: выборка, запрос и примеры кода

Как обойти регистр сведений в 1С: выборка через менеджер, запрос с виртуальными таблицами СрезПоследних и СрезПервых, примеры кода, типичные ошибки и проверка результата.

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

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

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

Что нужно знать о регистре сведений перед обходом

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

Ещё одно важное свойство — режим записи: подчинение регистратору или независимый. Независимый регистр заполняется напрямую (через набор записей или форму), и обходить его можно свободно. Регистр, подчинённый регистратору, изменяется только движениями документов — обход для чтения не отличается, но записывать в него «вручную» нельзя.

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

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

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

Выборка = РегистрыСведений.ЦеныНоменклатуры.Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить("Номенклатура: " + Выборка.Номенклатура

+ ", цена: " + Выборка.Цена);

КонецЦикла;

Для периодического регистра в Выбрать() можно передать начало и конец интервала дат, а также отбор по измерениям в виде структуры. Это сокращает объём читаемых данных:

Отбор = Новый Структура("Номенклатура", НужнаяНоменклатура);

Выборка = РегистрыСведений.ЦеныНоменклатуры.Выбрать(

НачалоМесяца(ТекущаяДатаСеанса()),

КонецМесяца(ТекущаяДатаСеанса()),

Отбор);

Пока Выборка.Следующий() Цикл

// обработка записи

КонецЦикла;

У выборки есть ограничение: она читает физическую таблицу целиком в указанном диапазоне, без агрегации. Если регистр содержит сотни тысяч записей, а нужна только последняя цена по каждому товару, выборка заставит прочитать всё и отбирать в коде — это медленно. Для такой задачи нужен запрос к срезу последних.

Обход запросом к физической таблице

Запрос к физической таблице регистра работает быстрее цикла по выборке, потому что фильтрация и сортировка выполняются на стороне СУБД. Результат можно обойти через Выбрать() запроса или выгрузить в таблицу значений.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Цены.Номенклатура КАК Номенклатура,

| Цены.Цена КАК Цена

|ИЗ

| РегистрСведений.ЦеныНоменклатуры КАК Цены

|ГДЕ

| Цены.Цена > 0

|

|УПОРЯДОЧИТЬ ПО

| Номенклатура";

Результат = Запрос.Выполнить().Выбрать();

Пока Результат.Следующий() Цикл

// обработка строки

КонецЦикла;

Если записей много и обход делается только для чтения, добавьте конструкцию ДЛЯ ИЗМЕНЕНИЯ только при необходимости блокировки — без неё запрос выполнится быстрее и не будет мешать другим пользователям. Для обхода без модификации данных блокировка не нужна.

Когда результат нужен не для построчной обработки, а для дальнейшей работы (передача в СКД, построение отчёта, обмен), выгружайте результат в таблицу значений методом Выгрузить() — это удобнее, чем ручной обход выборки.

Обход через срез последних и срез первых

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

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Срез.Номенклатура КАК Номенклатура,

| Срез.Цена КАК Цена

|ИЗ

| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(

| &Период, ) КАК Срез";

Запрос.УстановитьПараметр("Период", ТекущаяДатаСеанса());

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

// актуальные цены на дату

КонецЦикла;

Если параметр периода не указать (СрезПоследних(, )), срез строится на самую позднюю дату в регистре. Во втором параметре можно задать условие, например «Номенклатура В (&СписокНоменклатуры)» — это существенно ускоряет построение среза, потому что платформа сначала отфильтрует записи, а потом найдёт последние.

Аналогично работает СрезПервых — возвращает самую раннюю запись на дату. Он нужен реже: например, чтобы узнать первоначальное значение настройки или цену на начало периода.

Программная запись при обходе: набор записей

Частая сценарная задача — обойти регистр и изменить часть записей, например пересчитать значения ресурса. Для записи используется набор записей: создаётся через менеджер регистра, на него накладывается отбор по измерениям (и периоду для периодического), затем набор читается, изменяется и записывается.

Набор = РегистрыСведений.КурсыВалют.СоздатьНаборЗаписей();

Набор.Отбор.Валюта.Установить(Доллар);

Набор.Отбор.Период.Установить(НужнаяДата);

Набор.Прочитать();

Если Набор.Количество() > 0 Тогда

Набор[0].Курс = НовыйКурс;

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

КонецЕсли;

Ключевое правило: перед записью набора обязательно установите отбор по всем измерениям, иначе метод Записать() заменит или очистит все записи регистра. Это одна из самых разрушительных ошибок при работе с регистрами сведений — набор без отбора при записи с параметром Замещение = Истина затирает всю таблицу.

При массовом изменении записей в цикле выполняйте операцию в транзакции (НачатьТранзакцию / ЗафиксироватьТранзакцию), чтобы при сбое база не осталась в частично обновлённом состоянии. Перед массовой обработкой рабочей базы сделайте выгрузку через конфигуратор.

Порядок безопасного массового изменения записей
  1. 1
    Сделайте архивную копию или выгрузку базы через конфигуратор
  2. 2
    Напишите запрос, который только читает данные, и проверьте, что отбираются именно нужные записи
  3. 3
    Добавьте запись через набор записей с обязательным отбором по измерениям
  4. 4
    Оберните цикл записи в транзакцию
  5. 5
    Выполните обработку на копии базы и сверьте количество изменённых записей
  6. 6
    Только после проверки запустите обработку на рабочей базе

Сравнение способов обхода и выбор подходящего

У каждого метода есть область, где он оптимален. Ориентируйтесь на объём данных и на то, нужна ли история или только актуальные значения.

СпособКогда применятьОграничения
Выборка через Выбрать()Небольшие регистры, простая построчная обработка, нужен отбор по измерениям без запросаЧитает физическую таблицу, медленно на больших объёмах
Запрос к физической таблицеНужна вся история записей, фильтрация и сортировка на стороне СУБДНе даёт агрегации «последнее значение» без дополнительных соединений
СрезПоследних / СрезПервыхНужны актуальные (или первые) значения на дату по каждой комбинации измеренийТолько для периодических регистров; дорогая операция без отбора
Набор записейЧтение и изменение конкретных записей с отборомТребует обязательного отбора, иначе риск затереть весь регистр

Сравнение подходов

Выборка в цикле

Подходит для быстрых разовых обработок и малых таблиц; на больших регистрах проигрывает запросу по скорости

Запрос с срезом последних

Оптимален для отчётов и получения актуальных данных; требует аккуратности с параметрами и отборами

Набор записей

Единственный корректный способ программной записи; требует полного отбора по измерениям

Типичные ошибки и как их избежать

Первая ошибка — обращение к виртуальной таблице среза у непериодического регистра. У такого регистра нет периода, поэтому СрезПоследних недоступен: компилятор запроса выдаст ошибку. Для непериодического регистра актуальные данные — это просто вся таблица, обходите её обычным запросом.

Вторая ошибка — соединение таблицы со срезом последних по дате без учёта того, что срез возвращает значение «на дату включительно». Если документ проведён в 10:00, а срез построен на начало этого дня, записи документа в срез не попадут. Проверяйте границы периода: используйте конец дня или точный момент времени.

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

Типичные ошибки
01
Запись набора без отбора по измерениям — затирается вся таблица регистра
02
Использование СрезПоследних без условия во втором параметре на большом регистре — резкое падение производительности
03
Обращение к срезу у непериодического регистра — ошибка компиляции запроса
04
Чтение через Выбрать() в клиентском контексте управляемой формы — ошибка вызова метода
05
Опечатка в имени измерения в тексте запроса — исключение «Поле не найдено» при выполнении

Как проверить результат обхода

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

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

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

Контрольные точки

Как проверить результат

Количество записей в результате запроса
Норма

Совпадает с количеством строк в списке регистра при тех же отборах

Тревожный признак

Запрос вернул меньше или больше — проверьте условие ГДЕ и параметры среза

Значения ресурсов в срезе последних
Норма

Для контрольной комбинации измерений значение совпадает с последней записью в списке регистра

Тревожный признак

Возвращается устаревшее значение — неверно задан параметр периода среза

Состояние регистра после записи
Норма

Изменились только записи под отбором, остальные на месте

Тревожный признак

Пропали или изменились посторонние записи — набор записывался без полного отбора

Итоговая логика выбора проста: нужна вся таблица — запрос или выборка к физической таблице; нужны актуальные значения на дату — срез последних с отбором; нужно изменить данные — набор записей с полным отбором внутри транзакции. Соблюдение этих трёх правил закрывает большинство задач обхода регистров сведений.

Итог

Что важно запомнить

Для чтения всех записей используйте запрос или Выбрать(), для актуальных значений на дату — СрезПоследних с отбором, для изменения — набор записей с обязательным отбором по всем измерениям. Перед массовой записью делайте копию базы и работайте в транзакции.

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

Как обойти регистр сведений, подчинённый регистратору?

Для чтения — так же, как независимый: запросом к таблице регистра или выборкой. Дополнительно доступно поле Регистратор, по которому можно отбирать движения конкретного документа. Программно записывать в такой регистр напрямую нельзя — только через проведение документа-регистратора.

Что быстрее: Выбрать() или запрос?

На малых объёмах разница незаметна. На больших регистрах запрос быстрее, потому что фильтрация выполняется на стороне СУБД, а в код передаются только нужные строки. Выборка же читает записи последовательно и требует программной фильтрации.

Как получить последнюю запись по одному конкретному измерению?

Используйте СрезПоследних с условием во втором параметре, например: РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, Номенклатура = &Номенклатура). Либо выполните запрос к физической таблице с отбором, сортировкой по убыванию периода и ВЫБРАТЬ ПЕРВЫЕ 1.

Можно ли обойти регистр сведений на клиенте в управляемой форме?

Нет, методы Выбрать(), запросы и наборы записей доступны только на сервере. Создайте серверную процедуру в модуле формы или общем модуле с признаком «Сервер» и вызывайте её из клиентского кода.

Почему срез последних возвращает не ту запись, которую я вижу в списке?

Чаще всего дело в параметре периода: срез включает записи с датой меньше или равной указанной. Если передано начало дня, записи, сделанные в течение этого дня, в срез не попадут. Передайте конец дня или точный момент времени документа.

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

Запросом с группировкой: сгруппируйте по измерениям, вычислите Количество(*) и отберите группы, где количество больше одного, через ИМЕЮЩИЕ. Физически дублей по полному набору измерений в регистре быть не должно — платформа контролирует уникальность, но дубли по части измерений таким запросом находятся.