Как получить даты последнего поступления и последней продажи товаров в 1С:Управление торговлей 10.3 с учетом оптимизации и отбора по складу?

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

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

Оптимизация запроса для даты последнего поступления

Изначально, для получения даты последнего поступления, многие программисты склонны использовать связку типа Регистратор.Дата, как показано в первом варианте запроса:


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

Этот подход имеет существенный недостаток с точки зрения производительности. Обращение к полю Регистратор.Дата через "точку" приводит к неявному левому соединению с таблицей документа-регистратора для каждой записи регистра. На больших базах данных это может привести к значительному замедлению выполнения запроса.

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


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

В этом запросе мы заменили ПартииТоваровНаСкладах.Регистратор.Дата на ПартииТоваровНаСкладах.Период. Это позволяет избежать неявных соединений и значительно ускорить получение даты последнего поступления.

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


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

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

Получение даты последней продажи

Для определения даты последней продажи основным источником данных в 1С:Управление торговлей (УТ) является регистр накопления Продажи. Этот регистр фиксирует все операции продажи товаров. Аналогично дате поступления, для получения последней даты продажи мы будем использовать поле Период регистра и агрегатную функцию МАКСИМУМ.

Давайте посмотрим на запрос для получения даты последней продажи:


ВЫБРАТЬ
    Продажи.Номенклатура,
    МАКСИМУМ(Продажи.Период) КАК ДатаПоследнейПродажи
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Количество > 0
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура

Здесь мы используем:

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

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


ВЫБРАТЬ
    Продажи.Номенклатура,
    Продажи.ХарактеристикаНоменклатуры,
    МАКСИМУМ(Продажи.Период) КАК ДатаПоследнейПродажи
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Количество > 0
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура,
    Продажи.ХарактеристикаНоменклатуры

Крайне важный момент для производительности: независимо от того, как глубоко в прошлое вам нужна информация, для больших баз данных всегда старайтесь ограничивать период выборки данных. Если вам не нужна история продаж за 10 лет, а только за последний год или два, добавьте соответствующее условие в ГДЕ, например: Продажи.Период >= &ДатаНачалаПериода. Это значительно сократит объем обрабатываемых данных и ускорит запрос.

Комбинирование данных о поступлениях и продажах с учетом производительности

Когда нам необходимо получить одновременно и дату последнего поступления, и дату последней продажи для одного и того же списка товаров, эффективным подходом является использование временных таблиц (ВТ). Этот метод позволяет разделить сложный запрос на несколько более простых шагов, каждый из которых может быть оптимизирован, а затем соединить результаты. Мы создадим одну временную таблицу для поступлений и другую для продаж, а затем объединим их.

Разберем по шагам:

1. Получение даты последнего поступления во временную таблицу (ВТ)

Сначала мы формируем временную таблицу с датами последних поступлений. Как мы уже выяснили, лучше использовать поле Период и ограничить отбор по иерархии номенклатуры, если это требуется. Для повышения производительности при последующем соединении, мы проиндексируем эту временную таблицу по полю Номенклатура.


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

Здесь ИНДЕКСИРОВАТЬ ПО Номенклатура создает индекс во временной таблице, что критически важно для скорости дальнейшего соединения.

2. Получение даты последней продажи во временную таблицу (ВТ)

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


ВЫБРАТЬ
    Продажи.Номенклатура,
    МАКСИМУМ(Продажи.Период) КАК ДатаПоследнейПродажи
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Количество > 0
    И Продажи.Номенклатура В (ВЫБРАТЬ ВТ_Поступления.Номенклатура ИЗ ВТ_Поступления КАК ВТ_Поступления)
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура
ИНДЕКСИРОВАТЬ ПО
    Номенклатура;

Условие Продажи.Номенклатура В (ВЫБРАТЬ ВТ_Поступления.Номенклатура ИЗ ВТ_Поступления КАК ВТ_Поступления) помогает отфильтровать данные о продажах, чтобы обрабатывать только релевантную номенклатуру, что также способствует ускорению запроса. Если вам нужны продажи для всей номенклатуры независимо от поступлений, это условие можно убрать.

3. Соединение временных таблиц

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


ВЫБРАТЬ
    ВТ_Поступления.Номенклатура,
    ВТ_Поступления.ДатаПоступления,
    ВТ_Продажи.ДатаПоследнейПродажи
ИЗ
    ВТ_Поступления КАК ВТ_Поступления
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Продажи КАК ВТ_Продажи
        ПО ВТ_Поступления.Номенклатура = ВТ_Продажи.Номенклатура;

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

Решение проблемы отсутствия склада в регистре "Продажи"

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

Рассмотрим несколько вариантов решения этой задачи:

Вариант 1: Извлечение склада через документ-регистратор

Этот способ предполагает получение информации о складе непосредственно из документа, который является регистратором движения в регистре Продажи. Например, это может быть Документ.ОтчетОРозничныхПродажах или Документ.РеализацияТоваровУслуг. Вы можете обратиться к полю "Склад" через ссылку на документ-регистратор.

Пример запроса:


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

Важный аспект производительности: Использование "точки" в условиях запроса, таких как Продажи.ДокументПродажи.Склад, по-прежнему приводит к неявным левым соединениям с таблицами документов-регистраторов. Если таких документов много или период выборки большой, это может значительно замедлить выполнение запроса. Каждый раз, когда вы обращаетесь к полю через "точку" (например, Ссылка.Поле, Регистратор.Поле), система неявно выполняет соединение. Для оптимизации, если возможно, старайтесь избегать таких конструкций в условиях ГДЕ или в группировке.

Для случая, когда склад может отсутствовать (например, в некоторых типах документов или при некорректном заполнении), рекомендуется использовать функцию IsNull, чтобы избежать ошибок и получить "пустую ссылку" склада:


ВЫБРАТЬ
    Продажи.Номенклатура,
    МАКСИМУМ(Продажи.Период) КАК ДатаПоследнейПродажи,
    IsNull(Продажи.ДокументПродажи.Склад, Значение(Справочник.Склады.ПустаяСсылка)) КАК Склад
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Количество > 0
    И IsNull(Продажи.ДокументПродажи.Склад, Значение(Справочник.Склады.ПустаяСсылка)) В (&Склад)
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура,
    IsNull(Продажи.ДокументПродажи.Склад, Значение(Справочник.Склады.ПустаяСсылка))

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

Вариант 2: Использование регистра накопления "ТоварыНаСкладах"

Альтернативный и часто более производительный способ получения данных о продажах с учетом склада – это использование регистра накопления ТоварыНаСкладах. Этот регистр всегда содержит измерение "Склад". Для получения данных о продажах, мы будем отбирать движения с ВидДвижения = Расход и, при необходимости, фильтровать по типу документа-регистратора.


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

В этом запросе мы:

  1. Отбираем движения с ВидДвижения = Расход.
  2. Фильтруем по типу регистратора (ССЫЛКА Документ.XYZ), чтобы убедиться, что это именно продажные документы, а не, например, перемещения или списания.
  3. Используем КоличествоОборот > 0, если работаем с виртуальной таблицей Обороты, или Количество > 0 для основной таблицы регистра.
  4. Осуществляем отбор по ТоварыНаСкладах.Склад, который является прямым измерением регистра.

Этот подход часто бывает более быстрым, так как поле Склад является измерением регистра ТоварыНаСкладах и, как правило, хорошо индексируется, что исключает необходимость в "глубоких" неявных соединениях.

Включение в результат товаров без продаж

Часто требуется, чтобы в итоговый отчет попадали все интересующие нас товары, даже если по ним не было ни одной продажи. Если мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ или фильтруем таблицу продаж по номенклатуре, которая уже есть в поступлениях, товары без продаж будут исключены из результата.

Для решения этой задачи мы будем использовать ЛЕВОЕ СОЕДИНЕНИЕ. Общая логика такова: сначала мы формируем полный список интересующей нас номенклатуры (например, из регистра остатков или поступлений), а затем левым соединением добавляем к нему информацию о продажах. Если для какого-то товара продажи не найдены, соответствующие поля из таблицы продаж будут иметь значение NULL.

Разберем это на примере, объединяющем получение поступлений и продаж, а также учитывающем склад и вывод товаров без продаж:

1. Получение даты последней продажи для конкретного склада во временную таблицу (ВТ_Продажи)

Сначала мы собираем информацию о продажах, включая отбор по складу. Используем виртуальную таблицу Обороты регистра Продажи, так как она позволяет удобно работать с отборами. Мы также учтем, что по нашим данным, продажи осуществляются только через ОтчетОРозничныхПродажах. Если у вас могут быть другие документы, вам нужно будет добавить их в условие отбора.


ВЫБРАТЬ
    Продажи.Номенклатура,
    МАКСИМУМ(Продажи.ДокументПродажи.Дата) КАК ДатаПоследнейПродажи
ПОМЕСТИТЬ ВтПродажи
ИЗ
    РегистрНакопления.Продажи.Обороты(
        ,
        ,
        ,
        Номенклатура В (ВЫБРАТЬ ВТ.Номенклатура ИЗ ВТ_Поступления КАК ВТ) И ДокументПродажи.Склад В (&Склад)
    ) КАК Продажи
ГДЕ
    Продажи.КоличествоОборот > 0
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура
ИНДЕКСИРОВАТЬ ПО
    Номенклатура;

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

2. Формирование списка всей номенклатуры, по которой были поступления (ВТНомПоступала)

Далее мы формируем список всех уникальных номенклатур, по которым были поступления на интересующий нас склад. Это будет наша "базовая" таблица, к которой мы будем присоединять данные о продажах.


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПартииТоваровНаСкладах.Номенклатура,
    ПартииТоваровНаСкладах.Склад
ПОМЕСТИТЬ ВТНомПоступала
ИЗ
    РегистрНакопления.ПартииТоваровНаСкладах КАК ПартииТоваровНаСкладах
ГДЕ
    ПартииТоваровНаСкладах.Склад В (&Склад)
    И ПартииТоваровНаСкладах.ВидДвижения = &ВидДвиженияПриход
ИНДЕКСИРОВАТЬ ПО
    Номенклатура, Склад;

Примечание: Если у вас уже есть ВТ_Поступления, как в примере выше, вы можете использовать её для этого шага, просто дополнив её складом, если он там не был.

3. Окончательное соединение с учетом товаров без продаж

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


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

В этом итоговом запросе мы:

  1. Используем ВТНомПоступала как основу для вывода всех товаров.
  2. Присоединяем ВтПродажи с помощью ЛЕВОЕ СОЕДИНЕНИЕ.
  3. Используем функцию IsNull(ВтПродажи.ДатаПоследнейПродажи, ДатаВремя(1, 1, 1)). Это позволяет заменить значение NULL (для товаров без продаж) на минимальную дату 01.01.0001. Это удобно для дальнейшей обработки или отображения, так как NULL может быть нежелательным.
  4. Для получения даты поступления нам потребуется еще одно ЛЕВОЕ СОЕДИНЕНИЕ с самим регистром ПартииТоваровНаСкладах, так как мы хотим найти максимальную дату поступления для каждой конкретной пары "номенклатура-склад" из нашей базовой таблицы. Для этого мы снова используем МАКСИМУМ(ПартииТоваровНаСкладах.Период) и группируем по нему.

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

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

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

Шаг 1: Получение даты последнего поступления по складу во временную таблицу (ВТ_ПоступленияПоСкладу)


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

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

Шаг 2: Получение даты последней продажи по складу во временную таблицу (ВТ_ПродажиПоСкладу)

Здесь мы используем регистр ТоварыНаСкладах для получения информации о продажах, поскольку он содержит измерение "Склад" и позволяет напрямую фильтровать по нему.


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

В этом запросе мы отбираем только те движения, которые являются продажами, используя условие ССЫЛКА Документ.XYZ. Это обеспечивает точность данных. Опять же, индексируем для производительности.

Шаг 3: Объединение данных из временных таблиц

Теперь мы можем соединить наши две временные таблицы. Используя ЛЕВОЕ СОЕДИНЕНИЕ, мы гарантируем, что все товары, по которым были поступления на склад, будут в итоговом результате, даже если по ним не было продаж.


ВЫБРАТЬ
    ВТ_ПоступленияПоСкладу.Номенклатура,
    ВТ_ПоступленияПоСкладу.Склад,
    ВТ_ПоступленияПоСкладу.ДатаПоступления,
    ИСNULL(ВТ_ПродажиПоСкладу.ДатаПоследнейПродажи, ДатаВремя(1, 1, 1)) КАК ДатаПоследнейПродажи
ИЗ
    ВТ_ПоступленияПоСкладу КАК ВТ_ПоступленияПоСкладу
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПродажиПоСкладу КАК ВТ_ПродажиПоСкладу
        ПО ВТ_ПоступленияПоСкладу.Номенклатура = ВТ_ПродажиПоСкладу.Номенклатура
           И ВТ_ПоступленияПоСкладу.Склад = ВТ_ПродажиПоСкладу.Склад;

В этом финальном запросе:

  1. ВТ_ПоступленияПоСкладу является ведущей таблицей, обеспечивая вывод всех товаров, по которым были поступления.
  2. ЛЕВОЕ СОЕДИНЕНИЕ с ВТ_ПродажиПоСкладу добавляет информацию о продажах, или NULL, если продаж не было.
  3. ИСNULL(..., ДатаВремя(1, 1, 1)) заменяет NULL значения на минимальную дату, что упрощает дальнейшую обработку данных в отчетах или интерфейсе.

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

Дополнительные советы по оптимизации

При работе с запросами в 1С всегда следует помнить о нескольких общих принципах оптимизации:

  1. Ограничение периода выборки: Это самый простой и эффективный способ ускорить запросы, особенно для регистров накопления с большой историей. Если вам не нужны данные за все время существования базы, всегда указывайте ограничение по дате.
  2. Индексирование временных таблиц: Как мы уже видели, использование ИНДЕКСИРОВАТЬ ПО для временных таблиц значительно улучшает производительность при последующих соединениях и выборках.
  3. Пакетные запросы: Разделение сложного запроса на несколько более простых с использованием временных таблиц (пакетные запросы) часто позволяет достичь лучшей производительности, так как каждый подзапрос может быть оптимизирован отдельно, а промежуточные результаты кэшируются.
  4. Избегание "глубоких" соединений через "точку": Старайтесь минимизировать использование конструкций типа Регистратор.Дата или ДокументПродажи.Склад в условиях ГДЕ или в группировках, так как это приводит к неявным и часто медленным соединениям. Если такая информация критична, рассмотрите варианты с явным СОЕДИНЕНИЕМ или использованием регистров, где нужные поля являются прямыми измерениями.
  5. Использование виртуальных таблиц: Виртуальные таблицы регистров накопления (например, Обороты, ОстаткиИОбороты) часто бывают хорошо оптимизированы для агрегированных данных. Используйте их, когда это уместно, но помните, что для получения именно "последней даты" может потребоваться дополнительная агрегация.
  6. Анализ запросов: Если запрос все равно работает медленно, используйте инструменты анализа запросов 1С или профайлер вашей СУБД (например, SQL Profiler для MS SQL Server). Это поможет выявить "узкие места" и понять, какая часть запроса занимает больше всего времени.
  7. Избегание В Иерархии для больших иерархий: Для очень больших иерархических справочников конструкция В Иерархии может быть крайне ресурсоемкой. Если вы работаете с фиксированным списком групп или элементов, рассмотрите возможность предварительной подготовки этого списка в отдельной временной таблице или передаче его как параметра.

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

← На главную