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

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

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

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

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

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

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

Как устроен регистр сведений и что от этого зависит

Перед чтением важно понимать структуру конкретного регистра: от неё зависит, какие методы доступны. Откройте конфигуратор, найдите регистр в ветке «Регистры сведений» и посмотрите его свойства: периодичность (непериодический, в пределах секунды, дня, месяца, года), режим записи (независимый или подчинён регистратору), список измерений и ресурсов.

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

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

Чтение одной записи через менеджер регистра

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

bsl
Функция ПолучитьЦенуТовара(Номенклатура, ТипЦен)

Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", Номенклатура);
Отбор.Вставить("ТипЦен", ТипЦен);

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

Если Запись.Количество() > 0 Тогда
    Возврат Запись.Цена;
Иначе
    Возврат 0;
КонецЕсли;

КонецФункции

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

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

bsl
Отбор = Новый Структура("Валюта", ВалютаUSD);
ДанныеКурса = РегистрыСведений.КурсыВалют.ПолучитьПоследнее(ДатаДокумента, Отбор);

Если ДанныеКурса.Свойство("Курс", Курс) И Курс > 0 Тогда
    СуммаВРублях = СуммаВВалюте * Курс;
Иначе
    ВызватьИсключение "Не задан курс валюты на дату " + Формат(ДатаДокумента, "ДЛФ=D");
КонецЕсли;

Методы ПолучитьПоследнее() и ПолучитьПервое() физически обращаются к виртуальной таблице среза, поэтому по скорости сопоставимы с запросом. Но помните: каждый вызов — отдельное обращение к базе. В цикле по тысяче товаров такой код выполнит тысячу обращений, и это станет узким местом.

Чтение через запрос: выборка и соединения

Запрос — основной инструмент массового чтения. Простейший вариант — выбрать все записи или записи с отбором:

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

Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
    Сообщить(Строка(Результат.Номенклатура) + ": " + Результат.Цена);
КонецЦикла;

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

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

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

Левое соединение и ЕСТЬNULL защищают от ситуации, когда для части номенклатуры цена не задана: строка не потеряется, а в поле цены вернётся ноль. Это осознанное решение — в рабочем коде вместо нуля часто оставляют NULL и обрабатывают отдельно, чтобы отличить «цены нет» от «цена равна нулю».

Срез первых и чтение истории

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

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

Перебор записей через Выбрать()

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

bsl
Отбор = Новый Структура("Организация", Организация);
Выборка = РегистрыСведений.ОтветственныеЛица.Выбрать(,, Отбор);

Пока Выборка.Следующий() Цикл
    Если Не ЗначениеЗаполнено(Выборка.Ответственный) Тогда
        Выборка.Ответственный = ОтветственныйПоУмолчанию;
        Выборка.Записать();
    КонецЕсли;
КонецЦикла;

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

Чтение регистра, подчинённого регистратору

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

Для этого удобнее всего набор записей с отбором по регистратору:

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

Для Каждого Запись Из НаборЗаписей Цикл
    Сообщить("Статус: " + Запись.Статус);
КонецЦикла;

Тот же результат даёт запрос с условием «Регистратор = &Ссылка» — он предпочтительнее, если данные нужны только для чтения и участвуют в соединениях с другими таблицами. Набор записей оправдан, когда после чтения записи будут изменены или удалены.

Сравнение способов чтения и критерии выбора

Сравнение вариантов

Получить / ПолучитьПоследнее

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

Запрос к физической таблице

Массовое чтение, соединения, история периодического регистра. Только чтение, записи изменить нельзя.

СрезПоследних / СрезПервых в запросе

Актуальные значения на дату для множества объектов. Обязательно задавайте отбор в параметрах виртуальной таблицы.

Выбрать() / набор записей

Обход с возможностью изменения, чтение движений документа. Медленнее запроса, не для больших объёмов.

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

Маршрут решения

Как выбрать действие

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

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

После выполнения кода проверьте три вещи: вернулась ли запись вообще, заполнены ли нужные ресурсы, соответствует ли период ожиданиям. Пустая структура из Получить(), NULL в поле запроса и дата среза позже последней записи — три самых частых источника «пропавших» данных.

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

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

Результат ПолучитьПоследнее()
Норма

Структура содержит ресурсы, значения заполнены

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

Пустая структура или пустые значения — записи на эту дату нет

Поле из среза последних в запросе
Норма

Значение заполнено для всех строк с заданной ценой

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

NULL — для части ключей записей нет, нужна обработка ЕСТЬNULL

Период возвращённой записи
Норма

Период меньше или равен дате среза

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

Дата среза раньше первой записи — срез пуст

Типичные ошибки
01
Вызов ПолучитьПоследнее() в цикле по строкам — сотни обращений к базе, проведение документа замедляется в десятки раз. Решение: один запрос со срезом последних.
02
Отбор по измерению в секции ГДЕ вместо параметров виртуальной таблицы — срез строится по всему регистру, запрос резко деградирует на больших данных.
03
Обращение к ресурсу без проверки: Запись.Цена, когда записи нет, вызывает ошибку «Поле объекта не обнаружено». Сначала проверяйте результат.
04
Чтение подчинённого регистра через ПолучитьПоследнее() — метод неприменим, используйте запрос или набор записей с отбором по регистратору.
05
Путаница между датой среза и периодом записи: срез на начало дня не включает записи, сделанные позже в этот же день.

Отдельная ловушка — границы дат. СрезПоследних(&Дата) включает записи с периодом, меньшим или равным параметру. Если передать начало дня, а курс установлен в 10:00 этого же дня, запись не попадёт в срез. Для документов обычно передают дату документа целиком либо конец дня, если логика требует учитывать все записи дня.

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

Часто задаваемые вопросы

Чем отличается Получить() от ПолучитьПоследнее()?

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

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

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

Можно ли прочитать регистр сведений без прав на изменение?

Да, для чтения достаточно права «Чтение» на регистр в роли пользователя. Все описанные способы — запросы, Получить(), Выбрать() — требуют только его. Право «Изменение» понадобится, только если код записывает данные через Записать() или набор записей.

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

Никак — программное чтение регистров доступно только в серверном контексте. Оформите серверную процедуру с директивой &НаСервере, выполните чтение там и верните результат на клиента через параметры или реквизиты формы. Прямой вызов РегистрыСведений на клиенте недоступен в тонком и веб-клиенте.

Что быстрее: запрос к срезу последних или перебор Выбрать()?

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

Как получить все значения ресурса за период, а не только последнее?

Обратитесь запросом напрямую к физической таблице регистра с условием «Период МЕЖДУ &Начало И &Конец» и упорядочиванием по полю Период. Виртуальные таблицы срезов для этого не подходят — они возвращают по одной записи на комбинацию измерений.

Итог

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

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