Работа с параметрами в запросах 1С:Предприятие — одна из ключевых тем для разработчиков, которая позволяет создавать динамичные и гибкие отчеты. Без правильного использования параметров даже простые выборки данных могут превратиться в громоздкий код с дублированием логики. Но как именно передавать значения в запрос, какие типы параметров существуют и как избежать типичных ошибок?
В этой статье мы разберем все аспекты работы с параметрами — от базового синтаксиса до оптимизации сложных запросов. Вы узнаете, как использовать именованные и позиционные параметры, работать с датами и коллекциями, а также как правильно оформлять запросы для разных версий платформы 1С. Особое внимание уделим практическим примерам, которые можно сразу применить в своих конфигурациях.
Параметры в запросах 1С не просто упрощают код — они делают его безопаснее. Вместо конкатенации строк (что чревато SQL-инъекциями) платформа предлагает специальный механизм подстановки значений. Это особенно важно при работе с пользовательским вводом или данными из внешних источников.
Но есть и подводные камни: неправильное использование параметров может привести к ошибкам выполнения или даже падению производительности. Например, передача коллекции как параметра в старых версиях платформы работала иначе, чем в современных. Мы подробно разберем все эти нюансы.
1. Основы синтаксиса: как добавить параметр в запрос
Базовый синтаксис добавления параметров в запросы 1С выглядит так: значения передаются через метод УстановитьПараметр() перед выполнением запроса. Рассмотрим простейший пример с одним параметром:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Артикул = &Артикул";
Запрос.УстановитьПараметр("Артикул", "ABC123");
Результат = Запрос.Выполнить();
Здесь &Артикул — это именованный параметр, который будет заменен на значение "ABC123" при выполнении. Обратите внимание на символ & перед именем параметра — это обязательный синтаксис.
Платформа 1С:Предприятие 8.3 поддерживает два типа параметров:
- 🔹 Именованные — указываются с символом
&(например,&ДатаНачала). Рекомендуемый способ, так как код становится более читаемым. - 🔹 Позиционные — обозначаются знаком
?и заменяются по порядку (первый?— первый параметр, второй?— второй и т.д.). Менее наглядно, но иногда удобно для простых запросов.
Пример с позиционными параметрами:
Запрос.Текст =
"ВЫБРАТЬ
| Документ.ЗаказПокупателя.Номер КАК Номер
|ИЗ
| Документ.ЗаказПокупателя КАК ЗаказПокупателя
|ГДЕ
| ЗаказПокупателя.Дата МЕЖДУ ? И ?";
Запрос.УстановитьПараметр(1, НачалоПериода); // Первый параметр
Запрос.УстановитьПараметр(2, КонецПериода); // Второй параметр
2. Работа с разными типами данных
Платформа 1С автоматически преобразует типы данных при подстановке параметров, но некоторые нюансы важно учитывать. Например, при работе с датами лучше явно указывать формат, чтобы избежать ошибок из-за региональных настроек:
Запрос.Текст =
"ВЫБРАТЬ
| Документ.РеализацияТоваровУслуг.СуммаДокумента КАК Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата = &ДатаДок";
Запрос.УстановитьПараметр("ДатаДок", Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd"));
Для коллекций (массивов, списков значений) используется специальный синтаксис с ключевым словом В:
СписокНоменклатуры = Новый СписокЗначений;
СписокНоменклатуры.Добавить("Товар1");
СписокНоменклатуры.Добавить("Товар2");
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование В (&СписокТоваров)";
Запрос.УстановитьПараметр("СписокТоваров", СписокНоменклатуры);
Особое внимание требуют NULL-значения. Если параметр может быть не задан, используйте конструкцию ЕСТЬ NULL:
Если Не ЗначениеЗаполнено(ПараметрПоиска) Тогда
Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Поле = &Параметр ИЛИ &Параметр ЕСТЬ NULL";
КонецЕсли;
| Тип данных | Пример параметра | Особенности |
|---|---|---|
| Строка | &Наименование = "Товар" |
Автоматическое экранирование кавычек |
| Число | &Количество > 10 |
Поддерживаются все числовые типы (Число, Дробь) |
| Дата | &Дата МЕЖДУ ДАТАВРЕМЯ(2023,1,1) И ДАТАВРЕМЯ(2023,12,31) |
Рекомендуется использовать Формат() для унификации |
| Булево | &Активен = ИСТИНА |
В запросе преобразуется в 1 или 0 |
| Ссылка | &Контрагент = &СсылкаНаКонтрагента |
Работает со всеми справочниками и документами |
Для ускорения выполнения запросов с параметрами-коллекциями используйте временные таблицы вместо прямой подстановки большого списка значений.
3. Продвинутые техники: динамические параметры и оптимизация
При создании сложных отчетов часто требуется динамически формировать текст запроса и его параметры. Например, когда пользователь может выбрать разные критерии фильтрации:
ТекстЗапроса = "ВЫБРАТЬ | ... |ГДЕ";
Условия = Новый Массив;
Если ЗначениеЗаполнено(Фильтр.ДатаНачала) Тогда
Условия.Добавить("Дата >= &ДатаНачала");
Запрос.УстановитьПараметр("ДатаНачала", Фильтр.ДатаНачала);
КонецЕсли;
Если ЗначениеЗаполнено(Фильтр.Контрагент) Тогда
Условия.Добавить("Контрагент = &Контрагент");
Запрос.УстановитьПараметр("Контрагент", Фильтр.Контрагент);
КонецЕсли;
Если Условия.Количество() > 0 Тогда
ТекстЗапроса = ТекстЗапроса + " " + СтрСоединить(Условия, " И ");
Иначе
ТекстЗапроса = ТекстЗапроса + " 1=1"; // Если нет условий
КонецЕсли;
Для оптимизации производительности при работе с большими объемами данных:
- 🚀 Используйте временные таблицы для промежуточных результатов вместо вложенных запросов с параметрами.
- 📊 Ограничивайте выборку с помощью
ПЕРВЫЕилиРАЗЛИЧНЫЕ, если не нужны все записи. - ⚡ Избегайте параметров в функциях типа
НАЧИНАЕТСЯилиСОДЕРЖИТ— это блокирует использование индексов.
Критическая особенность платформы 8.3.20+: при передаче коллекции как параметра в запрос с оператором В сервер автоматически оптимизирует выполнение, преобразуя его в временную таблицу. В более ранних версиях это могло приводить к полному сканированию таблиц.
Использовать именованные параметры вместо конкатенации строк|
Проверять заполненность параметров перед установкой|
Ограничивать глубину вложенных запросов с параметрами|
Тестировать производительность с разными типами данных-->
4. Типичные ошибки и как их избежать
Ошибка №1: Пропущенный символ & перед параметром. В этом случае платформа воспринимает текст как часть запроса, что приводит к синтаксической ошибке:
// Неверно:
ГДЕ Дата = ДатаНачала // Ошибка: ДатаНачала не объявлена
// Правильно:
ГДЕ Дата = &ДатаНачала
Ошибка №2: Несоответствие типов данных. Например, передача строки вместо даты:
Запрос.УстановитьПараметр("Дата", "01.01.2023"); // Ошибка!
// Правильно:
Запрос.УстановитьПараметр("Дата", Дата(2023, 1, 1));
Ошибка №3: Использование параметров в выражениях без явного приведения типов. Это может привести к неожиданным результатам:
// Проблема:
ГДЕ Сумма > &Порог // Если &Порог строка, сравнение будет некорректным
// Решение:
ГДЕ Сумма > ЧИСЛО(&Порог)
⚠️ Внимание: В версиях платформы ниже 8.3.18 передача пустого списка значений как параметра могла приводить к ошибке выполнения. Всегда проверяйте коллекции на пустоту перед установкой параметра.
Ошибка №4: Избыточные параметры. Например, когда один и тот же параметр используется многократно в запросе, но устанавливается несколько раз с разными значениями. Последний УстановитьПараметр() перезаписывает предыдущие.
Для отладки сложных запросов используйте метод Запрос.ПолучитьТекстЗапроса() — он покажет финальный текст с подставленными параметрами (в режиме отладки).
5. Работа с параметрами в разных версиях 1С
Механизм параметров эволюционировал вместе с платформой. В таблице ниже — ключевые различия между версиями:
| Версия платформы | Особенности работы с параметрами | Рекомендации |
|---|---|---|
| 8.2 и ниже | Поддержка только позиционных параметров (?) |
Используйте конкатенацию с экранированием для именованных параметров |
| 8.3.1–8.3.9 | Появились именованные параметры, но ограниченная поддержка коллекций | Для больших списков используйте временные таблицы |
| 8.3.10–8.3.17 | Улучшена работа с датами и NULL-значениями в параметрах | Можно использовать ЕСТЬ NULL напрямую в условиях |
| 8.3.18+ | Оптимизация запросов с параметрами-коллекциями (автоматическое создание временных таблиц) | Рекомендуемая версия для работы с большими данными |
Для обеспечения совместимости кода между версиями:
- 🔧 Всегда проверяйте заполненность параметров перед установкой.
- 📋 Используйте универсальные форматы дат (
yyyy-MM-dd). - 🔄 Для коллекций реализуйте альтернативную логику через временные таблицы в старых версиях.
⚠️ Внимание: В конфигурациях с поддержкой нескольких версий платформы (например, 1С:ERP) тестируйте запросы с параметрами на всех целевых релизах. Некоторые оптимизации могут работать по-разному.
6. Практические примеры для разных задач
Пример 1. Фильтрация по нескольким критериям:
Запрос.Текст =
"ВЫБРАТЬ
| Документ.ПоступлениеТоваров.Номер КАК Номер,
| Документ.ПоступлениеТоваров.Дата КАК Дата
|ИЗ
| Документ.ПоступлениеТоваров КАК Поступление
|ГДЕ
| Поступление.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И (&Контрагент ЕСТЬ NULL ИЛИ Поступление.Контрагент = &Контрагент)
| И (&Склад ЕСТЬ NULL ИЛИ Поступление.Склад = &Склад)";
Запрос.УстановитьПараметр("ДатаНачала", НачалоПериода);
Запрос.УстановитьПараметр("ДатаОкончания", КонецПериода);
Запрос.УстановитьПараметр("Контрагент", Фильтр.Контрагент); // Может быть NULL
Запрос.УстановитьПараметр("Склад", Фильтр.Склад);
Пример 2. Использование параметра в подзапросе:
Запрос.Текст =
"ВЫБРАТЬ
| Товар.Наименование,
| Товар.КоличествоОстаток
|ИЗ
| Справочник.Номенклатура КАК Товар
|ГДЕ
| Товар.Артикул В (
| ВЫБРАТЬ РАЗЛИЧНЫЕ Артикул
| ИЗ Документ.ЗаказПокупателя.Товары
| ГДЕ ЗаказПокупателя.Ссылка = &СсылкаНаЗаказ
| )";
Запрос.УстановитьПараметр("СсылкаНаЗаказ", ТекущийДокумент.Ссылка);
Пример 3. Динамическое формирование списка полей:
Используется когда нужно выбрать разные поля в зависимости от условий. Например, для разных ролей пользователей могут быть доступны разные колонки отчета. Код формирует строку с перечислением полей через запятую, а затем подставляет её в основной запрос.Как работает динамическое формирование полей?
ПоляДляВыбора = Новый Массив;
ПоляДляВыбора.Добавить("Номенклатура.Наименование КАК Наименование");
Если ПоказыватьЦены Тогда
ПоляДляВыбора.Добавить("Цена КАК Цена");
ПоляДляВыбора.Добавить("Сумма КАК Сумма");
КонецЕсли;
Если ПоказыватьОстатки Тогда
ПоляДляВыбора.Добавить("Остаток КАК Остаток");
КонецЕсли;
ТекстЗапроса = "ВЫБРАТЬ " + СтрСоединить(ПоляДляВыбора, ", ") + "
|ИЗ РегистрНакопления.ОстаткиТоваров...";
Для сложных динамических запросов всегда используйте подготовленные выражения (Prepared Statements) через параметры — это защищает от SQL-инъекций и улучшает производительность.
7. Оптимизация производительности запросов с параметрами
Параметры сами по себе не ухудшают производительность, но их неправильное использование может приводить к полным сканированиям таблиц. Основные правила оптимизации:
1. Избегайте параметров в функциях над полями:
// Плохо (индекс не используется):
ГДЕ НАЧИНАЕТСЯ(Номенклатура.Наименование, &Префикс)
// Хорошо:
ГДЕ Номенклатура.Наименование ПОДОБНО &Шаблон
2. Используйте временные таблицы для больших коллекций:
// Вместо:
ГДЕ Товар В (&СписокТоваров) // Для списка из 1000+ элементов
// Лучше:
ВЫБРАТЬ ...
В ТВ_Товары
УСТАНОВИТЬ ;
ВСТАВИТЬ В ТВ_Товары (Товар)
ВЫБРАТЬ &СписокТоваров.Товар ИЗ &СписокТоваров КАК СписокТоваров;
ГДЕ Товар В (ВЫБРАТЬ Товар ИЗ ТВ_Товары)
3. Ограничивайте диапазоны дат:
Всегда добавляйте условия по датам, даже если они широкие. Это позволяет серверу 1С использовать индексы по временным полям:
ГДЕ Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
// Вместо:
ГДЕ Дата >= &ДатаНачала
4. Тестируйте планы выполнения:
В конфигураторе можно включить отображение плана выполнения запроса (меню Сервис → Параметры → Отладка → Показывать план выполнения запроса). Это поможет выявить "узкие места".
⚠️ Внимание: В кластерных базах данных (например, при работе с 1С:SQL или PostgreSQL) неэффективные запросы с параметрами могут блокировать таблицы. Всегда тестируйте нагрузку в условиях, близких к боевым.
8. Альтернативные подходы: когда параметры не подходят
В некоторых случаях использование параметров может быть неудобным или невозможным. Рассмотрим альтернативы:
1. Конкатенация строк с экранированием:
Подходит для простых значений, когда динамически формируется часть запроса:
ИмяПоля = "Наименование";
ЗначениеПоля = ЭкранироватьСтроку(ПользовательскийВвод);
Запрос.Текст = "ВЫБРАТЬ " + ИмяПоля + " ИЗ Справочник.Номенклатура
|ГДЕ " + ИмяПоля + " = '" + ЗначениеПоля + "'";
2. Временные таблицы:
Идеальны для сложных фильтров или когда нужно многократно использовать один набор данных:
// Создание временной таблицы
Запрос.Текст = "ВЫБРАТЬ * В ТВ_Фильтр ИЗ &ТаблицаФильтров КАК Фильтры";
Запрос.УстановитьПараметр("ТаблицаФильтров", ТаблицаЗначенийСФильтрами);
Запрос.Выполнить();
// Использование в основном запросе
Запрос.Текст = "ВЫБРАТЬ Товары.* ИЗ Справочник.Номенклатура КАК Товары
|ГДЕ Товары.Артикул В (ВЫБРАТЬ Артикул ИЗ ТВ_Фильтр)";
3. Хранимые процедуры (для внешних СУБД):
При работе с MS SQL Server или PostgreSQL можно перенести логику в хранимые процедуры, где параметры обрабатываются на уровне СУБД.
4. Объектная модель:
Для простых выборок иногда эффективнее использовать объекты 1С напрямую:
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.Артикул = ИскомыйАртикул Тогда
// Обработка
КонецЕсли;
КонецЦикла;
| Подход | Когда использовать | Ограничения |
|---|---|---|
| Параметры | Стандартные запросы, динамическая фильтрация | Ограничения на типы данных в старых версиях |
| Конкатенация | Динамическое формирование структуры запроса | Риск SQL-инъекций, сложное экранирование |
| Временные таблицы | Сложные фильтры, большие наборы данных | Требует дополнительных запросов для создания |
| Объектная модель | Простые выборки, работа с отдельными объектами | Низкая производительность для больших данных |
Параметры в запросах — это баланс между гибкостью, безопасностью и производительностью. Всегда выбирайте подход, который лучше всего подходит для конкретной задачи.
FAQ: Ответы на частые вопросы
Можно ли использовать параметры в запросах к внешним источникам данных (HTTP-сервисы, веб-АПИ)?
Нет, механизм параметров УстановитьПараметр() работает только для запросов к базе данных 1С. Для внешних источников нужно формировать строку запроса вручную с экранированием или использовать специализированные библиотеки для работы с API.
Пример для HTTP-запроса:
Адрес = "https://api.example.com/data?param=" + ЭкранироватьСтрокуДляURL(Параметр);
Ответ = HTTPСоединение.Получить(Адрес);
Как передать в запрос параметр типа "Таблица значений"?
Прямая передача таблицы значений как параметра невозможна. Нужно использовать один из обходных путей:
- Преобразовать таблицу во временную таблицу базы данных.
- Выгрузить данные в массив и передать как параметр-коллекцию (для небольших наборов данных).
- Использовать объектную модель вместо запроса.
Пример с временной таблицей:
// 1. Создаем временную таблицу
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * В ТВ_Данные ИЗ &ТаблицаДанных КАК Данные";
Запрос.УстановитьПараметр("ТаблицаДанных", ИсходнаяТаблицаЗначений);
Запрос.Выполнить();
// 2. Используем её в основном запросе
Запрос.Текст = "ВЫБРАТЬ Товары.* ИЗ Справочник.Номенклатура КАК Товары
|ГДЕ Товары.Ссылка В (ВЫБРАТЬ Ссылка ИЗ ТВ_Данные)";
Почему запрос с параметром выполняется дольше, чем без него?
Это может происходить по нескольким причинам:
- 🔍 Сервер 1С не использует индексы из-за неоптимального условия с параметром (например,
НАЧИНАЕТСЯ(Поле, &Параметр)). - 📊 Параметр передается как коллекция большого размера, что приводит к полному сканированию.
- 🔄 В некоторых версиях платформы подготовка плана выполнения для параметризованного запроса занимает дополнительное время.
Решения:
- Проверьте план выполнения запроса.
- Замените функции над полями на эквивалентные конструкции (например,
ПОДОБНОвместоНАЧИНАЕТСЯ). - Для больших коллекций используйте временные таблицы.
Как передать NULL в качестве значения параметра?
Для передачи NULL используйте специальное значение Неопределено:
Запрос.УстановитьПараметр("Параметр", Неопределено);
В тексте запроса проверяйте на ЕСТЬ NULL:
ГДЕ (&Параметр ЕСТЬ NULL ИЛИ Поле = &Параметр)
Важно: В некоторых версиях платформы передача Неопределено может приводить к ошибкам. В этом случае используйте пустые значения соответствующего типа (например, пустую строку для строковых параметров).
Можно ли использовать параметры в пакетных запросах?
Да, в пакетных запросах (с использованием + для объединения текстов) параметры работают так же, как и в обычных. Главное правило — устанавливать параметры после формирования полного текста пакетного запроса, но до его выполнения:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ... ГДЕ Поле1 = &Параметр1;
|
ВЫБРАТЬ ... ГДЕ Поле2 = &Параметр2;";
Запрос.УстановитьПараметр("Параметр1", Значение1);
Запрос.УстановитьПараметр("Параметр2", Значение2);
Результат = Запрос.Выполнить();
Обратите внимание, что в пакетных запросах параметры не наследуются между отдельными запросами в пакете — каждый запрос должен иметь свои собственные параметры.