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

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

У разработчиков нередко возникает закономерный вопрос: можно ли прямо внутри текста запроса сослаться на уже сформированные на предыдущих шагах строки или динамически отсекать дубли? Рассмотрим архитектурные особенности языка запросов в 1С:Предприятии и разберем практические способы решения этой задачи для платформ 7.7 и 8.x.

Архитектурные ограничения встроенного языка запросов

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

  1. Отсутствие процедурного контекста во время выполнения: внутри запроса нельзя прочитать строку, которая «только что добавилась» в результат, чтобы использовать её в условии фильтрации для следующей строки.
  2. Отсутствие оконных функций: во встроенном языке запросов 1С 8.x и стандартном движке 7.7 не поддерживаются аналитические функции (например, конструкции ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)), которые в классическом SQL позволяют пронумеровать строки внутри группы и отобрать запись с номером 1.

Тем не менее, данная задача успешно решается через правильное построение группировок, использование агрегатных функций или работу с подзапросами.

Решение задачи на платформе 1С:Предприятие 7.7

Проанализируем базовый механизм встроенного языка запросов платформы 7.7. Чтобы получить уникальные ГТД и для каждого из них взять только одну соответствующую партию, необходимо правильно выстроить иерархию группировок.

Посмотрим на пример построения текста запроса по справочнику партий:


ТекстЗапроса = "
|Обрабатывать НеПомеченныеНаУдаление;
|Партия = Справочник.Партии.ТекущийЭлемент;
|ГТД = Справочник.Партии.ГТД;
|Страна = Справочник.Партии.СтранаПроисхождения;
|Группировка ГТД без групп;
|Группировка Партия без групп;
|Условие (ПустоеЗначение(ГТД) + ПустоеЗначение(Страна) = 0);
|";

Здесь мы группируем данные сначала по полю ГТД, а затем по полю Партия. В результате движок запросов сформирует дерево, где верхним уровнем будут уникальные номера ГТД, а подчиненным уровнем — все партии, относящиеся к данному номеру.

Разберем, как правильно обойти такой результат в коде модуля, чтобы получить ровно по одной партии на каждый ГТД:


Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    Пока Запрос.Группировка(1) = 1 Цикл
        ТекГТД = Запрос.ГТД;
        ТекПартия = "";
        
        // Берем только первую попавшуюся партию из подчиненной группировки
        Если Запрос.Группировка(2) = 1 Тогда
            ТекПартия = Запрос.Партия;
        КонецЕсли;
        
        // Обработка полученной уникальной пары ТекГТД и ТекПартия
    КонецЦикла;
КонецЕсли;

При таком обходе мы извлекаем из второй группировки только одну первую запись и сразу переходим к следующему ГТД, что полностью исключает дублирование номеров деклараций.

Альтернатива для 1С 7.7: Прямые SQL-запросы через 1С++

Если требуется обработать миллионы записей без выгрузки лишних данных в оперативную память клиента, в среде 7.7 используют внешнюю компоненту 1С++. Она позволяет отправлять прямые SQL-запросы к базе данных и задействовать всю мощь СУБД:

  1. Использование коррелированных подзапросов в секции SELECT.
  2. Фильтрация с помощью конструкции NOT EXISTS.
  3. Использование аналитических функций СУБД для секционирования строк.

Решение задачи на платформе 1С:Предприятие 8.x

В платформе 8.x язык запросов предоставляет более гибкие инструменты агрегации и соединения таблиц. Рассмотрим основные сценарии.

Сценарий 1: Получение одной партии на каждый ГТД через агрегатные функции

Если для каждого уникального ГТД требуется выбрать одну партию (например, с минимальной или максимальной ссылкой), применим группировку по реквизиту ГТД с использованием агрегатной функции МИНИМУМ:


ВЫБРАТЬ
    Партии.ГТД КАК ГТД,
    МИНИМУМ(Партии.Ссылка) КАК Партия
ИЗ
    Справочник.Партии КАК Партии
ГДЕ
    Партии.ГТД <> ЗНАЧЕНИЕ(Справочник.ГТД.ПустаяСсылка)
СГРУППИРОВАТЬ ПО
    Партии.ГТД

Этот запрос возвращает строго уникальный список ГТД и единственную партию для каждого из них без необходимости процедурного отбора при обходе выборки.

Сценарий 2: Вывод всех ГТД и сопоставление им уникальных партий

Если необходимо получить полный список элементов справочника ГТД (включая те, по которым еще не было партий) и сопоставить им не более одной партии, используем левое соединение со сгруппированным подзапросом:


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

Сценарий 3: Выборка элементов, которые еще не использовались

Когда требуется отобрать только те ГТД, которые ни разу не встречались в партиях, не следует выгружать список использованных значений в массив и передавать его параметром через В (&Массив), так как на больших объемах это приведет к падению производительности.

Оптимальным решением является соединение с проверкой на NULL:


ВЫБРАТЬ
    СпрГТД.Ссылка КАК ГТД
ИЗ
    Справочник.ГТД КАК СпрГТД
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партии КАК Партии
    ПО СпрГТД.Ссылка = Партии.ГТД
ГДЕ
    Партии.Ссылка ЕСТЬ NULL

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

← На главную