← Назад к вопросам
Остаток товара по складам на каждый день
2.8 Senior🔥 221 комментариев
#Запросы и оптимизация#Регистры
Условие
Напишите запрос, который выводит остаток товара по складам на каждый день в заданном интервале, даже если в этот день не было движений.
Дано:
- Справочники "Номенклатура" и "Склады"
- Регистр накопления "ОстаткиТоваров" с измерениями "Номенклатура", "Склад" и ресурсом "Количество"
Пример
Интервал: 01.01.2024 - 03.01.2024
| Дата | Склад | Номенклатура | Остаток |
|---|---|---|---|
| 01.01.2024 | Основной | Товар 1 | 100 |
| 02.01.2024 | Основной | Товар 1 | 100 |
| 03.01.2024 | Основной | Товар 1 | 85 |
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Это классическая задача на кросс-джойн дат и складов с остатками. Нужно получить остаток на каждую дату, даже если движений не было.
Способ 1: С использованием вспомогательной таблицы дат
ДатаНачала = '2024-01-01';
ДатаОкончания = '2024-01-03';
Запрос = Новый Запрос();
Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
Запрос.Текст =
"""ВЫБРАТЬ
Остатки.Дата,
Остатки.Склад,
Остатки.Номенклатура,
Остатки.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(
@ДатаОкончания,
Дата,
Номенклатура,
Склад
) КАК Остатки
ГДЕ
Остатки.Дата >= @ДатаНачала
И Остатки.Дата <= @ДатаОкончания
ПОРЯДОКПО
Остатки.Дата,
Остатки.Склад,
Остатки.Номенклатура""";
Таблица = Запрос.Выполнить().Выгрузить();
Проблема: если в какой-то день не было движений по товару на складе, такая строка не выведется.
Способ 2: Через кросс-джойн (правильный подход)
ДатаНачала = '2024-01-01';
ДатаОкончания = '2024-01-03';
Запрос = Новый Запрос();
Запрос.УстановитьПараметр("ДатаНачала", ДатаНачала);
Запрос.УстановитьПараметр("ДатаОкончания", ДатаОкончания);
Запрос.Текст =
"""ВЫБРАТЬ
Даты.Дата,
Склады.Ссылка КАК Склад,
Номенклатура.Ссылка КАК Номенклатура,
ВЫБОР
КОГДА Остатки.Количество ЕСТЬ NULL
ТОГДА 0
ИНАЧЕ Остатки.Количество
КОНЕЦ КАК Остаток
ИЗ
Справочник.Склады КАК Склады,
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки(
@ДатаОкончания,
Номенклатура,
Склад
) КАК Остатки
ПО Остатки.Номенклатура = Номенклатура.Ссылка
И Остатки.Склад = Склады.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ РАЗЛИЧНЫЕ
ДатаНач + РАЗНИЦА_ДНЕЙ * 86400 КАК Дата
ИЗ
(ВЫБРАТЬ РАЗЛИЧНЫЕ 0 КАК РАЗНИЦА_ДНЕЙ
ОБЪЕДИНИТЬ
ВЫБРАТЬ 1
ОБЪЕДИНИТЬ
ВЫБРАТЬ 2)
) КАК Даты
ПО Остатки.Дата >= Даты.Дата
ГДЕ
Даты.Дата >= @ДатаНачала
И Даты.Дата <= @ДатаОкончания
ПОРЯДОКПО
Даты.Дата,
Склады.Ссылка,
Номенклатура.Ссылка""";
Способ 3: Самый надёжный (через программный цикл)
ДатаНачала = '2024-01-01';
ДатаОкончания = '2024-01-03';
Результат = Новая ТаблицаЗначений();
Результат.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
Результат.Колонки.Добавить("Склад", Новый ОписаниеТипов("Справочник.Склады"));
Результат.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("Справочник.Номенклатура"));
Результат.Колонки.Добавить("Остаток", Новый ОписаниеТипов("Число"));
// Получаем все склады и номенклатуру
Запрос = Новый Запрос();
Запрос.Текст =
"""ВЫБРАТЬ
Склады.Ссылка КАК СкладСсылка,
Номенклатура.Ссылка КАК НоменклатураСсылка
ИЗ
Справочник.Склады КАС Склады,
Справочник.Номенклатура КАК Номенклатура
ГДЕ
НЕ Склады.ПометкаУдаления
И НЕ Номенклатура.ПометкаУдаления""";
ТаблицаСкладыНоменклатура = Запрос.Выполнить().Выгрузить();
ТекущаяДата = ДатаНачала;
Пока ТекущаяДата <= ДатаОкончания Цикл
Для Каждого Строка Из ТаблицаСкладыНоменклатура Цикл
// Получаем остаток на эту дату
Остаток = РегистрНакопления.ОстаткиТоваров.ПолучитьОстаток(
ТекущаяДата,
Новая Структура(
"Номенклатура", Строка.НоменклатураСсылка,
"Склад", Строка.СкладСсылка
)
);
НоваяСтрока = Результат.Добавить();
НоваяСтрока.Дата = ТекущаяДата;
НоваяСтрока.Склад = Строка.СкладСсылка;
НоваяСтрока.Номенклатура = Строка.НоменклатураСсылка;
НоваяСтрока.Остаток = Остаток;
КонецЦикла;
ТекущаяДата = ТекущаяДата + 86400;
КонецЦикла;
Ключевые моменты решения:
Виртуальная таблица "Остатки":
- Она автоматически считает остатки с учётом всех движений
- Параметры: (ДатаКонца, Периодичность, Измерения)
- Периодичность "Дата" показывает остаток на каждую дату
Кросс-джойн:
- Используется декартово произведение всех комбинаций дат, складов, номенклатуры
- ЛЕВОЕ СОЕДИНЕНИЕ гарантирует строки даже без движений (остаток = 0)
- ВЫБОР КОГДА NULL ТОГДА 0 преобразует NULL в ноль
Программный цикл:
- Самый простой и надёжный способ
- Подходит для малых-средних объёмов данных
- ПолучитьОстаток() вычисляет остаток эффективнее, чем виртуальная таблица
Рекомендация: Используй Способ 3 (программный цикл) — он наиболее понятен и стабилен.