Почему запрос с левым соединением возвращает остаток, а прямой запрос к регистру пуст?

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

При разработке и отладке механизмов оперативного контроля остатков в 1С (в частности, в конфигурациях на базе «Управление торговлей 10.3») разработчики нередко сталкиваются с ситуацией, когда сложный запрос с левым соединением табличной части документа и виртуальной таблицы остатков возвращает ненулевой остаток, тогда как отдельный изолированный запрос к той же виртуальной таблице РегистрНакопления.ТоварыОрганизаций.Остатки возвращает пустой результат. Рассмотрим подробно причины возникновения подобной проблемы и разберем по шагам методы её диагностики и устранения.

Причина 1. Различие в детализации разрезов учета (характеристики, серии, качество)

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


|	ПО Док.Номенклатура = Остатки.Номенклатура
|	И Док.ХарактеристикаНоменклатуры = Остатки.ХарактеристикаНоменклатуры
|	И Док.СерияНоменклатуры = Остатки.СерияНоменклатуры
|	И Док.Качество = Остатки.Качество

Если мы создаем тестовый проверочный запрос и выбираем из виртуальной таблицы только поля Организация, Номенклатура и КоличествоОстаток, без детализации по характеристикам и сериям, результат может вводить в заблуждение. Если в регистре присутствуют движения с заполненными сериями или характеристиками, а в тестовом запросе они не отображаются или отсекаются неявными отборами, мы получим принципиально разные выборки.

Посмотрим на пример правильного запроса для проверки остатков с полным набором измерений:


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

Запрос.УстановитьПараметр("МоментВремени", ГраницаКонтроля);
Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);
Запрос.УстановитьПараметр("Организация", ДокументСсылка.Организация);
Запрос.УстановитьПараметр("Комиссионер", Справочники.Контрагенты.ПустаяСсылка());

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

Причина 2. Особенности агрегатных функций и группировок

Обратим внимание на конструкцию расчета полей в первом запросе:


|	СУММА(ВЫРАЗИТЬ(Док.Количество * Док.Коэффициент / Док.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент КАК ЧИСЛО(15, 3))) КАК ДокументКоличество,
|	ЕСТЬNULL(МАКСИМУМ(Остатки.КоличествоОстаток), 0) КАК ОстатокКоличество,
|	Остатки.Организация

Использование функции МАКСИМУМ для получения остатка при левом соединении таит в себе риски:

  1. Группировка по полям правой таблицы: поле Остатки.Организация включено в секцию СГРУППИРОВАТЬ ПО. Если соединение не находит соответствия в правой таблице, поле принимает значение NULL. Это может разбить результат на непредвиденные группы строк.
  2. Размножение записей: если в регистре накопления есть остатки по другим характеристикам или сериям, которые частично подошли под неполное условие соединения, МАКСИМУМ вернет наибольшее значение из найденных строк, создавая иллюзию наличия требуемого остатка.

Причина 3. Рассинхронизация итогов регистра накопления

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

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

Разберем по шагам, как устранить рассинхронизацию данных:

  1. Ручное перепроведение документов: перепроведем по цепочке документы движения по проблемной позиции номенклатуры (начиная с документа оприходования или поступления). При повторном проведении платформа заново формирует набор записей регистра и актуализирует итоги.
  2. Пересчет итогов регистра: выполним процедуру пересчета итогов. Это можно сделать программно для конкретного регистра: РегистрыНакопления.ТоварыОрганизаций.ПересчитатьИтоги();
  3. Тестирование и исправление: запустим процедуру «Тестирование и исправление информационной базы» в режиме «Конфигуратор», установив флаг «Пересчет итогов».

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

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

  1. Не использовать подзапросы в параметрах виртуальных таблиц: конструкция вида Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ ...) внутри параметров таблицы Остатки препятствует эффективной оптимизации плана выполнения на уровне СУБД. Рекомендуется помещать список номенклатуры документа в индексированную временную таблицу и соединять её с виртуальной таблицей остатков через ЛЕВОЕ СОЕДИНЕНИЕ.
  2. Всегда передавать момент времени: первым параметром виртуальной таблицы остатков всегда передаем точную границу времени: Новый Граница(ДокументСсылка.МоментВремени(), ВидГраницы.ВключаяГраницу) или ВидГраницы.ИсключаяГраницу в зависимости от учетной логики проведения. Пустой параметр заставляет систему рассчитывать остаток на текущую системную дату.
← На главную