В процессе разработки отчетов или написания обработок в 1С:Предприятие 8.3 часто возникает задача: получить информацию о самом последнем визите клиента. Это может быть дата последней покупки, сумма последнего чека или ссылка на конкретный документ реализации. На первый взгляд задача кажется простой, но она таит в себе подводные камни, связанные с производительностью и дублированием данных, особенно если у одного контрагента может быть несколько документов в одну и ту же секунду.
В этой статье мы подробно разберем, как правильно составить запрос в консоли для конфигурации Управление торговлей 10.3, проанализируем различные подходы и выберем наиболее оптимальный вариант, который не будет «тормозить» систему.
Прежде всего, нам нужно определиться, что именно мы считаем «последним чеком». В программе Управление торговлей 10.3 продажи могут оформляться разными документами:
Если мы будем делать запрос напрямую к таблицам документов, нам придется учитывать их все. Однако существует более универсальный способ — использование регистров накопления. Например, регистр Продажи хранит информацию о всех совершенных сделках вне зависимости от того, каким документом они были сделаны. Тем не менее, в рамках данного разбора мы рассмотрим классический подход запроса к таблицам документов, как это обсуждалось на форуме.
Разберем базовую логику. Нам нельзя просто выбрать все документы и отсортировать их, если нам нужен список всех покупателей с их последними покупками. Если мы просто укажем ПЕРВЫЕ 1, мы получим один единственный чек по всей базе. Нам же нужно сгруппировать данные.
Рассмотрим первый этап запроса. Мы должны отобрать уникальных контрагентов и найти для каждого из них максимальное значение поля Дата. Проанализируем пример кода:
ВЫБРАТЬ
Реализация.Контрагент КАК Контрагент,
МАКСИМУМ(Реализация.Дата) КАК МаксДата
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Проведен = ИСТИНА
И Реализация.Дата МЕЖДУ &НачПериода И &КонПериода
СГРУППИРОВАТЬ ПО
Реализация.Контрагент
;
В данном фрагменте мы используем временную таблицу ВТ_МаксимальныеДаты. Обратите внимание на условие Реализация.Проведен — это критически важный момент. Если мы его не укажем, в отчет могут попасть черновики или отмененные документы, которые не являются фактом продажи.
Теперь, когда у нас есть пары «Контрагент — Дата последней покупки», нам нужно «вытащить» саму ссылку на документ и его дополнительные реквизиты (сумму, склад и т.д.). Для этого мы соединим нашу временную таблицу с основной таблицей документов.
Выясним причину, по которой простого соединения по дате может быть недостаточно. В 1С время хранится с точностью до секунды. Если менеджер создал два документа для одного клиента в одну и ту же секунду, обычное соединение по полям Контрагент и Дата вернет две строки вместо одной. Чтобы этого избежать, профессиональные разработчики используют соединение по максимальной ссылке.
Рассмотрим финальный вариант запроса, который решает проблему дублей:
ВЫБРАТЬ
ТЗ_Даты.Контрагент,
МАКСИМУМ(докРТУ.Ссылка) КАК Ссылка
ПОМЕСТИТЬ ВТ_ПоследниеДокументы
ИЗ
ВТ_МаксимальныеДаты КАК ТЗ_Даты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК докРТУ
ПО ТЗ_Даты.Контрагент = докРТУ.Контрагент
И ТЗ_Даты.МаксДата = докРТУ.Дата
ГДЕ
докРТУ.Проведен = ИСТИНА
СГРУППИРОВАТЬ ПО
ТЗ_Даты.Контрагент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ПоследниеДокументы.Контрагент КАК Покупатель,
ПоследниеДокументы.Ссылка.Дата КАК ДатаЧека,
ПоследниеДокументы.Ссылка КАК ПоследнийДокумент,
ПоследниеДокументы.Ссылка.СуммаДокумента КАК Сумма
ИЗ
ВТ_ПоследниеДокументы КАК ПоследниеДокументы
УПОРЯДОЧИТЬ ПО
ДатаЧека УБЫВ
Посмотрим на пример из Сообщения 19 на форуме. Там предлагалось использовать конструкцию ИЗ (ВЫБРАТЬ...) КАК тзПоследняяСделка. Это называется вложенным запросом. Хотя для небольших баз данных это допустимо, на высоконагруженных системах СУБД (например, MS SQL Server) может некорректно построить план запроса, что приведет к зависанию отчета.
Мы рекомендуем использовать оператор ПОМЕСТИТЬ. Это создает временную таблицу на стороне сервера базы данных, индексирует ее и позволяет оптимизатору запросов работать быстрее. Кроме того, такой код гораздо легче читать и отлаживать в консоли запросов.
Проанализируем ситуацию, если автор темы имел в виду именно розничные чеки. В УТ 10.3 документ ЧекККМ имеет свои особенности. После проведения операции «Закрытие кассовой смены» все чеки за день помечаются на удаление или архивируются, а их движения переходят в документ ОтчетОРозничныхПродажах.
Поэтому, если вы хотите найти действительно последнюю покупку в рознице, ваш запрос должен обращаться либо к двум типам документов (и объединять их через ОБЪЕДИНИТЬ ВСЕ), либо работать с регистром накопления Продажи. Рассмотрим пример запроса к регистру:
ВЫБРАТЬ
Продажи.Контрагент,
МАКСИМУМ(Продажи.Период) КАК МаксДата
ПОМЕСТИТЬ ВТ_ДатыРегистра
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Период МЕЖДУ &НачПериода И &КонПериода
СГРУППИРОВАТЬ ПО
Продажи.Контрагент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Даты.Контрагент,
МАКСИМУМ(Продажи.Регистратор) КАК ПоследнийЧек
ИЗ
ВТ_ДатыРегистра КАК Даты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи КАК Продажи
ПО Даты.Контрагент = Продажи.Контрагент
И Даты.МаксДата = Продажи.Период
СГРУППИРОВАТЬ ПО
Даты.Контрагент
Преимущества работы с регистром:
Разберем несколько важных моментов, которые помогут сделать ваш запрос еще лучше:
Контрагент. Для этого после названия временной таблицы добавьте ИНДЕКСИРОВАТЬ ПО Контрагент.Контрагент часто бывает пустым (Розничный покупатель). Если вам нужны только конкретные клиенты, добавьте условие ГДЕ Контрагент <> ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).Таким образом, мы выяснили, что для получения даты последнего чека оптимально использовать двухэтапный запрос с поиском максимальной даты и последующим соединением. Использование временных таблиц и работа через регистры накопления — это признаки профессионального подхода к разработке на платформе 1С:Предприятие.