Как составить запрос проверки остатков в 1С по нескольким измерениям (Товар и Степень брака)?

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

При проведении документов списания или реализации часто возникает необходимость проверить наличие товара на складе. Если в регистре накопления остатки хранятся не только по номенклатуре, но и по дополнительным измерениям (например, СтепеньБрака, Серия или Характеристика), запрос должен учитывать весь набор этих измерений.

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

Анализ проблемы и частые ошибки

Разберем типичную ситуацию: в документе РеализацияТоваров есть табличная часть Торговля со столбцами Товар, СтепеньБрака и Количество. В регистре накопления ОстаткиТовара хранятся остатки номенклатуры с учетом измерений Товар и СтепеньБрака.

При составлении запроса начинающие разработчики часто совершают следующие ошибки:

  1. Использование параметров отбора по ресурсам в виртуальной таблице: Условие вида КоличествоОстаток < 0 нельзя передавать в параметры виртуальной таблицы Остатки(). Параметры виртуальной таблицы предназначены исключительно для отбора по измерениям и реквизитам регистра. Проверка на нехватку должна выполнять отбор в секции ГДЕ или производиться при обработке результата.
  2. Передача отдельного параметра для каждого значения измерения: Если в документе могут быть товары с разными степенями брака, нельзя передавать один фиксированный параметр &СтепеньБрака. Отбор должен формироваться на основе всего списка пар «Товар — Степень брака», присутствующих в документе.
  3. Множественное обращение к табличной части документа: Повторное считывание табличной части документа в подзапросах замедляет работу СУБД. Оптимальным подходом является однократное помещение строк документа во временную таблицу.

Вариант 1. Использование временных таблиц (Рекомендуемый подход)

Проанализируем наиболее производительный и правильный вариант построения запроса. Сначала мы помещаем данные табличной части документа во временную таблицу, группируя строки по товару и степени брака. За тем мы получаем остатки из виртуальной таблицы только по тем позициям, которые присутствуют в документе.

Рассмотрим пример пакета запросов:


ВЫБРАТЬ
	Торговля.Товар КАК Товар,
	Торговля.СтепеньБрака КАК СтепеньБрака,
	СУММА(Торговля.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_Документ
ИЗ
	Документ.РеализацияТоваров.Торговля КАК Торговля
ГДЕ
	Торговля.Ссылка = &Ссылка
СГРУППИРОВАТЬ ПО
	Торговля.Товар,
	Торговля.СтепеньБрака

ИНДЕКСИРОВАТЬ ПО
	Товар,
	СтепеньБрака
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	Док.Товар КАК Товар,
	Док.СтепеньБрака КАК СтепеньБрака,
	Док.Количество КАК Требуется,
	ЕСТЬНУЛЛ(Остатки.КоличествоОстаток, 0) КАК Остаток,
	Док.Количество - ЕСТЬНУЛЛ(Остатки.КоличествоОстаток, 0) КАК Нехватка
ИЗ
	ВТ_Документ КАК Док
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТовара.Остатки(
				&Момент,
				(Товар, СтепениБрака) В
					(ВЫБРАТЬ
						Т.Товар,
						Т.СтепеньБрака
					ИЗ
						ВТ_Документ КАК Т)) КАК Остатки
		ПО Док.Товар = Остатки.Товар
			И Док.СтепеньБрака = Остатки.СтепениБрака
ГДЕ
	ЕСТЬНУЛЛ(Остатки.КоличествоОстаток, 0) < Док.Количество

Разберем ключевые особенности этого запроса:

  1. Группировка по всем измерениям: В первой временной таблице ВТ_Документ мы суммируем требуемое количество по комбинации полей Товар и СтепеньБрака. Это предотвращает ошибки, если в документе одна и та же позиция с одинаковым браком введена несколькими строками.
  2. Индексация временной таблицы: Оператор ИНДЕКСИРОВАТЬ ПО Товар, СтепеньБрака создает индекс во временной таблице, что существенно ускоряет последующее соединение с остатками в СУБД.
  3. Кортежный отбор (список пар): В параметрах виртуальной таблицы остатков используется конструкция (Товар, СтепениБрака) В (ВЫБРАТЬ ...). Это позволяет платформе извлечь из регистра остатки только для тех сочетаний товара и степени брака, которые реально указаны в документе.
  4. Использование ЕСТЬНУЛЛ: Если товара с указанной степенью брака на складе вообще нет, левое соединение вернет значение NULL. Функция ЕСТЬНУЛЛ(Остатки.КоличествоОстаток, 0) предотвращает ошибки при расчете разницы количества.

Вариант 2. Простой запрос без временных таблиц

Если требуется быстро проверить остатки в неболших документах без создания временных таблиц, разберем структуру однопакетного запроса со связыванием по всем измерениям.

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


ВЫБРАТЬ
	Док.Товар КАК Товар,
	Док.СтепеньБрака КАК СтепеньБрака,
	МАКСИМУМ(Док.НомерСтроки) КАК НомерСтроки,
	ЕСТЬНУЛЛ(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
	(ВЫБРАТЬ
		Торговля.Товар КАК Товар,
		Торговля.СтепеньБрака КАК СтепеньБрака,
		МАКСИМУМ(Торговля.НомерСтроки) КАК НомерСтроки
	ИЗ
		Документ.РеализацияТоваров.Торговля КАК Торговля
	ГДЕ
		Торговля.Ссылка = &Ссылка
	СГРУППИРОВАТЬ ПО
		Торговля.Товар,
		Торговля.СтепеньБрака) КАК Док
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТовара.Остатки(
				&Момент,
				Товар В
					(ВЫБРАТЬ РАЗЛИЧНЫЕ
						Торговля.Товар
					ИЗ
						Документ.РеализацияТоваров.Торговля КАК Торговля
					ГДЕ
						Торговля.Ссылка = &Ссылка)) КАК Остатки
		ПО Док.Товар = Остатки.Товар
			И Док.СтепеньБрака = Остатки.СтепениБрака
ГДЕ
	Остатки.КоличествоОстаток < 0

В данном случае соединение таблиц происходит строго по двум полям: ПО Док.Товар = Остатки.Товар И Док.СтепеньБрака = Остатки.СтепениБрака. Однако при росте объема данных в документе этот вариант уступает по скорости первому способу с использованием временных таблиц.

Рекомендации по передаче параметров в код 1С

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


Запрос = Новый Запрос;
Запрос.Текст = "..."; // Текст запроса из Варианта 1

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

// Формируем границу времени, включая текущий момент
ГраницаВремени = Новый Граница(МоментВремени(), ВидГраницы.Включая);
Запрос.УстановитьПараметр("Момент", ГраницаВремени);

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

Использование объекта Граница с видом ВидГраницы.Включая гарантирует, что запрос учтет или исключит движения текущего документа в зависимости от выбранной методики проведения (старой или новой).

← На главную