Как читать текст запроса 1С: источники, псевдонимы, временные таблицы и подзапросы
Содержание
Таблицы в запросе 1С объявляются в предложении ИЗ: там стоит имя объекта метаданных (Справочник.Номенклатура, Документ.РеализацияТоваровУслуг.Товары) и через КАК — его псевдоним. Временная таблица объявляется в предложении ПОМЕСТИТЬ, таблица значений попадает в запрос только как параметр &ИмяПараметра, а подзапрос получает имя после закрывающей скобки. Всё остальное в тексте запроса — поля, условия и сортировки — ссылается на эти объявления по псевдониму.
Если вы открыли чужой модуль и пытаетесь понять, откуда берутся данные, читайте запрос не сверху вниз, а от ИЗ. Сначала найдите источники и их псевдонимы, потом поля в ВЫБРАТЬ, потом условия. Для длинного пакетного запроса — сначала найдите все ПОМЕСТИТЬ, они покажут цепочку промежуточных таблиц.
Порядок предложений в тексте запроса
Язык запросов 1С строгий к порядку частей. Переставить ГДЕ перед ИЗ нельзя — получите синтаксическую ошибку. Полная схема одного запроса выглядит так:
ВЫБРАТЬ [РАЗРЕШЕННЫЕ] [РАЗЛИЧНЫЕ] [ПЕРВЫЕ N]
<список полей>
[ПОМЕСТИТЬ <имя временной таблицы>]
ИЗ
<источники и соединения>
[ГДЕ <условие на записи>]
[СГРУППИРОВАТЬ ПО <поля>]
[ИМЕЮЩИЕ <условие на группы>]
[ОБЪЕДИНИТЬ [ВСЕ] <следующий запрос>]
[УПОРЯДОЧИТЬ ПО <поля>]
[ИТОГИ ... ПО ...]
Что за что отвечает:
- 📥
ВЫБРАТЬ— какие поля вернуть. Здесь же задаются псевдонимы полей:Товары.Количество * Товары.Цена КАК Сумма. - 📦
ПОМЕСТИТЬ— сохранить результат во временную таблицу вместо выдачи наружу. - 🗂
ИЗ— откуда брать данные. Единственное место, где объявляются источники. - 🔍
ГДЕ— отбор отдельных строк до группировки. - 🧮
СГРУППИРОВАТЬ ПОиИМЕЮЩИЕ— свёртка и отбор уже свёрнутых групп. - 🔗
ОБЪЕДИНИТЬ— склейка результатов нескольких запросов в один. - ↕️
УПОРЯДОЧИТЬ ПОиИТОГИ— сортировка и итоговые строки.
Ключевое слово РАЗРЕШЕННЫЕ пишется сразу после ВЫБРАТЬ. Оно отбрасывает записи, к которым у пользователя нет доступа по ограничениям RLS, вместо ошибки «недостаточно прав».
У запроса нет обязательного ИЗ: ВЫБРАТЬ 1 КАК Число — корректный запрос. Так иногда получают константу или дату сервера через ТекущаяДата() в старом коде.
Где в запросе объявляются таблицы метаданных и их псевдонимы
Источник в ИЗ записывается как путь по дереву метаданных: тип объекта, точка, имя объекта. Для табличной части добавляется ещё одно имя через точку.
| Что нужно | Как записать в ИЗ |
|---|---|
| Справочник целиком | Справочник.Контрагенты КАК Контрагенты |
| Шапка документа | Документ.ПоступлениеТоваровУслуг КАК Поступление |
| Табличная часть документа | Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТовары |
| Движения регистра накопления | РегистрНакопления.ТоварыНаСкладах КАК Движения |
| Остатки регистра (виртуальная таблица) | РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Остатки |
| Последние значения сведений | РегистрСведений.КурсыВалют.СрезПоследних(&Дата) КАК Курсы |
| Проводки бухгалтерии | РегистрБухгалтерии.Хозрасчетный КАК Проводки |
| Перечисление | Перечисление.СтавкиНДС КАК Ставки |
Имена объектов берите из дерева конфигурации, а не из синонимов в интерфейсе. Пользователь видит «Реализация (акт, накладная, УПД)», а в запросе нужен Документ.РеализацияТоваровУслуг. Точное имя подскажет свойство «Имя» в палитре свойств объекта.
Зачем нужен псевдоним
Псевдоним после КАК — это имя, под которым таблица живёт внутри запроса. Все поля дальше пишутся через него: Поступление.Дата, ПоступлениеТовары.Номенклатура. Если псевдоним не задан, платформа подставит имя объекта, но в запросе с двумя таблицами одного типа без псевдонимов не обойтись.
Пример: шапка и табличная часть одного документа.
ВЫБРАТЬ
Шапка.Номер КАК Номер,
Шапка.Контрагент КАК Поставщик,
Строки.Номенклатура КАК Товар,
Строки.Количество КАК Количество
ИЗ
Документ.ПоступлениеТоваровУслуг.Товары КАК Строки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг КАК Шапка
ПО Строки.Ссылка = Шапка.Ссылка
ГДЕ
Шапка.Проведен
У табличной части всегда есть поле Ссылка — ссылка на владельца. Поэтому соединение здесь не обязательно: Строки.Ссылка.Контрагент вернёт то же самое через неявное соединение. Но в больших выборках явное соединение читается проще и управляется лучше.
Параметры виртуальных таблиц — тоже часть объявления
У Остатки, Обороты, СрезПоследних в скобках после имени стоят параметры: период и условие. Это не украшение. Условие внутри скобок платформа применяет до расчёта остатков, а условие в ГДЕ — уже после.
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Остатки
Отбор по складу, организации или номенклатуре для виртуальной таблицы ставьте в её параметры, а не в ГДЕ. Результат иногда совпадает, но скорость на большом регистре отличается в разы.
Временные таблицы и таблицы значений в запросе
Где объявляется временная таблица
Временная таблица появляется в момент выполнения запроса с ПОМЕСТИТЬ. Имя задаёте вы, принято начинать его с ВТ:
ВЫБРАТЬ
Товары.Номенклатура КАК Номенклатура,
СУММА(Товары.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_Отгружено
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Ссылка.Дата МЕЖДУ &Начало И &Конец
СГРУППИРОВАТЬ ПО
Товары.Номенклатура
ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
ВЫБРАТЬ
ВТ.Номенклатура,
ВТ.Количество
ИЗ
ВТ_Отгружено КАК ВТ
В следующих запросах пакета временная таблица указывается в ИЗ просто по имени, без префикса Справочник. или Документ.. По этому признаку её и отличают при чтении: имя без точки — это временная таблица или подзапрос.
ИНДЕКСИРОВАТЬ ПО ставится в конце запроса с ПОМЕСТИТЬ. Индекс имеет смысл, если по этой таблице дальше идёт соединение или отбор.
Сколько живёт временная таблица
- ⏳ В пределах одного пакета — запросы разделены точкой с запятой и выполняются одним вызовом
Выполнить()илиВыполнитьПакет(). - 🔁 Между разными объектами
Запрос— только если им присвоен одинМенеджерВременныхТаблиц. - 🗑 Удаление вручную —
УНИЧТОЖИТЬ ВТ_Отгруженоотдельным запросом пакета. Иначе таблица живёт, пока жив менеджер.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос1.Текст = "ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Отгружено ИЗ ...";
Запрос1.Выполнить();
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос2.Текст = "ВЫБРАТЬ * ИЗ ВТ_Отгружено КАК ВТ";
Результат = Запрос2.Выполнить();
Повторное ПОМЕСТИТЬ с тем же именем в одном менеджере даёт ошибку: таблица уже существует. В циклах сначала уничтожайте старую.
Как передать таблицу значений
Таблица значений из кода не становится источником напрямую. Её передают параметром и сразу кладут во временную таблицу — это единственное место, где &Параметр допустим в ИЗ:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Строки
|ИЗ
| &ТаблицаСтрок КАК Т
|;
|ВЫБРАТЬ
| ВТ.Номенклатура,
| ВТ.Количество,
| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
|ИЗ
| ВТ_Строки КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
| , Номенклатура В (ВЫБРАТЬ Н.Номенклатура ИЗ ВТ_Строки КАК Н)) КАК Остатки
| ПО ВТ.Номенклатура = Остатки.Номенклатура";
Запрос.УстановитьПараметр("ТаблицаСтрок", ТаблицаСтрок);
У колонок таблицы значений должны быть заданы типы. Колонка, созданная как ТЗ.Колонки.Добавить("Номенклатура") без описания типов, в запросе вызовет ошибку типа. Создавайте так: ТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура")).
Для числовых колонок задавайте разрядность через ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)). Иначе платформа не поймёт, какого размера поле создавать.
Вложенные запросы и что из них видно
Подзапрос объявляется в ИЗ или в соединении: текст в скобках, после скобки — псевдоним.
ВЫБРАТЬ
Продажи.Контрагент,
Продажи.Выручка
ИЗ
(ВЫБРАТЬ
Реализация.Контрагент КАК Контрагент,
СУММА(Реализация.СуммаДокумента) КАК Выручка
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Проведен
СГРУППИРОВАТЬ ПО
Реализация.Контрагент) КАК Продажи
ГДЕ
Продажи.Выручка > 1000000
Правила видимости простые:
- Снаружи видны только поля, перечисленные в
ВЫБРАТЬподзапроса, и только по их псевдонимам.Продажи.Выручкаработает,Продажи.СуммаДокумента— нет. - Изнутри подзапрос не видит таблицы внешнего запроса. Связанных подзапросов, как в SQL с обращением к внешней строке, в языке запросов 1С нет.
- Псевдонимы внутри подзапроса могут совпадать с внешними — у них разные области.
Второй вид — подзапрос в условии: ГДЕ Товары.Номенклатура В (ВЫБРАТЬ ...). Он возвращает одну колонку и служит фильтром, псевдоним ему не нужен.
Подзапрос в соединении — частая причина медленных отчётов: оптимизатор СУБД плохо оценивает его размер. Вынесите подзапрос во временную таблицу с ИНДЕКСИРОВАТЬ ПО и соединяйтесь уже с ней.
ОБЪЕДИНИТЬ и пакетный запрос: где задаются имена и типы полей
Как ОБЪЕДИНИТЬ влияет на структуру
При объединении имена колонок результата берутся из первого запроса. Во втором и следующих псевдонимы полей можно не писать — они ни на что не влияют.
ВЫБРАТЬ
Приход.Номенклатура КАК Номенклатура,
Приход.Количество КАК Приход,
0 КАК Расход
ИЗ
Документ.ПоступлениеТоваровУслуг.Товары КАК Приход
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Расход.Номенклатура,
0,
Расход.Количество
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Расход
Что держать в голове:
- 🔢 Число полей во всех частях одинаковое. Нет значения — поставьте
0,""илиNULL. - 🧷 Поля сопоставляются по порядку, а не по имени. Перепутали местами
ПриходиРасходво втором запросе — получите неверные цифры без всякой ошибки. - 🧹
ОБЪЕДИНИТЬбезВСЕубирает полностью совпадающие строки. Две одинаковые строки накладной схлопнутся в одну, и количество уменьшится. Для учётных данных почти всегда нуженОБЪЕДИНИТЬ ВСЕ. - 📌
ПОМЕСТИТЬпишется в первом запросе объединения и относится ко всему результату. - ↕️
УПОРЯДОЧИТЬ ПОставится один раз в самом конце и сортирует общий результат.
Пакет запросов
Пакет — это несколько запросов через ; в одном тексте. Каждый следующий видит временные таблицы предыдущих. Выполнить() вернёт результат только последнего запроса, ВыполнитьПакет() — массив результатов всех. Если нужен итог из середины пакета, берите его по индексу массива, считая с нуля.
Как найти запрос и используемые объекты в коде конфигурации
Чтобы понять, где в конфигурации читается нужный регистр или документ, есть три рабочих приёма.
Глобальный поиск по тексту. В Конфигураторе Правка → Глобальный поиск (Ctrl+Shift+F). Ищите имя объекта в форме, как оно пишется в запросе: РегистрНакопления.ТоварыНаСкладах. Поиск найдёт и запросы в модулях, и тексты в макетах схем компоновки. Для поиска своих временных таблиц ищите по имени ВТ_....
Поиск ссылок на объект. Контекстное меню объекта в дереве конфигурации → Поиск ссылок на объект. Он показывает, где объект используется в свойствах метаданных: в реквизитах, регистраторах, подписках. Текст запросов внутри строк он не разбирает, поэтому для кода нужен глобальный поиск.
Конструктор запроса. Поставьте курсор внутрь текста запроса в модуле, откройте контекстное меню и выберите конструктор запроса. Он разложит текст по вкладкам: таблицы, поля, условия, объединения, пакет. Для чтения запутанного пакета это быстрее, чем глазами. Учтите: при сохранении конструктор переформатирует текст и может убрать комментарии.
В отладчике значение Запрос.Текст можно скопировать и выполнить в обработке «Консоль запросов» с теми же параметрами. Делайте это на копии базы: запрос с ДЛЯ ИЗМЕНЕНИЯ или тяжёлой выборкой по рабочей базе нагрузит сервер для всех пользователей.
Порядок разбора чужого запроса
Ошибки при объявлении таблиц и полей
Сообщения об ошибках запроса содержат номер строки и позицию в тексте — по ним сразу видно, какое объявление сломано. Самые частые случаи:
Ещё одна тихая ошибка — параметр, который не установлен. Если в тексте есть &Склад, а в коде нет УстановитьПараметр("Склад", ...), запрос упадёт при выполнении. Если же параметр установлен, но пустой, отбор Склад = &Склад вернёт ноль строк — и это легко принять за отсутствие данных.
Частые вопросы
Можно ли обратиться к временной таблице из схемы компоновки данных?
Чем временная таблица лучше вложенного запроса?
Регистр букв в именах таблиц и ключевых словах важен?
ВЫБРАТЬ и выбрать, Справочник.Контрагенты и справочник.контрагенты равнозначны. Английские ключевые слова (SELECT, FROM) тоже допустимы, но смешивать их в одном тексте не стоит.Как в запросе выбрать данные из реквизита ссылочного поля?
Строки.Номенклатура.Артикул. Платформа сама построит неявное соединение. При длинных цепочках точек и составных типах это соединение становится тяжёлым — лучше соединиться с нужной таблицей явно.Можно ли использовать в ИЗ имя переменной из модуля?
&. Имя таблицы можно подставить в текст строковой заменой перед выполнением, но это затрудняет поиск и проверку кода.