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

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

Анализ проблемы: почему метод Остатки() выдает ошибку

Рассмотрим типичную ситуацию. Программист пытается использовать метод менеджера регистра накопления Остатки(). Код выглядит примерно так:


Отбор = Новый Массив;
Отбор.Добавить(Склад1);
Отбор.Добавить(Склад2);

Рег = РегистрыНакопления.ТоварыНаСкладах;
ТаблицаИтогов = Рег.Остатки(ТекущаяДата, Отбор, , "Количество");

В данном случае система выдаст ошибку. Давайте выясним причину. Метод Остатки() в объектной модели 1С ожидает в качестве второго параметра структуру (Структура), где ключи — это имена измерений регистра, а значения — это конкретные ссылки, числа или строки. Объектный метод не умеет работать с массивами или списками значений. Он выполняет поиск только по строгому равенству. Если вы передадите массив там, где ожидается ссылка на склад, система не сможет сопоставить типы и выдаст ошибку параметров.

Решение через механизм запросов

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

Проанализируем правильный подход к построению запроса. Допустим, нам нужно получить остатки по массиву складов и конкретной номенклатуре.

Вариант 1. Оптимизированный запрос (Рекомендуется)

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


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

Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("МассивСкладов", СписокСкладов); // Массив или СписокЗначений
Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);

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

Почему это работает быстро? В данном примере условие Склад В (&МассивСкладов) передано непосредственно в параметры виртуальной таблицы Остатки. Это заставляет платформу 1С формировать SQL-запрос таким образом, чтобы выборка из таблиц итогов происходила уже с учетом фильтра. СУБД не считывает лишние данные, что критически важно для высоконагруженных систем.

Вариант 2. Ошибка использования предложения ГДЕ

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


// ПЛОХОЙ ПРИМЕР
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТоварыНаСкладахОстатки.КоличествоОстаток
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки(&Период) КАК ТоварыНаСкладахОстатки
    |ГДЕ
    |	ТоварыНаСкладахОстатки.Склад В (&МассивСкладов)";

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

Тонкости работы с временем и актуальностью итогов

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

Если мы хотим включить движения документов, зафиксированных в ту же секунду, используем следующий подход:


ГраницаОстатков = Новый Граница(КонецДня(НужнаяДата), ВидГраницы.Включая);
Запрос.УстановитьПараметр("Период", ГраницаОстатков);

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

Дополнительные факторы производительности

Если даже оптимизированный запрос выполняется долго, проанализируем состояние базы данных по следующим пунктам:

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

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

← На главную