При разработке на платформе 1С часто возникает необходимость получить данные из регистра сведений таким образом, чтобы одно из измерений (например, ИЗМ_1) встречалось в результате строго один раз, а значения других полей брались из первой попавшейся (или любой другой) записи. Рассмотрим подробнее, как правильно составить такой запрос, выясним причину частых ошибок и разберем по шагам несколько вариантов решения этой задачи — от классических до самых современных.
Многие начинающие разработчики пытаются решить эту проблему, используя операцию ВЫБРАТЬ РАЗЛИЧНЫЕ. Проанализируем ситуацию: почему этот метод не дает нужного результата? Архитектурная особенность языка запросов 1С (как и классического SQL) такова, что оператор РАЗЛИЧНЫЕ оценивает уникальность не по одному какому-то выбранному полю, а по комбинации всех полей, указанных в секции выборки.
Если значение ИЗМ_1 в двух строках одинаковое, а ИЗМ_2 разное, то для системы эти строки в целом считаются разными, и дубли не отсекутся. Нам нужен принципиально иной подход.
Самый простой и распространенный метод для выборки всего двух полей — использование группировки. Посмотрим на пример, где мы группируем записи по целевому измерению и применяем функцию МИНИМУМ (или МАКСИМУМ) к остальным полям.
ВЫБРАТЬ
ВашРегистр.ИЗМ_1 КАК ИЗМ_1,
МИНИМУМ(ВашРегистр.ИЗМ_2) КАК ИЗМ_2
ИЗ
РегистрСведений.ВашРегистр КАК ВашРегистр
СГРУППИРОВАТЬ ПО
ВашРегистр.ИЗМ_1
В этом случае платформа соберет все строки с одинаковым ИЗМ_1 в одну группу, а для поля ИЗМ_2 выберет минимальное значение из этой группы. Проблема решена одним компактным запросом!
Разберем ситуацию, когда нам необходимо вытащить не только ИЗМ_1 и ИЗМ_2, но и другие поля (например, ИЗМ_3 или ресурсы), причем эти поля должны относиться строго к одной и той же физической строке регистра.
Если мы просто продолжим добавлять агрегатные функции (например, МИНИМУМ(ИЗМ_2) и МАКСИМУМ(ИЗМ_3)), то запрос может взять значение второго измерения из первой строки регистра, а значение третьего — из второй. В итоге мы получим несуществующую, "слепленную" строку-Франкенштейна. Чтобы этого избежать, применяют другие методы.
Чтобы достать целую физическую строку со всеми реквизитами для каждого уникального ИЗМ_1, традиционно используют внутреннее соединение таблиц. Проанализируем этот алгоритм:
ИЗМ_1 с получением МАКСИМУМ(ИЗМ_2).Посмотрим на пример кода:
ВЫБРАТЬ
Исходный.ИЗМ_1 КАК ИЗМ_1,
Исходный.ИЗМ_2 КАК ИЗМ_2,
Исходный.ИЗМ_3 КАК ИЗМ_3
ИЗ
РегистрСведений.ВашРегистр КАК Исходный
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
Т.ИЗМ_1 КАК ИЗМ_1,
МАКСИМУМ(Т.ИЗМ_2) КАК ИЗМ_2
ИЗ
РегистрСведений.ВашРегистр КАК Т
СГРУППИРОВАТЬ ПО
Т.ИЗМ_1) КАК ВложенныйЗапрос
ПО Исходный.ИЗМ_1 = ВложенныйЗапрос.ИЗМ_1
И Исходный.ИЗМ_2 = ВложенныйЗапрос.ИЗМ_2
Этот способ надежно отсекает дубли и позволяет «вытянуть» все остальные поля строго из реально существующей уникальной записи.
Если ваша конфигурация работает на платформе 1С версии 8.3.20 и выше, задачу можно решить максимально элегантно, вообще избегая сложных соединений и группировок. Воспользуемся оконными функциями. Функция АВТОНОМЕРЗАПИСИ() с секционированием присвоит порядковые номера строкам внутри каждого отдельного значения.
Рассмотрим, как это работает на практике:
ВЫБРАТЬ
Т.ИЗМ_1 КАК ИЗМ_1,
Т.ИЗМ_2 КАК ИЗМ_2,
Т.ИЗМ_3 КАК ИЗМ_3,
АВТОНОМЕРЗАПИСИ() ОКОННАЯФУНКЦИЯ (РАЗБИЕНИЕ ПО Т.ИЗМ_1 УПОРЯДОЧИТЬ ПО Т.ИЗМ_2) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_Пронумерованные
ИЗ
РегистрСведений.ВашРегистр КАК Т
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.ИЗМ_1 КАК ИЗМ_1,
ВТ.ИЗМ_2 КАК ИЗМ_2,
ВТ.ИЗМ_3 КАК ИЗМ_3
ИЗ
ВТ_Пронумерованные КАК ВТ
ГДЕ
ВТ.НомерСтроки = 1
Важное преимущество: Оконные функции разделяют данные по полю ИЗМ_1 (конструкция РАЗБИЕНИЕ ПО) и нумеруют строки. Оставив условие ГДЕ НомерСтроки = 1, мы мгновенно получаем первую попавшуюся запись со всеми нужными полями.
Существует и еще один вариант — использование коррелированных запросов, где выборка идет из таблицы, а в условии проверяется вхождение комбинации полей в результат подзапроса ВЫБРАТЬ ПЕРВЫЕ 1. Однако из-за высоких нагрузок на СУБД (так как подзапрос выполняется для каждой строки основной выборки), разработчики 1С настоятельно рекомендуют избегать такого подхода.
В большинстве задач лучше остановиться на классическом самосоединении, либо (при наличии новой платформы) уверенно применять современные оконные функции 1С.