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

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

При разработке алгоритмов в 1С часто возникает задача получить движения регистра накопления, сформированные документами определенного вида. Попытка решить эту задачу с помощью стандартного метода Выбрать() менеджера регистра часто приводит к ошибке «Недопустимое значение параметра (параметр номер '3')». Разберем причины возникновения этой ошибки и рассмотрим правильные способы выборки данных.

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

Проанализируем исходный фрагмент кода, вызывающий ошибку:


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

Выясним причину такого поведения платформы 1С:

  1. Несоответствие типов данных: Значение Документы.ВыдачаВПрокат является менеджером объекта метаданных, а не ссылкой на конкретный документ. В структуре отбора для регистра, подчиненного регистратору, поле Регистратор строго ожидает точную ссылку типа ДокументСсылка.ВыдачаВПрокат.
  2. Ограничения объектной выборки: Метод Выбрать() у регистров накопления поддерживает отбор только на строгое равенство конкретному значению. Платформа не позволяет передать в отбор тип метаданных, список значений или условие вхождения.
  3. Ограничение набора записей: Использование объекта РегистрНакопленияНаборЗаписей также не решит задачу выборки по типу документа, так как отбор набора записей для регистров с регистратором жестко привязан к конкретной ссылке на документ с видом сравнения Равно.

Способ 1. Использование оператора ССЫЛКА в запросе

Рассмотрим базовый и наиболее простой вариант решения задачи через язык запросов 1С. Для проверки принадлежности регистратора к определенному виду документа применим логический оператор ССЫЛКА.

Посмотрим на пример текста запроса:


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

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

Пока Выборка.Следующий() Цикл
    // Обработка полученных записей
КонецЦикла;

В данном случае нет необходимости передавать какие-либо параметры в запрос: платформа 1С на уровне базы данных самостоятельно проверяет соответствие таблицы-источника ссылки регистратора.

Способ 2. Проверка типа через ТИПЗНАЧЕНИЯ и ТИП

Разберем альтернативную конструкцию языка запросов с использованием функций ТИПЗНАЧЕНИЯ и ТИП. Этот вариант позволяет обойтись без передачи образца документа в параметры запроса.

Посмотрим на пример реализации:


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

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

Способ 3. Оптимизированный запрос через соединение таблиц

Проанализируем производительность при больших объемах данных в регистре. Условия вида ГДЕ Регистратор ССЫЛКА ... в некоторых СУБД могут приводить к полному сканированию таблицы движений. Для максимального быстродействия рекомендуется использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ с таблицей конкретного документа.

Рассмотрим пример построения запроса с соединением:


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

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

Такой подход обеспечивает эффективное использование индексов СУБД по полю Регистратор и гарантирует оптимальный план выполнения запроса.

Способ 4. Получение движений конкретного документа без запроса

Если задача заключается в чтении движений не по всему типу документов, а для одного известного экземпляра документа, наиболее правильным объектным способом является обращение к коллекции Движения:


// Получаем объект документа по ссылке
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();

// Обращаемся к набору записей конкретного регистра
НаборЗаписей = ДокументОбъект.Движения.ПоступлениеИСписаниеКниг;
НаборЗаписей.Прочитать();

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

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

← На главную