Язык запросов в платформе 1С:Предприятие 8 является фундаментальным инструментом для любого разработчика и аналитика. Именно через этот механизм происходит взаимодействие с базой данных, будь то MS SQL Server, PostgreSQL или встроенный файловый вариант. Понимание того, как формируются, исполняются и оптимизируются запросы, напрямую влияет на быстродействие всей информационной системы. Неумение грамотно составить выборку может привести к блокировкам таблиц и существенному замедлению работы пользователей в часы пик.

В отличие от стандартного SQL, язык запросов 1С обладает рядом уникальных особенностей, упрощающих работу с метаданными конфигурации. Вам не нужно знать физические имена таблиц в базе данных; достаточно обращаться к логическим именам объектов, определенным в конфигураторе. Это обеспечивает независимость кода от конкретной СУБД и упрощает миграцию проектов. Однако за этим удобством скрываются сложные механизмы трансляции, которые необходимо учитывать при написании производительного кода.

Данная статья подробно разберет процесс создания объектов запроса, специфику синтаксиса, работу с временными таблицами и методы отладки. Мы рассмотрим не только базовые конструкции ВЫБРАТЬ и ГДЕ, но и более сложные аспекты, такие как соединения таблиц и индексы. Освоение этих навыков позволит вам создавать эффективные отчеты и обработки, которые не будут «ложить» сервер 1С.

Создание объекта запроса и базовый синтаксис

Для выполнения выборки данных в коде необходимо создать специальный объект системы. Это делается через конструктор Новый Запрос. После создания объекта текст запроса присваивается свойству Текст. Важно понимать, что сам по себе конструктор не выполняет выборку; он лишь подготавливает структуру. Непосредственное обращение к данным происходит только после вызова метода Выполнить. Результатом выполнения является объект РезультатЗапроса, который содержит выборку.

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

Рассмотрим простейший пример получения списка номенклатуры. В этом случае мы обращаемся к справочнику через псевдоним. Псевдонимы — это алиасы, которые вы задаете таблицам для удобства обращения к их полям в условиях отбора и списка вывода. Использование псевдонимов обязательно, если вы выбираете данные из нескольких таблиц или если имя поля может конфликтовать с ключевыми словами.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Ссылка,

| Номенклатура.Наименование КАК Наименование

|ИЗ

| Справочник.Номенклатура КАК Номенклатура";

Результат = Запрос.Выполнить;

Обратите внимание на использование вертикальной черты | в начале строк текста запроса. Это требование платформы для корректного выделения блока текста в коде 1С. Без этого символа редактор кода не сможет правильно распознать структуру запроса, что вызовет ошибки подсветки синтаксиса и потенциальные проблемы при выполнении. Всегда следите за отступами внутри строки запроса для поддержания чистоты кода.

💡

Используйте конструктор запросов (кнопка с изображением воронки) для автоматической генерации текста. Это исключит опечатки в именах полей и таблиц, а также автоматически проставит необходимые псевдонимы.

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

Одной из самых критичных ошибок при работе с данными является подстановка значений переменных прямо в текст запроса через конкатенацию строк. Такой подход не только усложняет чтение кода, но и создает уязвимости, аналогичные SQL-инъекциям в веб-разработке. В 1С существует механизм параметров, который позволяет безопасно передавать значения внутрь запроса. Параметры объявляются в тексте через символ & и заполняются перед выполнением через коллекцию Параметры.

Использование параметров также положительно сказывается на производительности. Сервер 1С и СУБД могут кэшировать план выполнения запроса, если его текст остается неизменным, а меняются только значения параметров. Если же вы каждый раз собираете новый текст запроса, подставляя значения вручную, сервер вынужден заново компилировать план выполнения, что расходует лишние ресурсы процессора. Это особенно важно в высоконагруженных системах с большим количеством одновременных пользователей.

При работе с параметрами необходимо учитывать их типы. Платформа автоматически пытается преобразовать переданное значение к типу, ожидаемому в условии. Однако явное указание типов или использование приведенных типов может потребоваться в сложных случаях. Например, если параметр может быть либо ссылкой на документ, либо неопределено, это нужно корректно обработать в коде перед передачей в запрос.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| РегистрНакопления.Продажи.Сумма КАК Сумма

|ИЗ

| РегистрНакопления.Продажи КАК Продажи

|ГДЕ

| Продажи.Период МЕЖДУ &НачПериода И &КонПериода";

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

Запрос.Параметры.Вставить("КонПериода", КонецДня(ТекущаяДата));

Результат = Запрос.Выполнить;

⚠️ Внимание: Никогда не передавайте в параметры запроса объекты, которые не могут быть сериализованы или имеют сложный внутренний контекст выполнения (например, некоторые виды объектов метаданных в специфических контекстах). Это может привести к ошибке выполнения на сервере.

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

📊 Какой способ подстановки значений вы используете чаще?
Прямая конкатенация строк
Параметры запроса (&Параметр)
Конструктор запросов
Не пишу запросы вручную

Работа с временными таблицами

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

Использование временных таблиц позволяет разбить сложный запрос на несколько логических этапов. Это улучшает читаемость кода и часто дает выигрыш в производительности, так как промежуточные данные могут быть индексированы или отфильтрованы до участия в основных соединениях. Имя временной таблицы начинается с символа #. Обращение к ней в последующих частях запроса происходит так же, как к обычной таблице метаданных.

При создании временной таблицы можно явно указать структуру полей и их типы, хотя чаще всего структура наследуется из результата выборки. Чрезмерное использование больших временных таблиц может привести к исчерпанию памяти сервера 1С.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Ссылка,

| СУММА(Остатки.Количество) КАК ОбщееКоличество

|ПОМЕСТИТЬ #ВременныеОстатки

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Остатки КАК Остатки

| ПО Номенклатура.Ссылка = Остатки.Номенклатура

|ГДЕ

| Остатки.Период >= &НачПериода

|

|СГРУППИРОВАТЬ ПО

| Номенклатура.Ссылка

|

|ВЫБРАТЬ

| ВременныеОстатки.Ссылка,

| ВременныеОстатки.ОбщееКоличество

|ИЗ

| #ВременныеОстатки КАК ВременныеОстатки

|ГДЕ

| ВременныеОстатки.ОбщееКоличество > 0";

Результат = Запрос.Выполнить;

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

☑️ Оптимизация работы с временными таблицами

Выполнено: 0 / 4

Соединения таблиц и типыJoin

Самая мощная и одновременно сложная часть языка запросов — это соединение таблиц. В 1С поддерживаются основные типы соединений: ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN), ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) и ПРАВОЕ СОЕДИНЕНИЕ (RIGHT JOIN). Выбор правильного типа соединения критически важен для корректности получаемых данных. Ошибка в типе соединения может привести к потере строк или, наоборот, к их дублированию.

Внутреннее соединение возвращает только те записи, для которых есть соответствие в обеих таблицах. Если в правой таблице нет.matching записей, строка из левой таблицы в результат не попадет. Левое соединение возвращает все записи из левой таблицы, а из правой — только те, где есть совпадение. Если совпадения нет, поля из правой таблицы будут заполнены значениями NULL (или пустыми значениями для типов 1С). Понимание этой разницы необходимо при формировании отчетов по остаткам или продажам.

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

Тип соединения Описание поведения Когда применять
ВНУТРЕННЕЕ Только совпадающие записи из обеих таблиц Когда нужны данные, существующие в обоих регистрах/справочниках
ЛЕВОЕ Все записи левой + совпадения правой Когда нужно показать все товары, даже если продаж не было
ПРАВОЕ Все записи правой + совпадения левой Редко, аналогично левому, но таблицы меняются местами

При использовании нескольких соединений необходимо внимательно следить за порядком их следования и приоритетом вычислений. В 1С можно использовать скобки для группировки соединений, хотя синтаксис требует внимательности. Неправильная вложенность соединений может привести к тому, что фильтр ГДЕ сработает не так, как ожидалось, отсекая нужные строки до завершения соединения.

Особенность полного внешнего соединения

В 1С нет прямого синтаксического аналога FULL OUTER JOIN как в классическом SQL. Для реализации полной внешней выборки обычно используют объединение (ОБЪЕДИНИТЬ) левого и правого соединений, либо левое соединение с проверкой на NULL и дополнительной выборкой.

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

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

Частой ошибкой новичков является попытка выбрать поле, которое не входит ни в список агрегатных функций, ни в группу. Это вызывает ошибку выполнения. Логика здесь проста: если вы группируете данные по складу, то для каждого склада может быть много разных товаров. Система не знает, какой именно товар вывести в строку результата, если вы не попросили её посчитать их количество или сумму. Поэтому все «неагрегированные» поля должны быть в группировке.

Для фильтрации результатов агрегации используется конструкция ИМЕЮЩИЕ (аналог SQL HAVING). Она отличается от ГДЕ тем, что применяется уже после того, как данные сгруппированы и посчитаны. В условии ИМЕЮЩИЕ можно использовать только агрегатные функции или поля, участвующие в группировке. Это позволяет отбирать, например, только те склады, где общая сумма остатков превышает миллион рублей.

Запрос.Текст = 

"ВЫБРАТЬ

| Остатки.Склад,

| СУММА(Остатки.Сумма) КАК ОбщаяСумма

|ИЗ

| РегистрНакопления.Остатки КАК Остатки

|СГРУППИРОВАТЬ ПО

| Остатки.Склад

|ИМЕЮЩИЕ

| СУММА(Остатки.Сумма) > 1000000";

Агрегатные функции игнорируют значения NULL. Если в поле, по которому считается сумма, встречаются пустые значения, они не влияют на результат, в отличие от некоторых других систем, где это могло бы привести к ошибке. Однако при подсчете количества (КОЛИЧЕСТВО()) учитываются все строки, включая те, где поля пусты. Разница между КОЛИЧЕСТВО(Поле) и КОЛИЧЕСТВО() заключается в том, что первая функция не считает строки с NULL в указанном поле.

💡

Помните золотое правило группировки: каждое поле в разделе ВЫБРАТЬ должно быть либо обернуто в агрегатную функцию, либо явно указано в разделе СГРУППИРОВАТЬ ПО. Исключений из этого правила в 1С нет.

Оптимизация и анализ производительности

Написание работающего запроса — это только половина дела. Вторая, не менее важная часть —, что этот запрос выполняется быстро и не блокирует работу других пользователей. Производительность запроса зависит от множества факторов: наличия индексов, объема выбираемых данных, типа соединений и сложности условий отбора. Платформа 1С предоставляет мощные инструменты для анализа выполнения запросов, главным из которых является технологический журнал (ТЖ) и встроенный анализатор запросов.

Одним из первых правил оптимизации является отбор данных на стороне СУБД. Старайтесь максимально сужать выборку с помощью условий в блоке ГДЕ. Избегайте выбора всех записей с последующей фильтрацией в коде 1С. Каждый лишний байт, переданный из базы данных в сервер 1С, и каждая лишняя строка, обработанная в цикле, снижают общую производительность системы. Используйте индексные поля для условий отбора, особенно по датам и ссылкам.

Также стоит обращать внимание на использование функций в условиях отбора. Если вы применяете функцию к полю таблицы (например, ГОД(Период) = 2023), это часто приводит к тому, что индекс по этому полю не используется, и происходит полный скан таблицы. Лучше переписать условие на диапазон значений (например, Период МЕЖДУ.. И..), чтобы СУБД могла эффективно использовать индекс.

⚠️ Внимание: Интерфейсы и возможности встроенного анализатора запросов могут различаться в разных версиях платформы 1С:Предприятие. В новых версиях (8.3.20+) функционал значительно расширен. Всегда проверяйте актуальность инструментов в документации к вашей конкретной версии платформы или в личном кабинете поддержки пользователей 1С.

Для диагностики медленных запросов используйте режим отладки с включенным замером производительности. Он покажет время выполнения каждого этапа запроса и общее потребление ресурсов. В сложных случаях может потребоваться анализ плана выполнения на стороне СУБД (например, в MS SQL Server через Execution Plan), чтобы понять, какие индексы используются и где возникают узкие места. Часто создание недостающего индекса в конфигураторе решает проблему мгновенно.

💡

Избегайте использования конструкции"НЕ" в условиях отбора по индексным полям, если это возможно. Условие"Где НЕ Ссылка = Значение" часто менее производительно, чем выборка всех записей с последующим исключением, так как оптимизатору сложнее построить эффективный план.

Часто задаваемые вопросы (FAQ)

Как получить количество записей в результате запроса без выбора всех данных?

Для этого используйте агрегатную функцию КОЛИЧЕСТВО(*) в разделе ВЫБРАТЬ. В этом случае запрос вернет одну строку с одним числом, что значительно быстрее, чем выборка всех полей всех записей с последующим подсчетом в коде.

Можно ли выполнять запросы к регистрам сведений без указания периода?

Да, можно. Однако для регистров сведений, у которых включено ведение периодичности (внутри дня или внутри месяца), отсутствие периода в условии может привести к получению некорректных данных или дублированию записей. Рекомендуется всегда явно указывать срез (последний или первый) или конкретный период.

Что делать, если запрос выполняется слишком долго?

В первую очередь проверьте условия отбора и наличие индексов по полям, участвующим в ГДЕ и ПО. Воспользуйтесь анализатором запросов для выявления узких мест. Попробуйте разбить сложный запрос на несколько этапов с использованием временных таблиц. Также проверьте, не блокируется ли запрос другими активными транзакциями.

Как передать в запрос список значений (массив)?

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