Как читать и писать запросы 1С: от первой выборки до СКД
Содержание
Когда говорят «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С:Предприятие» через
Файл → Открыть. Удобна для разовых выборок: пишете текст, задаёте параметры, смотрите результат таблицей. - Отладчик. В точке останова можно посмотреть текст запроса и параметры, а результат выгрузить в таблицу значений и раскрыть в окне вычисления выражения.
Временные таблицы и пакетные запросы
Сложную выборку проще собрать по шагам. Для этого в одном тексте пишут несколько запросов через точку с запятой — это пакет. Промежуточный результат кладут во временную таблицу через ПОМЕСТИТЬ.
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
СУММА(Продажи.Сумма) КАК Выручка
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Контрагент
ИНДЕКСИРОВАТЬ ПО
Контрагент
;
ВЫБРАТЬ
ВТ.Контрагент,
ВТ.Выручка,
ВТ.Контрагент.ОсновнойМенеджер КАК Менеджер
ИЗ
ВТ_Продажи КАК ВТ
ГДЕ
ВТ.Выручка > 100000
Запрос.Выполнить() вернёт результат последнего запроса пакета. Если нужны результаты всех шагов, вызывайте Запрос.ВыполнитьПакет() — он вернёт массив результатов.
Когда нужен менеджер временных таблиц
Пока временная таблица живёт внутри одного пакета, менеджер не нужен. Если же таблицу надо использовать в другом запросе позже, создайте МенеджерВременныхТаблиц:
МВТ = Новый МенеджерВременныхТаблиц;
Запрос1 = Новый Запрос;
Запрос1.МенеджерВременныхТаблиц = МВТ;
// … запрос с ПОМЕСТИТЬ ВТ_Продажи …
Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МВТ;
// второй запрос видит ВТ_Продажи
Таблицы живут, пока существует менеджер. Ненужную таблицу удаляют командой УНИЧТОЖИТЬ ВТ_Продажи в тексте пакета или закрывают менеджер методом Закрыть().
ИНДЕКСИРОВАТЬ ПО ставьте на поля, по которым временная таблица потом соединяется или фильтруется. На маленьких таблицах в пару сотен строк индекс выигрыша не даёт.
Временную таблицу можно наполнить и из таблицы значений: передать её параметром и написать ВЫБРАТЬ … ПОМЕСТИТЬ ВТ ИЗ &Таблица КАК Т. Колонки таблицы значений при этом должны иметь описание типов, иначе платформа выдаст ошибку.
Параметры в запросе 1С и безопасность данных
Любое значение, которое меняется от вызова к вызову, передавайте параметром: &Контрагент, &Дата, &СписокСкладов. Параметр начинается с амперсанда, а значение задаётся через УстановитьПараметр.
Параметром можно передать массив или список значений. Тогда условие пишут через В:
ГДЕ Остатки.Склад В (&СписокСкладов)
Почему нельзя склеивать текст запроса
Соблазн «подставить строку» в текст понятен: "ГДЕ Наименование = """ + ИмяИзФормы + """". Это плохо по трём причинам:
- 🛡️ кавычка или служебное слово в пользовательском вводе ломает или меняет запрос;
- 📅 даты и числа превращаются в строки с разным форматом на разных компьютерах;
- 🐢 СУБД не может переиспользовать план, если текст каждый раз новый.
Значения пользователя — только через параметры, никогда через конкатенацию. Склейка допустима лишь для частей, которые вы задаёте сами в коде: имя таблицы, набор полей.
Поиск по подстроке
Для поиска по части наименования используют ПОДОБНО с параметром-шаблоном. Символы % и _ в шаблоне — служебные, поэтому ввод пользователя нужно экранировать:
ГДЕ Контрагенты.Наименование ПОДОБНО &Шаблон СПЕЦСИМВОЛ "~"
В коде перед установкой параметра замените в строке ~ на ~~, % на ~%, _ на ~_, а потом добавьте % по краям.
Права: РАЗРЕШЕННЫЕ и привилегированный режим
Если у роли пользователя настроены ограничения доступа на уровне записей, запрос без слова РАЗРЕШЕННЫЕ упадёт с ошибкой нехватки прав, как только встретит недоступную запись. Со словом ВЫБРАТЬ РАЗРЕШЕННЫЕ платформа просто отбросит такие строки.
Привилегированный режим (в общем модуле с флагом «Привилегированный» или через УстановитьПривилегированныйРежим(Истина)) снимает проверки прав. Включайте его только на сервере и только там, где пользователь не может повлиять на текст и параметры запроса.
Оптимизация запросов 1С и поиск медленных мест
Медленный запрос почти всегда медленный по одной из нескольких типовых причин. Вот самые частые:
| Что сделано | Почему медленно | Как лучше |
|---|---|---|
| Запрос внутри цикла по документам | тысячи обращений к СУБД | один запрос со списком в параметре |
Фильтр виртуальной таблицы в ГДЕ | регистр считается целиком, потом режется | условие в параметрах таблицы |
Точка через составной тип Регистратор.Дата | соединение со всеми таблицами документов | ВЫРАЗИТЬ к нужному типу |
| Соединение с подзапросом | оптимизатор СУБД плохо оценивает подзапрос | сначала временная таблица с индексом |
ИЛИ в условиях соединения | индекс не используется | разбить на ОБЪЕДИНИТЬ ВСЕ |
Функция над полем в ГДЕ | НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) = &Д мешает индексу | Дата МЕЖДУ &Начало И &Конец |
Параметры виртуальных таблиц
Это главный приём, который отличает запрос новичка от запроса практика. Сравните:
// Медленно: остатки по всем складам, потом фильтр
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Ост
ГДЕ Ост.Склад = &Склад
// Быстро: остатки считаются только по нужному складу
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Ост
В первом варианте платформа сначала рассчитает остатки по всем складам. На базе в несколько миллионов движений разница в скорости бывает многократной.
Чем измерять
- ⏱️ Замер производительности в Конфигураторе (
Отладка → Замер производительности) показывает, на какой строке кода уходит время. Если это строкаЗапрос.Выполнить()— проблема в самом запросе. - 📜 Технологический журнал. Файл
logcfg.xmlс событиемDBMSSQLи элементом<plansql/>пишет тексты SQL-запросов, их длительность и план выполнения. Подходит для клиент-серверной базы. - 🧭 Средства СУБД. Для MS SQL — профилировщик и планы выполнения, для PostgreSQL —
EXPLAIN ANALYZE. Здесь видно, какие индексы реально используются.
Технологический журнал на рабочем сервере быстро растёт. Включайте его с фильтром по длительности и базе, на короткое время, и потом отключайте.
Запросы в системе компоновки данных
В СКД запрос — основа набора данных. Отчёт берёт его текст, а дальше сам добавляет группировки, отборы и итоги, которые пользователь выбрал в настройках. Поэтому запрос для СКД пишут чуть иначе, чем для кода.
Чем запрос в СКД отличается
- 🧩 Автозаполнение. При включённом флаге «Автозаполнение» на вкладке «Наборы данных» поля запроса сами становятся доступными для отбора и группировки.
- 🎯 Параметры подхватываются автоматически. Каждый
&Параметриз текста появляется на вкладке «Параметры». Там задают тип, значение по умолчанию и видимость для пользователя. - 🪄 Расширения языка в фигурных скобках. Конструкции
{ГДЕ …},{ВЫБРАТЬ …}и параметры виртуальных таблиц в фигурных скобках говорят СКД, куда подставлять отборы пользователя. Если поле измерения взять в фигурные скобки внутри параметров виртуальной таблицы, СКД передаст отбор пользователя по этому полю прямо в расчёт остатков, а не наложит его на готовый результат. - 🔧 Ресурсы и итоги. Не надо считать итоги через
ИТОГИ ПО— их задают на вкладке «Ресурсы», а СКД посчитает их сама.
Практические правила
- Не пишите в запросе СКД
УПОРЯДОЧИТЬ ПОиИТОГИ— порядок и итоги задаются в настройках варианта отчёта. - Отборы, которые пользователь будет менять, лучше давать полями, а не жёстким условием
ГДЕ: тогда СКД сама протолкнёт их в запрос. - Период для оборотов передавайте параметрами
&НачалоПериодаи&КонецПериода. Для&КонецПериодав настройках параметра удобно задать выражениеКонецПериода(&КонецПериода, "День"), чтобы пользователь выбирал дату без времени. - Проверяйте итоговый текст: в режиме отладки можно получить запрос, который СКД реально отправляет в базу, через макет компоновки.
Запрос, который в консоли работает за секунду, в СКД может тормозить. Обычно причина в том, что отбор пользователя не дошёл до параметров виртуальной таблицы и наложился уже на результат.
Ошибки в запросах 1С и как их читать
Платформа указывает место ошибки в тексте символами <<?>>. Начинайте разбор именно с этой позиции.
Ещё одна частая проблема — не ошибка, а неверный результат. Если суммы после ЛЕВОЕ СОЕДИНЕНИЕ выросли в несколько раз, значит, справа на одну строку слева пришлось несколько строк. Сначала сгруппируйте правую таблицу во временную, а потом соединяйте.