Как читать и писать запросы 1С: от первой выборки до СКД

· 10 мин

Содержание

Когда говорят «1С в запросе», обычно имеют в виду язык запросов 1С:Предприятия. Это встроенный SQL-подобный язык, которым программа читает данные из базы: ВЫБРАТЬ … ИЗ … ГДЕ …. Запрос выполняет объект Запрос во встроенном языке, а платформа переводит текст в SQL той СУБД, на которой стоит база: файловой, MS SQL, PostgreSQL.

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

Чем язык запросов 1С отличается от обычного SQL

Язык запросов похож на SQL, но работает не с таблицами СУБД, а с объектами конфигурации. Вместо dbo._Reference42 вы пишете Справочник.Контрагенты, вместо _Document118 — Документ.РеализацияТоваровУслуг.

Главные отличия:

  • 📖 Только чтение. В языке нет UPDATE, INSERT, DELETE. Изменять данные можно только через объекты встроенного языка, а запрос лишь читает.
  • 🔗 Разыменование через точку. Поле Контрагент.ИНН платформа превратит в соединение со справочником сама, без явного JOIN.
  • 🧮 Виртуальные таблицы. У регистров есть Остатки, Обороты, ОстаткиИОбороты, СрезПоследних — готовые расчёты, которых нет в SQL.
  • 🇷🇺 Два языка ключевых слов. ВЫБРАТЬ и SELECT равноправны, их можно даже смешивать в одном тексте.
  • 🔒 Права пользователя. Ключевое слово РАЗРЕШЕННЫЕ и ограничения доступа на уровне записей (RLS) встроены в язык.
  • ⚙️ Независимость от СУБД. Один и тот же текст работает в файловой базе и на сервере. Платформа сама формирует диалект SQL.

Писать запросы прямо к таблицам СУБД в обход платформы нельзя: имена таблиц меняются после реструктуризации, а права и RLS не применяются.

Синтаксис запроса 1С: основные конструкции

Минимальный запрос состоит из двух частей — что выбрать и откуда:

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

Псевдонимы через КАК лучше давать всегда. Без них поля в результате получают автоматические имена, и обращаться к ним из кода неудобно.

Основные предложения

КонструкцияЧто делаетПример
ВЫБРАТЬ ПЕРВЫЕ 10ограничивает число строктоп-10 должников
ВЫБРАТЬ РАЗЛИЧНЫЕубирает дубли строкуникальные склады в документах
ГДЕфильтр строк до группировкиДата МЕЖДУ &Начало И &Конец
СГРУППИРОВАТЬ ПОсвёртка с агрегатамиСУММА(Сумма) по контрагенту
ИМЕЮЩИЕфильтр после группировкиСУММА(Сумма) > 100000
ОБЪЕДИНИТЬ ВСЕсклеивает выборкиприходы и расходы в одну таблицу
ИТОГИ ПОпромежуточные итогиитог по каждому складу

Соединения

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

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

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

Полезные функции и выражения

  • ВЫБОР КОГДА … ТОГДА … ИНАЧЕ … КОНЕЦ — условие прямо в поле.
  • ЗНАЧЕНИЕ(Перечисление.ВидыОпераций.Возврат) и ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка) — предопределённые значения без параметров.
  • НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ), КОНЕЦПЕРИОДА, ДОБАВИТЬКДАТЕ, РАЗНОСТЬДАТ — работа с датами.
  • ПОДСТРОКА(Строка, 1, 10) — часть строки.
  • ВЫРАЗИТЬ(Поле КАК Документ.ПоступлениеТоваровУслуг) — приведение составного типа к одному.
  • ССЫЛКА — проверка типа: ГДЕ Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг.

Сравнение с NULL через = всегда ложно. Пишите ГДЕ Поле ЕСТЬ NULL или оборачивайте поле в ЕСТЬNULL().

Как выполнить запрос из кода и где его писать

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

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Реализация.Ссылка КАК Документ,
|    Реализация.СуммаДокумента КАК Сумма
|ИЗ
|    Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
|    Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|    И Реализация.Проведен";

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

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Строка(Выборка.Документ) + ": " + Формат(Выборка.Сумма, "ЧДЦ=2"));
КонецЦикла;

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

Где удобнее писать текст

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

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

Сложную выборку проще собрать по шагам. Для этого в одном тексте пишут несколько запросов через точку с запятой — это пакет. Промежуточный результат кладут во временную таблицу через ПОМЕСТИТЬ.

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

ВЫБРАТЬ
    ВТ.Контрагент,
    ВТ.Выручка,
    ВТ.Контрагент.ОсновнойМенеджер КАК Менеджер
ИЗ
    ВТ_Продажи КАК ВТ
ГДЕ
    ВТ.Выручка > 100000

Запрос.Выполнить() вернёт результат последнего запроса пакета. Если нужны результаты всех шагов, вызывайте Запрос.ВыполнитьПакет() — он вернёт массив результатов.

Когда нужен менеджер временных таблиц

Пока временная таблица живёт внутри одного пакета, менеджер не нужен. Если же таблицу надо использовать в другом запросе позже, создайте МенеджерВременныхТаблиц:

МВТ = Новый МенеджерВременныхТаблиц;
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МВТ;
// … запрос с ПОМЕСТИТЬ ВТ_Продажи …

Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МВТ;
// второй запрос видит ВТ_Продажи

Таблицы живут, пока существует менеджер. Ненужную таблицу удаляют командой УНИЧТОЖИТЬ ВТ_Продажи в тексте пакета или закрывают менеджер методом Закрыть().

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

Временную таблицу можно наполнить и из таблицы значений: передать её параметром и написать ВЫБРАТЬ … ПОМЕСТИТЬ ВТ ИЗ &Таблица КАК Т. Колонки таблицы значений при этом должны иметь описание типов, иначе платформа выдаст ошибку.

Параметры в запросе 1С и безопасность данных

Любое значение, которое меняется от вызова к вызову, передавайте параметром: &Контрагент, &Дата, &СписокСкладов. Параметр начинается с амперсанда, а значение задаётся через УстановитьПараметр.

Параметром можно передать массив или список значений. Тогда условие пишут через В:

ГДЕ Остатки.Склад В (&СписокСкладов)

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

Соблазн «подставить строку» в текст понятен: "ГДЕ Наименование = """ + ИмяИзФормы + """". Это плохо по трём причинам:

  • 🛡️ кавычка или служебное слово в пользовательском вводе ломает или меняет запрос;
  • 📅 даты и числа превращаются в строки с разным форматом на разных компьютерах;
  • 🐢 СУБД не может переиспользовать план, если текст каждый раз новый.

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

Поиск по подстроке

Для поиска по части наименования используют ПОДОБНО с параметром-шаблоном. Символы % и _ в шаблоне — служебные, поэтому ввод пользователя нужно экранировать:

ГДЕ Контрагенты.Наименование ПОДОБНО &Шаблон СПЕЦСИМВОЛ "~"

В коде перед установкой параметра замените в строке ~ на ~~, % на ~%, _ на ~_, а потом добавьте % по краям.

Права: РАЗРЕШЕННЫЕ и привилегированный режим

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

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

Оптимизация запросов 1С и поиск медленных мест

Медленный запрос почти всегда медленный по одной из нескольких типовых причин. Вот самые частые:

Что сделаноПочему медленноКак лучше
Запрос внутри цикла по документамтысячи обращений к СУБДодин запрос со списком в параметре
Фильтр виртуальной таблицы в ГДЕрегистр считается целиком, потом режетсяусловие в параметрах таблицы
Точка через составной тип Регистратор.Датасоединение со всеми таблицами документовВЫРАЗИТЬ к нужному типу
Соединение с подзапросомоптимизатор СУБД плохо оценивает подзапроссначала временная таблица с индексом
ИЛИ в условиях соединенияиндекс не используетсяразбить на ОБЪЕДИНИТЬ ВСЕ
Функция над полем в ГДЕНАЧАЛОПЕРИОДА(Дата, ДЕНЬ) = &Д мешает индексуДата МЕЖДУ &Начало И &Конец

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

Это главный приём, который отличает запрос новичка от запроса практика. Сравните:

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

// Быстро: остатки считаются только по нужному складу
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Ост

В первом варианте платформа сначала рассчитает остатки по всем складам. На базе в несколько миллионов движений разница в скорости бывает многократной.

Чем измерять

  • ⏱️ Замер производительности в Конфигураторе (Отладка → Замер производительности) показывает, на какой строке кода уходит время. Если это строка Запрос.Выполнить() — проблема в самом запросе.
  • 📜 Технологический журнал. Файл logcfg.xml с событием DBMSSQL и элементом <plansql/> пишет тексты SQL-запросов, их длительность и план выполнения. Подходит для клиент-серверной базы.
  • 🧭 Средства СУБД. Для MS SQL — профилировщик и планы выполнения, для PostgreSQL — EXPLAIN ANALYZE. Здесь видно, какие индексы реально используются.

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

Запросы в системе компоновки данных

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

Чем запрос в СКД отличается

  • 🧩 Автозаполнение. При включённом флаге «Автозаполнение» на вкладке «Наборы данных» поля запроса сами становятся доступными для отбора и группировки.
  • 🎯 Параметры подхватываются автоматически. Каждый &Параметр из текста появляется на вкладке «Параметры». Там задают тип, значение по умолчанию и видимость для пользователя.
  • 🪄 Расширения языка в фигурных скобках. Конструкции {ГДЕ …}, {ВЫБРАТЬ …} и параметры виртуальных таблиц в фигурных скобках говорят СКД, куда подставлять отборы пользователя. Если поле измерения взять в фигурные скобки внутри параметров виртуальной таблицы, СКД передаст отбор пользователя по этому полю прямо в расчёт остатков, а не наложит его на готовый результат.
  • 🔧 Ресурсы и итоги. Не надо считать итоги через ИТОГИ ПО — их задают на вкладке «Ресурсы», а СКД посчитает их сама.

Практические правила

  1. Не пишите в запросе СКД УПОРЯДОЧИТЬ ПО и ИТОГИ — порядок и итоги задаются в настройках варианта отчёта.
  2. Отборы, которые пользователь будет менять, лучше давать полями, а не жёстким условием ГДЕ: тогда СКД сама протолкнёт их в запрос.
  3. Период для оборотов передавайте параметрами &НачалоПериода и &КонецПериода. Для &КонецПериода в настройках параметра удобно задать выражение КонецПериода(&КонецПериода, "День"), чтобы пользователь выбирал дату без времени.
  4. Проверяйте итоговый текст: в режиме отладки можно получить запрос, который СКД реально отправляет в базу, через макет компоновки.

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

Ошибки в запросах 1С и как их читать

Платформа указывает место ошибки в тексте символами <<?>>. Начинайте разбор именно с этой позиции.

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

Ещё одна частая проблема — не ошибка, а неверный результат. Если суммы после ЛЕВОЕ СОЕДИНЕНИЕ выросли в несколько раз, значит, справа на одну строку слева пришлось несколько строк. Сначала сгруппируйте правую таблицу во временную, а потом соединяйте.

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

Можно ли запросом 1С изменить или удалить данные?
Нет. Язык запросов только читает данные. Изменения делают через объекты встроенного языка: справочник, документ, набор записей регистра.
Есть ли в языке запросов 1С ограничение на число временных таблиц в пакете?
Жёсткого практического лимита нет, но каждая временная таблица создаётся в СУБД. Удаляйте ненужные через УНИЧТОЖИТЬ, особенно в длинных пакетах.
Можно ли писать запросы на английском?
Да. SELECT, FROM, WHERE и остальные английские ключевые слова полностью равноправны русским. Имена объектов конфигурации при этом остаются такими, как в метаданных.
Чем РАЗЛИЧНЫЕ отличается от СГРУППИРОВАТЬ ПО без агрегатов?
Результат одинаковый: уникальные строки. РАЗЛИЧНЫЕ короче и понятнее, когда агрегаты не нужны.
Почему запрос в файловой базе быстрый, а на сервере медленный, или наоборот?
Файловая база и СУБД выполняют запрос разными движками с разными планами. Медленное место ищите средствами конкретной СУБД и технологическим журналом.