Как правильно передать параметры в запрос и выбрать движения по всем элементам справочника

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

При разработке на платформе 1С:Предприятие 8 начинающие программисты часто сталкиваются с ситуацией, когда запрос не отрабатывает или выдает ошибку при попытке установить параметр. Типичный сценарий: необходимо получить данные из регистра сведений для всех или нескольких элементов справочника. В этой статье мы подробно разберем, почему возникают ошибки при передаче выборки в параметры запроса, и как реализовать задачу эффективно с помощью механизмов языка запросов.

Разбор основной ошибки: Выборка vs Ссылка

Рассмотрим ситуацию, представленную в исходном коде. Автор пытается выполнить следующий фрагмент:


СпрСредства = Справочники.Средства.Выбрать();
// ... код запроса ...
Запрос.УстановитьПараметр("Средство", СпрСредства);

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

Язык запросов 1С ожидает в параметре либо конкретное значение (например, СправочникСсылка), либо массив значений, либо таблицу значений. Передача выборки приводит к ошибке чтения данных, так как СУБД не понимает, как сопоставить объект перебора с полем ссылки в таблице базы данных.

Решение 1: Использование массива ссылок

Если нам действительно нужно передать в запрос конкретный список элементов, полученных программно, мы должны использовать массив ссылок и оператор В. Рассмотрим, как переписать этот код правильно:


МассивСсылок = Новый Массив;
Выборка = Справочники.Средства.Выбрать();
Пока Выборка.Следующий() Цикл
    МассивСсылок.Добавить(Выборка.Ссылка);
КонецЦикла;

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

Запрос.УстановитьПараметр("МассивСредств", МассивСсылок);

В данном случае мы явно формируем коллекцию ссылок, которую запрос может обработать. Однако этот метод не является оптимальным, если нам нужно обработать все элементы справочника, существующие в базе.

Решение 2: Соединение таблиц (Оптимальный способ)

Вместо того чтобы сначала выбирать элементы справочника в объектную память, а затем передавать их обратно в запрос, мы можем решить задачу целиком на уровне SQL-запроса. Это значительно повышает производительность и уменьшает потребление оперативной памяти.

Рассмотрим вариант, когда нам нужно получить движения только по тем средствам, у которых они есть (Внутреннее соединение):


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

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

Важный момент: Обратите внимание, что мы убрали условие ГДЕ и параметр &Средство. Система сама сопоставит таблицы по полю Средства.

Решение 3: Проверка наличия записей (Левое соединение)

Часто возникает вопрос: "Как проверить, есть ли у элемента справочника регистратор или нет?". Для этого идеально подходит Левое соединение. Проанализируем, как это работает: в результат попадут все элементы справочника "Средства", а для тех, по которым нет записей в регистре, поля периода и регистратора будут заполнены значением NULL.


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

Выясним причину использования функции ЕСТЬNULL. Она позволяет заменить неопределенное значение в базе данных на понятный текст или пустую ссылку, что удобно для вывода в отчеты.

Рекомендации по оптимизации запросов

Рассмотрим подробнее несколько важных правил, которые помогут избежать ошибок производительности при работе с регистрами и справочниками:

  1. Избегайте запросов в цикле. Никогда не пытайтесь подставлять элементы справочника по одному в цикле Для Каждого. Это создает огромную нагрузку на сервер. Используйте соединения таблиц, как показано выше.
  2. Обращение через точку. В тексте запроса конструкция ДвиженияСредств.Средства.Ссылка избыточна. Поле Средства уже является ссылкой. Лишняя точка заставляет систему создавать дополнительное неявное соединение с таблицей справочника в SQL-запросе, что замедляет выполнение. Пишите просто: ДвиженияСредств.Средства.
  3. Использование виртуальных таблиц. Если вам не нужна вся история движений, а только текущее состояние, используйте виртуальную таблицу СрезПоследних. Она работает значительно быстрее физической таблицы регистра.
  4. Индексация. Убедитесь, что поле Средства в регистре сведений является измерением или для него установлен признак Индексировать. Без этого поиск по данному полю при большом количестве записей будет крайне медленным.

Итоги

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

← На главную