При разработке алгоритмов в 1С часто возникает задача получить движения регистра накопления, сформированные документами определенного вида. Попытка решить эту задачу с помощью стандартного метода Выбрать() менеджера регистра часто приводит к ошибке «Недопустимое значение параметра (параметр номер '3')». Разберем причины возникновения этой ошибки и рассмотрим правильные способы выборки данных.
Проанализируем исходный фрагмент кода, вызывающий ошибку:
Отбор = Новый Структура("Регистратор", Документы.ВыдачаВПрокат);
Выборка = РегистрыНакопления.ПоступлениеИСписаниеКниг.Выбрать(,,Отбор);
Выясним причину такого поведения платформы 1С:
Документы.ВыдачаВПрокат является менеджером объекта метаданных, а не ссылкой на конкретный документ. В структуре отбора для регистра, подчиненного регистратору, поле Регистратор строго ожидает точную ссылку типа ДокументСсылка.ВыдачаВПрокат.Выбрать() у регистров накопления поддерживает отбор только на строгое равенство конкретному значению. Платформа не позволяет передать в отбор тип метаданных, список значений или условие вхождения.РегистрНакопленияНаборЗаписей также не решит задачу выборки по типу документа, так как отбор набора записей для регистров с регистратором жестко привязан к конкретной ссылке на документ с видом сравнения Равно.Рассмотрим базовый и наиболее простой вариант решения задачи через язык запросов 1С. Для проверки принадлежности регистратора к определенному виду документа применим логический оператор ССЫЛКА.
Посмотрим на пример текста запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПоступлениеИСписаниеКниг.Регистратор КАК Регистратор,
| ПоступлениеИСписаниеКниг.Книга КАК Книга,
| ПоступлениеИСписаниеКниг.Количество КАК Количество
|ИЗ
| РегистрНакопления.ПоступлениеИСписаниеКниг КАК ПоступлениеИСписаниеКниг
|ГДЕ
| ПоступлениеИСписаниеКниг.Регистратор ССЫЛКА Документ.ВыдачаВПрокат";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Обработка полученных записей
КонецЦикла;
В данном случае нет необходимости передавать какие-либо параметры в запрос: платформа 1С на уровне базы данных самостоятельно проверяет соответствие таблицы-источника ссылки регистратора.
Разберем альтернативную конструкцию языка запросов с использованием функций ТИПЗНАЧЕНИЯ и ТИП. Этот вариант позволяет обойтись без передачи образца документа в параметры запроса.
Посмотрим на пример реализации:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПоступлениеИСписаниеКниг.Регистратор КАК Регистратор,
| ПоступлениеИСписаниеКниг.Книга КАК Книга
|ИЗ
| РегистрНакопления.ПоступлениеИСписаниеКниг КАК ПоступлениеИСписаниеКниг
|ГДЕ
| ТИПЗНАЧЕНИЯ(ПоступлениеИСписаниеКниг.Регистратор) = ТИП(Документ.ВыдачаВПрокат)";
РезультатЗапроса = Запрос.Выполнить();
Проанализируем производительность при больших объемах данных в регистре. Условия вида ГДЕ Регистратор ССЫЛКА ... в некоторых СУБД могут приводить к полному сканированию таблицы движений. Для максимального быстродействия рекомендуется использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ с таблицей конкретного документа.
Рассмотрим пример построения запроса с соединением:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПоступлениеИСписаниеКниг.Регистратор КАК Регистратор,
| ПоступлениеИСписаниеКниг.Книга КАК Книга,
| ПоступлениеИСписаниеКниг.Количество КАК Количество
|ИЗ
| РегистрНакопления.ПоступлениеИСписаниеКниг КАК ПоступлениеИСписаниеКниг
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ВыдачаВПрокат КАК ВыдачаВПрокат
| ПО ПоступлениеИСписаниеКниг.Регистратор = ВыдачаВПрокат.Ссылка";
РезультатЗапроса = Запрос.Выполнить();
Такой подход обеспечивает эффективное использование индексов СУБД по полю Регистратор и гарантирует оптимальный план выполнения запроса.
Если задача заключается в чтении движений не по всему типу документов, а для одного известного экземпляра документа, наиболее правильным объектным способом является обращение к коллекции Движения:
// Получаем объект документа по ссылке
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
// Обращаемся к набору записей конкретного регистра
НаборЗаписей = ДокументОбъект.Движения.ПоступлениеИСписаниеКниг;
НаборЗаписей.Прочитать();
Для Каждого Движение Из НаборЗаписей Цикл
// Обработка строк набора записей
Сообщить(Движение.Книга);
КонецЦикла;
Этот метод считывает только те записи, которые принадлежат указанному документу, не требуя ручного формирования структуры отбора.