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

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

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

В этой статье мы подробно разберем, как правильно составить запрос, чтобы эффективно получить по одному документу для каждой строки справочника, рассмотрим классический метод «Максимальной даты» и обсудим вопросы производительности.

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

Этот способ считается «золотым стандартом» в 1С. Разберем ситуацию на примере: нам нужно найти последние документы ПоступлениеТоваровУслуг для всего справочника Номенклатура. Проблема в том, что если мы просто сгруппируем документы по номенклатуре и дате, мы получим дату, но не саму ссылку на документ. Если мы добавим ссылку в группировку, то получим все документы за эту дату.

Для решения мы разделим задачу на три этапа. Рассмотрим их подробнее:

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

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


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

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

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

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

Метод 2. Использование среза последних из регистра сведений

Проанализируем ситуацию, когда в системе ведется учет цен поставщиков. В типовых конфигурациях 1С (УТ 10.3, КА 1.1, УПП) при проведении поступления часто записываются данные в регистр сведений ЦеныНоменклатурыКонтрагентов. Если ваша задача — просто найти «последнюю цену» или факт последнего поступления через цену, работа с виртуальной таблицей СрезПоследних будет в десятки раз быстрее, чем запрос к табличным частям документов.

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


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

Этот метод предпочтителен, если включена соответствующая настройка регистрации цен. Основное преимущество здесь в том, что платформа 1С на уровне СУБД уже оптимизировала получение последней записи для каждой комбинации измерений (Номенклатура, Контрагент, ТипЦен).

Метод 3. Группировка по МАКСИМУМУ ссылки (Упрощенный)

Если нам не критична точность по времени (секундам), а достаточно просто получить «какой-то последний» документ, можно применить упрощенную схему с группировкой по МАКСИМУМ(Ссылка). Выясним причину, почему это работает: ссылки в 1С (GUID) для документов одного типа, созданных последовательно, обычно возрастают, хотя это и не гарантируется на 100%. Однако в рамках запроса МАКСИМУМ(Ссылка) вернет один уникальный идентификатор для каждой группы номенклатуры.

Пример упрощенного запроса:


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

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

Оптимизация и типичные ошибки

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

  1. Индексация: При использовании временных таблиц всегда индексируйте поле Номенклатура (и Ссылка, если по ней идет соединение). Это значительно ускоряет работу JOIN на стороне SQL-сервера.
  2. Фильтрация: Старайтесь ограничивать выборку документов по дате (например, за последний год), если база данных очень большая. Запрос ко всем документам ПоступлениеТоваровУслуг.Товары за 10 лет может привести к чрезмерной нагрузке на дисковую подсистему.
  3. Избегайте ПЕРВЫЕ 1 во вложенных запросах: Как было отмечено в обсуждении на форуме, конструкция ВЫБРАТЬ ПЕРВЫЕ 1... УПОРЯДОЧИТЬ ПО Дата УБЫВ внутри соединения не позволит вам сопоставить документ конкретной номенклатуре из внешнего запроса. Платформа просто выберет один самый последний документ из всей таблицы и попытается соединить его со всеми строками номенклатуры.
  4. Работа с Моментом Времени: Если важна строгая хронология, вместо Дата лучше использовать МоментВремени. Однако помните, что в языке запросов нельзя использовать функцию МАКСИМУМ напрямую к полю МоментВремени. В этом случае используется связка Дата + Ссылка.

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

← На главную