Как правильно получить остатки и обороты в одном запросе 1С и почему пропадают данные при соединении

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

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

Ошибка №1: Условия в секции ГДЕ вместо параметров виртуальной таблицы

Рассмотрим ситуацию: нам нужно получить остатки по конкретной номенклатуре. Часто можно встретить такой код:


ВЫБРАТЬ
    ЗапасыОстатки.КоличествоОстаток
ИЗ
    РегистрНакопления.Запасы.Остатки КАК ЗапасыОстатки
ГДЕ
    ЗапасыОстатки.Номенклатура = &Номенклатура

Это классическая ошибка проектирования. Проанализируем ситуацию: когда мы пишем условие в секции ГДЕ, платформа 1С сначала заставляет SQL-сервер рассчитать остатки по всем товарам в базе, и только потом фильтрует результат. Это создает колоссальную нагрузку на сервер. Правильный подход — передавать фильтр в параметры виртуальной таблицы:


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

В этом случае SQL-сервер сразу начнет работу только с нужным индексом, что ускорит выполнение запроса в десятки и сотни раз.

Ошибка №2: Как секция ГДЕ ломает ПОЛНОЕ СОЕДИНЕНИЕ

Выясним причину, по которой у автора вопроса пропадали данные. В исходном запросе использовалось ПОЛНОЕ СОЕДИНЕНИЕ таблицы остатков и таблицы движений (продаж). Однако в секции ГДЕ было наложено условие на период движений:


ГДЕ
    ЗапасыНаСкладах.Период МЕЖДУ &НачДата И &КонДата

Рассмотрим подробнее, что происходит на уровне логики. Если на складе "Склад №1" есть остаток, но не было продаж, то в полях таблицы ЗапасыНаСкладах (правая таблица) после соединения будет значение NULL. Условие NULL МЕЖДУ &НачДата И &КонДата всегда дает ложь. В итоге SQL-сервер просто выбрасывает все строки, где нет движений, превращая ваше ПОЛНОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ.

Решение задачи: все условия на правую (или левую при соответствующем соединении) таблицу должны быть вынесены либо в параметры виртуальной таблицы, либо непосредственно в условие связи ПО.

Особенности работы со «Срезом последних»: Измерения против Реквизитов

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

Вариант А (в параметрах виртуальной таблицы):


РегистрСведений.Работники.СрезПоследних(&Период, Состояние <> ЗНАЧЕНИЕ(Перечисление.Состояния.Увольнение))

Вариант Б (в секции ГДЕ):


ВЫБРАТЬ ... ИЗ РегистрСведений.Работники.СрезПоследних(&Период, ) КАК Р
ГДЕ Р.Состояние <> ЗНАЧЕНИЕ(Перечисление.Состояния.Увольнение)

Проанализируем разницу:

  1. В Варианте Б платформа сначала находит последнюю запись по сотруднику. Если эта запись — "Увольнение", условие ГДЕ ее отсекает. Итог: сотрудника в результате нет.
  2. В Варианте А записи об увольнении отсекаются до расчета среза последних. Платформа будет искать "последнюю запись среди тех, которые не являются увольнением". И если сотрудник был принят в 2020 году, а уволен в 2021, то запрос вернет запись о приеме от 2020 года как "последнюю актуальную".

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

Проблема оптимизатора СКД

Часто разработчики замечают, что один и тот же запрос в Консоли запросов работает правильно, а в Системе компоновки данных (СКД) выдает неверные остатки. Это происходит из-за механизмов оптимизации СКД:

Рекомендуемый подход: Остатки и Обороты в одном флаконе

Чтобы избежать проблем с соединениями NULL значений и сложной логикой ГДЕ, в 1С предусмотрена специализированная виртуальная таблица ОстаткиИОбороты. Рассмотрим, как эффективно решить задачу автора с её помощью:


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

Этот метод является наиболее производительным и надежным, так как платформа сама заботится о правильном соединении данных и сохранении строк, где есть остатки, но нет оборотов (и наоборот).

Если же задача требует сложной фильтрации движений (например, только по определенным типам документов, как Чек ККМ), используйте в СКД объединение наборов данных. Создайте один набор для остатков и второй для продаж, а затем свяжите их по измерениям на закладке "Связи наборов данных". Это избавит вас от проблем с ПОЛНЫМ СОЕДИНЕНИЕМ в тексте одного тяжелого запроса.

← На главную