Язык запросов 1С 8.3 на практике: от первой выборки до пакета с временными таблицами

· 11 мин

Содержание

Запрос в 1С делают объектом Новый Запрос: пишут текст на языке запросов (ВЫБРАТЬ … ИЗ … ГДЕ …), передают параметры через УстановитьПараметр(), выполняют Выполнить() и обходят результат выборкой. Текст можно написать руками или собрать мышкой в конструкторе запроса — он открывается из контекстного меню модуля в конфигураторе.

Что выбрать? Конструктор удобен, когда таблиц много и не помнишь имена полей. Ручной текст быстрее для коротких выборок и правок. Опытные разработчики делают запросы в 1С так: каркас собирают в конструкторе, а тонкие места (условия виртуальных таблиц, индексы временных таблиц) дописывают вручную. Всё ниже относится к платформе 8.3 и работает в любой конфигурации; примеры с документами взяты из 1С:Бухгалтерии 3.0.

Из чего состоит запрос 1С и как выполнить его из кода

Язык запросов 1С — это SQL-подобный язык только для чтения. Изменить или удалить данные запросом нельзя, для этого есть объекты и наборы записей.

Порядок секций в тексте фиксирован:

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

Минимальный рабочий пример — контрагенты с ИНН, начинающимся на 77:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Контрагенты.Ссылка КАК Контрагент,
|	Контрагенты.ИНН КАК ИНН
|ИЗ
|	Справочник.Контрагенты КАК Контрагенты
|ГДЕ
|	НЕ Контрагенты.ПометкаУдаления
|	И Контрагенты.ИНН ПОДОБНО &Маска
|
|УПОРЯДОЧИТЬ ПО
|	ИНН";
Запрос.УстановитьПараметр("Маска", "77%");

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	Сообщить(Строка(Выборка.Контрагент) + " — " + Выборка.ИНН);
КонецЦикла;

Что здесь происходит:

  • 🔹 | в начале строки — продолжение многострочного литерала во встроенном языке, к языку запросов отношения не имеет.
  • 🔹 КАК задаёт псевдоним. По нему потом обращаются к полю выборки: Выборка.ИНН.
  • 🔹 &Маска — параметр. Значение подставляет платформа, склеивать текст запроса со значениями не нужно.
  • 🔹 % в ПОДОБНО — любое количество символов, _ — ровно один символ.

Результат можно получить тремя способами:

  1. Результат.Выбрать() — построчный обход, самый экономный по памяти.
  2. Результат.Выгрузить() — таблица значений целиком, если её нужно передать дальше или показать на форме.
  3. Результат.Пустой() — быстрая проверка «есть ли хоть одна строка» без обхода.

Ключевые слова пишут в любом регистре и на английском (SELECT, FROM, WHERE). В типовых конфигурациях принят русский вариант заглавными буквами — так текст легче читать рядом с кодом.

Конструктор запроса: как собрать текст без ручного набора

В конфигураторе откройте модуль, щёлкните правой кнопкой в нужном месте и выберите Конструктор запроса.... Если курсор стоит внутри уже готового текста запроса, конструктор откроет его для правки. Есть и вариант Конструктор запроса с обработкой результата — он сразу допишет код с Новый Запрос, выполнением и циклом по выборке.

Основные вкладки и что на них делать:

ВкладкаЧто настраивают
Таблицы и поляСлева дерево метаданных, перетаскиваете таблицы и поля в выбранные
СвязиСоединения: какая таблица с какой, по какому условию, «Все» слева или справа
ГруппировкаПоля группировки и агрегатные функции для суммируемых полей
УсловияОтборы ГДЕ; галочка «Произвольное» позволяет написать условие вручную
ДополнительноПЕРВЫЕ N, РАЗЛИЧНЫЕ, РАЗРЕШЕННЫЕ, режим временной таблицы
Объединения/ПсевдонимыНесколько запросов через ОБЪЕДИНИТЬ и имена полей
ПорядокСортировка
ИтогиПоля для ИТОГИ ПО

Пакет из нескольких запросов собирается там же: каждый запрос пакета — отдельная закладка внизу окна, а на вкладке «Дополнительно» запрос можно сделать временной таблицей и задать её имя.

Для виртуальных таблиц регистров у конструктора есть кнопка «Параметры виртуальной таблицы» над списком выбранных таблиц. Именно туда переносят условия по измерениям — почему это важно, разобрано в разделе про оптимизацию.

Проверить запрос без конфигуратора можно внешней обработкой «Консоль запросов»: в пользовательском режиме откройте её через главное меню Файл → Открыть. Консоль показывает результат, время выполнения и содержимое временных таблиц пакета.

Соединения таблиц в запросах 1С

Соединение берёт строки из двух источников и склеивает их по условию после ПО. Вид соединения решает, что делать со строками без пары.

СоединениеЧто попадает в результатГде применяют
ВНУТРЕННЕЕ СОЕДИНЕНИЕТолько строки, у которых нашлась параДокументы и их строки, где пара есть всегда
ЛЕВОЕ СОЕДИНЕНИЕВсе строки левой таблицы, справа NULL, если пары нетНоменклатура + остатки: показать и товары с нулём
ПРАВОЕ СОЕДИНЕНИЕТо же, но главная — праваяПочти не нужно: то же самое пишут левым, поменяв таблицы местами
ПОЛНОЕ СОЕДИНЕНИЕВсе строки обеих таблицСравнение двух источников: план и факт

Пример: все товары из списка и их цены, даже если цены нет:

ВЫБРАТЬ
	Номенклатура.Ссылка КАК Номенклатура,
	ЕСТЬNULL(Цены.Цена, 0) КАК Цена
ИЗ
	Справочник.Номенклатура КАК Номенклатура
		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цены КАК Цены
		ПО Номенклатура.Ссылка = Цены.Номенклатура
ГДЕ
	НЕ Номенклатура.ЭтоГруппа

Три вещи, которые нужно знать про соединения:

  • ⚠️ NULL справа. При левом соединении поле без пары равно NULL. Сравнение = 0 для него ложно, сумма с ним даёт NULL. Оборачивайте такие поля в ЕСТЬNULL(Поле, 0), а проверяйте конструкцией Поле ЕСТЬ NULL.
  • ⚠️ Условие на правую таблицу в ГДЕ ломает левое соединение. Если написать ГДЕ Цены.Цена > 0, строки без цены отсекутся, и соединение фактически станет внутренним. Условие на правую таблицу ставят в ПО: ПО Номенклатура.Ссылка = Цены.Номенклатура И Цены.Цена > 0.
  • ⚠️ Точка — это тоже соединение. Запись Товары.Ссылка.Контрагент.ИНН платформа превращает в неявные левые соединения. Для поля составного типа соединений будет столько, сколько типов в составе. Если нужен один тип, уточните его: ВЫРАЗИТЬ(Хозрасчетный.Субконто1 КАК Справочник.Номенклатура).Артикул.

Параметры, виртуальные и временные таблицы

Как передать параметры в запрос

Любое значение извне — дату, ссылку, число, список — передают через параметр &Имя:

Запрос.УстановитьПараметр("Начало", НачалоМесяца(ДатаОтчета));
Запрос.УстановитьПараметр("Конец", КонецМесяца(ДатаОтчета));
Запрос.УстановитьПараметр("Склады", МассивСкладов);

Для условия Склад В (&Склады) подходит массив или список значений. Таблицу значений можно передать как источник ИЗ &Таблица КАК Т, но только с помещением во временную таблицу, и у колонок должны быть заданы типы.

Виртуальные таблицы регистров

У регистров накопления есть виртуальные таблицы Остатки, Обороты и ОстаткиИОбороты, у регистров сведений — СрезПервых и СрезПоследних. В скобках передаются период и условие, которое платформа применит до расчёта.

Пример на условном регистре накопления ОстаткиТоваров с измерениями Склад и Номенклатура:

ВЫБРАТЬ
	Остатки.Номенклатура КАК Номенклатура,
	Остатки.КоличествоОстаток КАК Количество
ИЗ
	РегистрНакопления.ОстаткиТоваров.Остатки(&Дата, Склад = &Склад) КАК Остатки

Остатки на дату &Дата считаются на начало этого момента: движения с точно таким же временем в расчёт не попадут. Чтобы их учесть, передают границу: Новый Граница(Дата, ВидГраницы.Включая).

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

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

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

Результаты = Запрос.ВыполнитьПакет();
Выборка = Результаты[1].Выбрать(); // второй запрос пакета, нумерация с нуля

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

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

Группировка, агрегатные функции и итоги

Агрегатные функции считают значение по группе строк: СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ. Все поля, которые выбираются без агрегатной функции, должны быть в СГРУППИРОВАТЬ ПО.

Пример для 1С:Бухгалтерии 3.0: продажи по контрагентам и номенклатуре за период, только где сумма больше 100 000 ₽, с итогом по каждому контрагенту:

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

Чем отличаются похожие конструкции:

  • 📌 ГДЕ режет строки до группировки, ИМЕЮЩИЕ — готовые группы. Условие на сумму группы можно поставить только в ИМЕЮЩИЕ.
  • 📌 КОЛИЧЕСТВО(*) считает строки, КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Товары.Номенклатура) — уникальные значения.
  • 📌 СГРУППИРОВАТЬ ПО схлопывает строки, а ИТОГИ оставляет детальные строки и добавляет над ними строки-итоги.

Результат с итогами обходят вложенными выборками:

Результат = Запрос.Выполнить();
ВыборкаКонтрагент = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаКонтрагент.Следующий() Цикл
	// здесь итог по контрагенту: ВыборкаКонтрагент.Сумма
	ВыборкаДетали = ВыборкаКонтрагент.Выбрать();
	Пока ВыборкаДетали.Следующий() Цикл
		// строки по номенклатуре этого контрагента
	КонецЦикла;
КонецЦикла;

В БП поле Сумма в табличной части реализации может включать НДС или нет — это зависит от флага «Сумма включает НДС» в документе. Для отчёта по выручке без налога вычитайте СуммаНДС там, где налог включён в сумму.

Функции языка запросов, без которых не обходится ни один отчёт

ФункцияЧто делаетПример
ЕСТЬNULL(А, Б)Подставляет Б, если А равно NULLЕСТЬNULL(Остатки.КоличествоОстаток, 0)
ВЫБОР КОГДА … ТОГДА … ИНАЧЕ … КОНЕЦУсловное значениеВЫБОР КОГДА Сумма > 0 ТОГДА "Долг" ИНАЧЕ "Аванс" КОНЕЦ
ВЫРАЗИТЬ(Поле КАК Тип)Приведение типаВЫРАЗИТЬ(Комментарий КАК СТРОКА(100))
НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ)Начало периодаГруппировка продаж по месяцам
ДОБАВИТЬКДАТЕ(Дата, ДЕНЬ, 10)Сдвиг датыПлановая дата оплаты
РАЗНОСТЬДАТ(Д1, Д2, ДЕНЬ)Разница в единицахДней просрочки
ПОДСТРОКА(Стр, 1, 10)Часть строкиПервые символы артикула
ПРЕДСТАВЛЕНИЕ(Ссылка)Текстовое представлениеВыгрузка в файл без лишних обращений
ССЫЛКАПроверка типа ссылкиРегистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
В ИЕРАРХИИЭлемент и все подчинённыеНоменклатура В ИЕРАРХИИ (&Группа)

Отдельный случай — строки неограниченной длины (например, реквизит «Комментарий»). Их нельзя сравнивать, группировать и использовать в РАЗЛИЧНЫЕ. Приведите поле к фиксированной длине через ВЫРАЗИТЬ(… КАК СТРОКА(N)) или возьмите ПОДСТРОКА.

Почему запрос тормозит или падает: оптимизация и типичные ошибки

Что ускоряет запросы 1С

  1. Не выполняйте запрос в цикле. Сто запросов по одной строке медленнее одного запроса по ста строкам. Соберите ссылки в массив или таблицу значений и передайте одним параметром.
  2. Условия по измерениям — в параметры виртуальной таблицы. Остатки(&Дата, Склад = &Склад) считает остатки только по одному складу. То же условие в ГДЕ заставит платформу посчитать остатки по всем складам и потом отбросить лишнее.
  3. Не разыменовывайте составные поля через точку. Регистратор.Дата у регистра даёт соединение с таблицей каждого документа-регистратора. Берите поле Период из самого регистра или уточняйте тип через ВЫРАЗИТЬ.
  4. Вложенный запрос в соединении замените временной таблицей. У временной таблицы есть индекс и известный размер, у вложенного запроса — нет.
  5. Не оборачивайте поле в функцию в условии. ГОД(Документ.Дата) = 2025 не использует индекс по дате, а Документ.Дата МЕЖДУ &Начало И &Конец использует.
  6. Выбирайте только нужные поля. ВЫБРАТЬ * в рабочем коде тянет все реквизиты, включая хранилища значений.
  7. Ограничивайте выборку. Для проверки «есть ли хоть что-то» хватит ВЫБРАТЬ ПЕРВЫЕ 1.

Ключевое слово РАЗРЕШЕННЫЕ нужно, если для пользователя настроены ограничения доступа на уровне записей. Без него запрос пользователя с ограничениями упадёт с ошибкой доступа, а с ним вернёт только доступные записи.

Ошибки, которые встречаются чаще всего

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

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

Как посмотреть текст запроса и результат в отладчике?
Поставьте точку останова после формирования текста, выделите переменную Запрос и откройте её в окне вычисления выражения. Текст лежит в свойстве Текст, параметры — в свойстве Параметры. Результат удобно выгрузить выражением Запрос.Выполнить().Выгрузить().
Чем ОБЪЕДИНИТЬ отличается от ОБЪЕДИНИТЬ ВСЕ?
ОБЪЕДИНИТЬ убирает одинаковые строки из общего результата, ОБЪЕДИНИТЬ ВСЕ оставляет все. Второй вариант быстрее, потому что не ищет дубли; используйте его, если дублей быть не может или они нужны.
Можно ли изменить или удалить данные запросом?
Нет. Язык запросов 1С только читает данные. Запись идёт через объекты (СправочникОбъект, ДокументОбъект) и наборы записей регистров.
Как сделать запрос в 1С без доступа к конфигуратору?
Через внешнюю обработку «Консоль запросов» в пользовательском режиме. Нужны права на открытие внешних обработок, иначе платформа не даст её запустить.
Как написать в тексте запроса строку с кавычками?
Внутри строкового литерала встроенного языка кавычки удваивают: условие на строку “Да” выглядит в коде как ““Да””. Надёжнее не писать такие значения в текст, а передать параметром.