Как запросом выбрать последние документы по каждой номенклатуре, включая непроведенные?

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

В практике разработчика 1С часто возникает задача получить актуальные цены или состояния объектов, основываясь не на данных регистров сведений, а напрямую из документов. Это особенно актуально, когда необходимо учитывать «черновики» — непроведенные документы, которые еще не отразились в движениях регистров. Стандартный механизм СрезПоследних здесь не поможет, а использование оператора ПЕРВЫЕ 1 в простом запросе ограничит выборку всего одной строкой вместо того, чтобы вернуть последнюю запись для каждого товара.

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

Проблема использования ПЕРВЫЕ 1 в выборке по группам

Рассмотрим ситуацию: у нас есть номенклатура «Хлеб» с ценами от 10-го и 20-го числа, и номенклатура «Сыр» также с двумя документами от разных дат. Если мы напишем простой запрос с условием ВЫБРАТЬ ПЕРВЫЕ 1 и сортировкой по дате убывающе, система вернет нам только одну строку — самый поздний документ во всей базе (например, по «Сыру»). Наша же цель — получить по одной строке для каждого уникального товара. Для решения этой задачи в языке запросов 1С применяется методика многоступенчатой фильтрации через временные таблицы.

Шаг 1: Подготовка исходных данных во временную таблицу

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


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

Обратим внимание на важный нюанс: мы добавили условие НЕ Ссылка.ПометкаУдаления. Даже если мы работаем с непроведенными документами, пометку на удаление игнорировать нельзя, так как это фактически «удаленные» данные, которые просто физически еще присутствуют в базе.

Шаг 2: Поиск максимальных дат для каждой позиции

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


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

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

Шаг 3: Обработка дублей и получение финальных данных

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

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


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

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

////////////////////////////////////////////////////////////////////////////////
// 3. Находим максимальный номер документа на эту дату (для исключения дублей)
ВЫБРАТЬ
    ВТ.Номенклатура КАК Номенклатура,
    ВТ.Дата КАК Дата,
    МАКСИМУМ(ВТ.Номер) КАК МаксНомер
ПОМЕСТИТЬ ВТ_ДатыНомера
ИЗ
    ВТ_ИсходныеДанные КАК ВТ
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Даты КАК ВТ_Даты
    ПО ВТ.Номенклатура = ВТ_Даты.Номенклатура
    И ВТ.Дата = ВТ_Даты.МаксДата
СГРУППИРОВАТЬ ПО
    ВТ.Номенклатура,
    ВТ.Дата;

////////////////////////////////////////////////////////////////////////////////
// 4. Получаем итоговые данные
ВЫБРАТЬ
    ВТ_Результат.Номенклатура,
    ВТ_Результат.Ссылка,
    ВТ_Результат.Цена,
    ВТ_Результат.Дата
ИЗ
    ВТ_ИсходныеДанные КАК ВТ_Результат
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДатыНомера КАК Фильтр
    ПО ВТ_Результат.Номенклатура = Фильтр.Номенклатура
    И ВТ_Результат.Дата = Фильтр.Дата
    И ВТ_Результат.Номер = Фильтр.МаксНомер;

Дополнительные рекомендации по оптимизации

Проанализируем ситуацию с точки зрения производительности. Прямое обращение к табличным частям (например, Документ.УстановкаЦенНоменклатуры.Товары) — это ресурсозатратная операция, так как эти таблицы в базе данных SQL обычно не имеют индексов по полю Номенклатура.

Для ускорения работы рекомендуется:

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

Также стоит помнить о характеристиках. Если ваш учет ведется в разрезе характеристик, то во всех группировках (СГРУППИРОВАТЬ ПО) и во всех соединениях (ПО) обязательно должно присутствовать поле Характеристика. В противном случае вы получите одну общую «последнюю» цену на товар, которая может относиться к любой его характеристике случайным образом.

Таким образом, мы рассмотрели надежный алгоритм получения последних данных из документов. Несмотря на то что типовые конфигурации (УТ, ERP, КА) ориентированы на работу с регистрами сведений, данный метод незаменим для задач оперативного анализа «черновиков» и документов, находящихся в процессе оформления.

← На главную