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

· 10 мин

Содержание

Таблицы в запросе 1С объявляются в предложении ИЗ: там стоит имя объекта метаданных (Справочник.Номенклатура, Документ.РеализацияТоваровУслуг.Товары) и через КАК — его псевдоним. Временная таблица объявляется в предложении ПОМЕСТИТЬ, таблица значений попадает в запрос только как параметр &ИмяПараметра, а подзапрос получает имя после закрывающей скобки. Всё остальное в тексте запроса — поля, условия и сортировки — ссылается на эти объявления по псевдониму.

Если вы открыли чужой модуль и пытаетесь понять, откуда берутся данные, читайте запрос не сверху вниз, а от ИЗ. Сначала найдите источники и их псевдонимы, потом поля в ВЫБРАТЬ, потом условия. Для длинного пакетного запроса — сначала найдите все ПОМЕСТИТЬ, они покажут цепочку промежуточных таблиц.

Порядок предложений в тексте запроса

Язык запросов 1С строгий к порядку частей. Переставить ГДЕ перед ИЗ нельзя — получите синтаксическую ошибку. Полная схема одного запроса выглядит так:

ВЫБРАТЬ [РАЗРЕШЕННЫЕ] [РАЗЛИЧНЫЕ] [ПЕРВЫЕ N]
    <список полей>
[ПОМЕСТИТЬ <имя временной таблицы>]
ИЗ
    <источники и соединения>
[ГДЕ <условие на записи>]
[СГРУППИРОВАТЬ ПО <поля>]
[ИМЕЮЩИЕ <условие на группы>]
[ОБЪЕДИНИТЬ [ВСЕ] <следующий запрос>]
[УПОРЯДОЧИТЬ ПО <поля>]
[ИТОГИ ... ПО ...]

Что за что отвечает:

  • 📥 ВЫБРАТЬ — какие поля вернуть. Здесь же задаются псевдонимы полей: Товары.Количество * Товары.Цена КАК Сумма.
  • 📦 ПОМЕСТИТЬ — сохранить результат во временную таблицу вместо выдачи наружу.
  • 🗂 ИЗ — откуда брать данные. Единственное место, где объявляются источники.
  • 🔍 ГДЕ — отбор отдельных строк до группировки.
  • 🧮 СГРУППИРОВАТЬ ПО и ИМЕЮЩИЕ — свёртка и отбор уже свёрнутых групп.
  • 🔗 ОБЪЕДИНИТЬ — склейка результатов нескольких запросов в один.
  • ↕️ УПОРЯДОЧИТЬ ПО и ИТОГИ — сортировка и итоговые строки.

Ключевое слово РАЗРЕШЕННЫЕ пишется сразу после ВЫБРАТЬ. Оно отбрасывает записи, к которым у пользователя нет доступа по ограничениям RLS, вместо ошибки «недостаточно прав».

У запроса нет обязательного ИЗ: ВЫБРАТЬ 1 КАК Число — корректный запрос. Так иногда получают константу или дату сервера через ТекущаяДата() в старом коде.

Где в запросе объявляются таблицы метаданных и их псевдонимы

Источник в ИЗ записывается как путь по дереву метаданных: тип объекта, точка, имя объекта. Для табличной части добавляется ещё одно имя через точку.

Что нужноКак записать в ИЗ
Справочник целикомСправочник.Контрагенты КАК Контрагенты
Шапка документаДокумент.ПоступлениеТоваровУслуг КАК Поступление
Табличная часть документаДокумент.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТовары
Движения регистра накопленияРегистрНакопления.ТоварыНаСкладах КАК Движения
Остатки регистра (виртуальная таблица)РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Остатки
Последние значения сведенийРегистрСведений.КурсыВалют.СрезПоследних(&Дата) КАК Курсы
Проводки бухгалтерииРегистрБухгалтерии.Хозрасчетный КАК Проводки
ПеречислениеПеречисление.СтавкиНДС КАК Ставки

Имена объектов берите из дерева конфигурации, а не из синонимов в интерфейсе. Пользователь видит «Реализация (акт, накладная, УПД)», а в запросе нужен Документ.РеализацияТоваровУслуг. Точное имя подскажет свойство «Имя» в палитре свойств объекта.

Зачем нужен псевдоним

Псевдоним после КАК — это имя, под которым таблица живёт внутри запроса. Все поля дальше пишутся через него: Поступление.Дата, ПоступлениеТовары.Номенклатура. Если псевдоним не задан, платформа подставит имя объекта, но в запросе с двумя таблицами одного типа без псевдонимов не обойтись.

Пример: шапка и табличная часть одного документа.

ВЫБРАТЬ
    Шапка.Номер КАК Номер,
    Шапка.Контрагент КАК Поставщик,
    Строки.Номенклатура КАК Товар,
    Строки.Количество КАК Количество
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК Строки
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг КАК Шапка
        ПО Строки.Ссылка = Шапка.Ссылка
ГДЕ
    Шапка.Проведен

У табличной части всегда есть поле Ссылка — ссылка на владельца. Поэтому соединение здесь не обязательно: Строки.Ссылка.Контрагент вернёт то же самое через неявное соединение. Но в больших выборках явное соединение читается проще и управляется лучше.

Параметры виртуальных таблиц — тоже часть объявления

У Остатки, Обороты, СрезПоследних в скобках после имени стоят параметры: период и условие. Это не украшение. Условие внутри скобок платформа применяет до расчёта остатков, а условие в ГДЕ — уже после.

ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Остатки

Отбор по складу, организации или номенклатуре для виртуальной таблицы ставьте в её параметры, а не в ГДЕ. Результат иногда совпадает, но скорость на большом регистре отличается в разы.

Временные таблицы и таблицы значений в запросе

Где объявляется временная таблица

Временная таблица появляется в момент выполнения запроса с ПОМЕСТИТЬ. Имя задаёте вы, принято начинать его с ВТ:

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    СУММА(Товары.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_Отгружено
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
    Товары.Ссылка.Дата МЕЖДУ &Начало И &Конец
СГРУППИРОВАТЬ ПО
    Товары.Номенклатура
ИНДЕКСИРОВАТЬ ПО
    Номенклатура
;
ВЫБРАТЬ
    ВТ.Номенклатура,
    ВТ.Количество
ИЗ
    ВТ_Отгружено КАК ВТ

В следующих запросах пакета временная таблица указывается в ИЗ просто по имени, без префикса Справочник. или Документ.. По этому признаку её и отличают при чтении: имя без точки — это временная таблица или подзапрос.

ИНДЕКСИРОВАТЬ ПО ставится в конце запроса с ПОМЕСТИТЬ. Индекс имеет смысл, если по этой таблице дальше идёт соединение или отбор.

Сколько живёт временная таблица

  • ⏳ В пределах одного пакета — запросы разделены точкой с запятой и выполняются одним вызовом Выполнить() или ВыполнитьПакет().
  • 🔁 Между разными объектами Запрос — только если им присвоен один МенеджерВременныхТаблиц.
  • 🗑 Удаление вручную — УНИЧТОЖИТЬ ВТ_Отгружено отдельным запросом пакета. Иначе таблица живёт, пока жив менеджер.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос1.Текст = "ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Отгружено ИЗ ...";
Запрос1.Выполнить();

Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос2.Текст = "ВЫБРАТЬ * ИЗ ВТ_Отгружено КАК ВТ";
Результат = Запрос2.Выполнить();

Повторное ПОМЕСТИТЬ с тем же именем в одном менеджере даёт ошибку: таблица уже существует. В циклах сначала уничтожайте старую.

Как передать таблицу значений

Таблица значений из кода не становится источником напрямую. Её передают параметром и сразу кладут во временную таблицу — это единственное место, где &Параметр допустим в ИЗ:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Т.Номенклатура КАК Номенклатура,
|    Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Строки
|ИЗ
|    &ТаблицаСтрок КАК Т
|;
|ВЫБРАТЬ
|    ВТ.Номенклатура,
|    ВТ.Количество,
|    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
|ИЗ
|    ВТ_Строки КАК ВТ
|        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
|            , Номенклатура В (ВЫБРАТЬ Н.Номенклатура ИЗ ВТ_Строки КАК Н)) КАК Остатки
|        ПО ВТ.Номенклатура = Остатки.Номенклатура";
Запрос.УстановитьПараметр("ТаблицаСтрок", ТаблицаСтрок);

У колонок таблицы значений должны быть заданы типы. Колонка, созданная как ТЗ.Колонки.Добавить("Номенклатура") без описания типов, в запросе вызовет ошибку типа. Создавайте так: ТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура")).

Для числовых колонок задавайте разрядность через ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)). Иначе платформа не поймёт, какого размера поле создавать.

Вложенные запросы и что из них видно

Подзапрос объявляется в ИЗ или в соединении: текст в скобках, после скобки — псевдоним.

ВЫБРАТЬ
    Продажи.Контрагент,
    Продажи.Выручка
ИЗ
    (ВЫБРАТЬ
        Реализация.Контрагент КАК Контрагент,
        СУММА(Реализация.СуммаДокумента) КАК Выручка
    ИЗ
        Документ.РеализацияТоваровУслуг КАК Реализация
    ГДЕ
        Реализация.Проведен
    СГРУППИРОВАТЬ ПО
        Реализация.Контрагент) КАК Продажи
ГДЕ
    Продажи.Выручка > 1000000

Правила видимости простые:

  1. Снаружи видны только поля, перечисленные в ВЫБРАТЬ подзапроса, и только по их псевдонимам. Продажи.Выручка работает, Продажи.СуммаДокумента — нет.
  2. Изнутри подзапрос не видит таблицы внешнего запроса. Связанных подзапросов, как в SQL с обращением к внешней строке, в языке запросов 1С нет.
  3. Псевдонимы внутри подзапроса могут совпадать с внешними — у них разные области.

Второй вид — подзапрос в условии: ГДЕ Товары.Номенклатура В (ВЫБРАТЬ ...). Он возвращает одну колонку и служит фильтром, псевдоним ему не нужен.

Подзапрос в соединении — частая причина медленных отчётов: оптимизатор СУБД плохо оценивает его размер. Вынесите подзапрос во временную таблицу с ИНДЕКСИРОВАТЬ ПО и соединяйтесь уже с ней.

ОБЪЕДИНИТЬ и пакетный запрос: где задаются имена и типы полей

Как ОБЪЕДИНИТЬ влияет на структуру

При объединении имена колонок результата берутся из первого запроса. Во втором и следующих псевдонимы полей можно не писать — они ни на что не влияют.

ВЫБРАТЬ
    Приход.Номенклатура КАК Номенклатура,
    Приход.Количество КАК Приход,
    0 КАК Расход
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК Приход

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Расход.Номенклатура,
    0,
    Расход.Количество
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Расход

Что держать в голове:

  • 🔢 Число полей во всех частях одинаковое. Нет значения — поставьте 0, "" или NULL.
  • 🧷 Поля сопоставляются по порядку, а не по имени. Перепутали местами Приход и Расход во втором запросе — получите неверные цифры без всякой ошибки.
  • 🧹 ОБЪЕДИНИТЬ без ВСЕ убирает полностью совпадающие строки. Две одинаковые строки накладной схлопнутся в одну, и количество уменьшится. Для учётных данных почти всегда нужен ОБЪЕДИНИТЬ ВСЕ.
  • 📌 ПОМЕСТИТЬ пишется в первом запросе объединения и относится ко всему результату.
  • ↕️ УПОРЯДОЧИТЬ ПО ставится один раз в самом конце и сортирует общий результат.

Пакет запросов

Пакет — это несколько запросов через ; в одном тексте. Каждый следующий видит временные таблицы предыдущих. Выполнить() вернёт результат только последнего запроса, ВыполнитьПакет() — массив результатов всех. Если нужен итог из середины пакета, берите его по индексу массива, считая с нуля.

Как найти запрос и используемые объекты в коде конфигурации

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

Глобальный поиск по тексту. В Конфигураторе Правка → Глобальный поиск (Ctrl+Shift+F). Ищите имя объекта в форме, как оно пишется в запросе: РегистрНакопления.ТоварыНаСкладах. Поиск найдёт и запросы в модулях, и тексты в макетах схем компоновки. Для поиска своих временных таблиц ищите по имени ВТ_....

Поиск ссылок на объект. Контекстное меню объекта в дереве конфигурации → Поиск ссылок на объект. Он показывает, где объект используется в свойствах метаданных: в реквизитах, регистраторах, подписках. Текст запросов внутри строк он не разбирает, поэтому для кода нужен глобальный поиск.

Конструктор запроса. Поставьте курсор внутрь текста запроса в модуле, откройте контекстное меню и выберите конструктор запроса. Он разложит текст по вкладкам: таблицы, поля, условия, объединения, пакет. Для чтения запутанного пакета это быстрее, чем глазами. Учтите: при сохранении конструктор переформатирует текст и может убрать комментарии.

В отладчике значение Запрос.Текст можно скопировать и выполнить в обработке «Консоль запросов» с теми же параметрами. Делайте это на копии базы: запрос с ДЛЯ ИЗМЕНЕНИЯ или тяжёлой выборкой по рабочей базе нагрузит сервер для всех пользователей.

Порядок разбора чужого запроса

Разбор запроса из чужого модуля
Выполнено: 0 / 5

Ошибки при объявлении таблиц и полей

Сообщения об ошибках запроса содержат номер строки и позицию в тексте — по ним сразу видно, какое объявление сломано. Самые частые случаи:

Диагностика: выберите симптом

Ещё одна тихая ошибка — параметр, который не установлен. Если в тексте есть &Склад, а в коде нет УстановитьПараметр("Склад", ...), запрос упадёт при выполнении. Если же параметр установлен, но пустой, отбор Склад = &Склад вернёт ноль строк — и это легко принять за отсутствие данных.

Частые вопросы

Можно ли обратиться к временной таблице из схемы компоновки данных?
Да, если набор данных СКД получает внешний менеджер временных таблиц при программном выводе отчёта. При обычном формировании отчёта из интерфейса временная таблица должна создаваться в том же тексте запроса набора данных.
Чем временная таблица лучше вложенного запроса?
Её можно проиндексировать и использовать несколько раз в пакете. Вложенный запрос выполняется там, где написан, и при повторном использовании пишется заново.
Регистр букв в именах таблиц и ключевых словах важен?
Нет. ВЫБРАТЬ и выбрать, Справочник.Контрагенты и справочник.контрагенты равнозначны. Английские ключевые слова (SELECT, FROM) тоже допустимы, но смешивать их в одном тексте не стоит.
Как в запросе выбрать данные из реквизита ссылочного поля?
Через точку: Строки.Номенклатура.Артикул. Платформа сама построит неявное соединение. При длинных цепочках точек и составных типах это соединение становится тяжёлым — лучше соединиться с нужной таблицей явно.
Можно ли использовать в ИЗ имя переменной из модуля?
Нет. В тексте запроса доступны только объекты метаданных, временные таблицы, подзапросы и параметры через &. Имя таблицы можно подставить в текст строковой заменой перед выполнением, но это затрудняет поиск и проверку кода.