Как использовать подзапросы в строке выбора запроса 1С и чем их заменить

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе с классического SQL (T-SQL или PL/SQL) на язык запросов 1С многие разработчики сталкиваются с неожиданным ограничением: попытка написать скалярный подзапрос непосредственно в списке выбора (после оператора ВЫБРАТЬ) приводит к ошибке синтаксиса. В этой статье мы разберем, почему так происходит, и проанализируем наиболее эффективные способы решения этой задачи, принятые в экосистеме 1С.

Суть проблемы: почему стандартный SQL-подход не работает?

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


SELECT 
    val1, 
    (SELECT COUNT(val2) FROM t2 WHERE t1.val1 = t2.pole2) AS CountVal2 
FROM t1

В языке запросов 1С такая конструкция не поддерживается. Платформа 1С:Предприятие использует собственное подмножество языка SQL, которое оптимизировано для работы с объектной моделью данных. В нем подзапросы (вложенные запросы) могут располагаться только в секции ИЗ (формируя производную таблицу) или в секции ГДЕ (для условий связи). Прямые коррелированные подзапросы в списке выбора запрещены на уровне парсера платформы.

Давайте выясним, как решить эту задачу правильно, используя инструменты, которые предоставляет нам платформа 1С.

Решение 1: Использование временных таблиц (Рекомендуемый способ)

Проанализируем самый надежный и производительный метод — использование временных таблиц. Этот подход позволяет СУБД (MS SQL Server, PostgreSQL и др.) более эффективно строить план выполнения запроса, особенно если мы добавим индексацию по ключевым полям.

Пошаговый алгоритм реализации:

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

Разберем это на примере кода:


// Шаг 1: Подготовка агрегированных данных во временную таблицу
ВЫБРАТЬ
    Т2.pole2 КАК КлючСвязи,
    КОЛИЧЕСТВО(Т2.val2) КАК КоличествоЗаписей
ПОМЕСТИТЬ ВТ_КоличествоПоТ2
ИЗ
    Таблица2 КАК Т2
СГРУППИРОВАТЬ ПО
    Т2.pole2
ИНДЕКСИРОВАТЬ ПО
    КлючСвязи
;

// Шаг 2: Получение итогового результата
ВЫБРАТЬ
    Т1.val1,
    ЕСТЬNULL(ВТ.КоличествоЗаписей, 0) КАК CountVal2
ИЗ
    Таблица1 КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_КоличествоПоТ2 КАК ВТ
    ПО Т1.val1 = ВТ.КлючСвязи

Почему этот метод хорош? Во-первых, мы разделяем сложную логику на простые этапы. Во-вторых, использование ИНДЕКСИРОВАТЬ ПО гарантирует высокую скорость соединения таблиц даже на огромных объемах данных.

Решение 2: Левое соединение с вложенным запросом

Если мы не хотим использовать менеджер временных таблиц (хотя это рекомендуется), мы можем использовать вложенный запрос непосредственно в секции ИЗ. Это более компактная запись, но она может работать медленнее на больших базах данных, так как оптимизатору СУБД сложнее обработать такие вложенные конструкции.

Посмотрим на пример реализации:


ВЫБРАТЬ
    Т1.val1,
    ЕСТЬNULL(Подзапрос.КоличествоЗаписей, 0) КАК CountVal2
ИЗ
    Таблица1 КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            Т2.pole2 КАК КлючСвязи,
            КОЛИЧЕСТВО(Т2.val2) КАК КоличествоЗаписей
        ИЗ
            Таблица2 КАК Т2
        СГРУППИРОВАТЬ ПО
            Т2.pole2
    ) КАК Подзапрос
    ПО Т1.val1 = Подзапрос.КлючСвязи

Здесь важно помнить об использовании функции ЕСТЬNULL. При левом соединении для строк первой таблицы, которым не нашлось соответствия во второй, придет значение NULL, которое необходимо заменить на числовой ноль для корректности отчета.

Решение 3: Оконные функции (Для современных версий платформы)

Начиная с версии 8.3.20, в 1С появились оконные функции. Если обе таблицы фактически являются одной таблицей или данные можно свести в один набор, это самый современный способ решения задачи.

Рассмотрим ситуацию, когда нам нужно посчитать количество строк внутри групп в рамках одного запроса. Мы можем использовать конструкцию ОБЪЕМЛЮЩЕЕ (аналог OVER в SQL):


ВЫБРАТЬ
    Т1.val1,
    КОЛИЧЕСТВО(Т1.val2) ОБЪЕМЛЮЩЕЕ (ПЕРЕГОРОДКА ПО Т1.Группа) КАК КоличествоВГруппе
ИЗ
    Таблица1 КАК Т1

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

Решение 4: Группировка всего запроса

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

Тем не менее, для простых таблиц схема выглядит так:


ВЫБРАТЬ
    Т1.val1,
    КОЛИЧЕСТВО(Т2.val2) КАК CountVal2
ИЗ
    Таблица1 КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ Таблица2 КАК Т2
    ПО Т1.val1 = Т2.pole2
СГРУППИРОВАТЬ ПО
    Т1.val1

Подведем итоги

В 1С нет возможности написать подзапрос прямо в ВЫБРАТЬ, но это ограничение обходится стандартными механизмами платформы. Мы рассмотрели четыре способа решения задачи:

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

← На главную