September 20

СрезПоследних без регистра

Возникла тут задача поиска последних документов по определенной аналитике. Регистр ради такого создавать как-то странно. Но в памяти записано, что без него почему-то сложно. Давайте же, наконец, разберемся...

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

Решение в лоб:

Выбрать Контрагент, Максимум(Ссылка)
Из Документ.РеализацияТоваровУслуг
Сгруппировать по Контрагент

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

Проверим правильность результата тоже запросом:

Выбрать * Из
(
 Выбрать Контрагент,
 Количество(Ссылка) Как Количество,
 Минимум(Ссылка) Как Первый,
 Максимум(Ссылка) Как Последний
 Из Документ.РеализацияТоваровУслуг Где Контрагент в
 	(
 	 Выбрать Контрагент
 	 из Документ.РеализацияТоваровУслуг
 	 Сгруппировать по Контрагент
 	 Имеющие Количество(*) > 1
 	)
 Сгруппировать по Контрагент
) Как Подзапрос
Где Первый.Дата > Последний.Дата

И борода! Ошибок конечно меньше половины, но они есть и их много. Значит, простое решение в лоб не годится. Жаль.

Гуглим, яндексим... Большинство убеждено в таком подходе:

Выбрать Последние.Контрагент, Последние.Ссылка
Из Документ.РеализацияТоваровУслуг Как Последние
Внутреннее Соединение
(
 Выбрать Контрагент, Максимум(Дата) Как МаксДата
 Из Документ.РеализацияТоваровУслуг
 Сгруппировать По Контрагент
) Как Даты
По Последние.Контрагент = Даты.Контрагент
и Последние.Дата = Даты.МаксДата

Вроде все логично, оно работает. Даже проверять не надо. Ура! Но одинокие робкие голоса в форумах задают неудобный вопрос: что будет если два документа с одним контрагентом умещаются в одну секунду. А будет плохо. Соединение, как и положено, выведет обе записи. Тут можно поразмышлять о том, что такое последний документ, если их два в секунду. Например, в моей задаче такое почти невозможно, а уж если произойдет, вполне достаточно любого из двух. Но когда выводятся оба, это некрасиво.

К сожалению к ошибке приводит такой напрашивающийся код:

Выбрать Максимум(МоментВремени) из Документ.РеализацияТоваровУслуг

А что если применить возможность новых платформ по преобразованию к строке, ведь нас при одинаковом времени вполне устроит больший номер:

Максимум(Дата)
заменим на
Максимум(Строка(Дата) + Номер)

Совсем ерунда, потому что "02.01" больше чем "01.02" (второе января позже первого февраля). Эх.

И тут приходит решение. В строку надо преобразовывать разность в секундах относительно какой-то очень старой даты, например, как принято в unix-системах, 1970 года:

Выбрать Последние.Контрагент, Последние.Ссылка
Из Документ.РеализацияТоваровУслуг Как Последние
Внутреннее Соединение
(
 Выбрать Контрагент,
 Максимум(Строка(РазностьДат(ДатаВремя(1970,1,1), Дата, Секунда)) + Номер) Как МаксДатаНомер
 Из Документ.РеализацияТоваровУслуг
 Сгруппировать По Контрагент
) Как Даты
По Последние.Контрагент = Даты.Контрагент
и Строка(РазностьДат(ДатаВремя(1970,1,1), Последние.Дата, Секунда)) + Номер = Даты.МаксДатаНомер

Наконец-то. Задвоение ушло. Все работает как надо.

Вывод как всегда прост, не верьте никаким советам в блогах и форумах, в том числе моим. Всегда проверяйте, ищите ошибки при крайних (граничных) значениях. Не внедряйте в работу чужое чуть-чуть некорректное. Потратьте время, разберитесь, найдите правильное решение именно вашей задачи и только тогда - в продакшн.

←27 | заметка 28 | 29→