При разработке обработок загрузки данных из внешних источников (например, из файлов Excel) начинающие разработчики часто сталкиваются с синтаксическими ошибками в тексте запроса. Одной из распространенных является ошибка вида «{(17, 2)}: Ожидается выражение "ВЫБРАТЬ"» при попытке связать загруженные данные со справочником в базе. Разберем причины возникновения этой ошибки, выясним типичные заблуждения при написании запросов и рассмотрим правильный пошаговый алгоритм сопоставления и обновления данных в 1С.
Проанализируем исходный код и выделим критические ошибки, которые приводят к сбою выполнения программы:
ЛЕВОЕ СОЕДИНЕНИЕ, ПОЛНОЕ СОЕДИНЕНИЕ, ВНУТРЕННЕЕ СОЕДИНЕНИЕ) возможно только между таблицами, а не отдельными полями или реквизитами. Кроме того, условие связи всегда задается ключевым словом ПО, а не конструкциями через точку или операторы И.КАК) не может содержать точку (например, конструкция вида КАК НоваяСтрока.ОписаниеТовара является недопустимой). Псевдоним должен быть простым идентификатором.; непосредственно перед секцией ЛЕВОЕ СОЕДИНЕНИЕ приводит к тому, что платформа воспринимает соединение как начало нового пакета и выдает ошибку ожидания ключевого слова ВЫБРАТЬ.ИЗ: Нельзя напрямую обращаться к реквизитам объекта или формы (например, Объект.ДанныеПоискаИзExcel) в тексте запроса. Табличную часть необходимо предварительно выгрузить в объект ТаблицаЗначений и передать в запрос через параметр.Выполнить() является функцией объекта Запрос и требует круглых скобок. Конструкция Запрос.Выполнить.Выгрузить вызывает синтаксическую ошибку времени выполнения. Корректный синтаксис: Запрос.Выполнить().Выгрузить() или Запрос.Выполнить().Выбрать().SELECT в SQL. Изменить или записать данные в базу напрямую из текста запроса невозможно — модификация реквизитов объектов выполняется через объектную модель после получения выборки.Рассмотрим, как правильно подготовить загруженные данные. Сначала выгрузим табличную часть в отдельную таблицу значений и передадим ее параметром в запрос. При необходимости очистим строковые данные от лишних пробелов.
ТаблицаДляЗапроса = Объект.ДанныеПоискаИзExcel.Выгрузить();
// Рекомендуется удалить лишние пробелы в полях поиска
Для Каждого Стр Из ТаблицаДляЗапроса Цикл
Стр.Артикул = СокрЛП(Стр.Артикул);
Стр.Производитель = СокрЛП(Стр.Производитель);
КонецЦикла;
Посмотрим на правильную структуру пакетного запроса. В первом пакете мы помещаем переданную таблицу во временную таблицу и индексируем поля, по которым будет выполняться сопоставление. Во втором пакете соединяем временную таблицу со справочником Номенклатура по полям Артикул и Производитель.
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| ВремТаб.Артикул КАК Артикул,
| ВремТаб.Производитель КАК Производитель,
| ВремТаб.СсылкаНаКартинку КАК СсылкаНаКартинку,
| ВремТаб.ОписаниеТовара КАК ОписаниеТовара
|ПОМЕСТИТЬ ВТ_ДанныеExcel
|ИЗ
| &ВремТаб КАК ВремТаб
|ИНДЕКСИРОВАТЬ ПО
| Артикул,
| Производитель
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СпрНоменклатура.Ссылка КАК СсылкаНоменклатура,
| ВТ_ДанныеExcel.СсылкаНаКартинку КАК СсылкаНаКартинку,
| ВТ_ДанныеExcel.ОписаниеТовара КАК ОписаниеТовара
|ИЗ
| ВТ_ДанныеExcel КАК ВТ_ДанныеExcel
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНоменклатура
| ПО ВТ_ДанныеExcel.Артикул = СпрНоменклатура.Артикул
| И ВТ_ДанныеExcel.Производитель = СпрНоменклатура.Производитель
|ГДЕ
| НЕ СпрНоменклатура.ПометкаУдаления";
Запрос.УстановитьПараметр("ВремТаб", ТаблицаДляЗапроса);
РезультатЗапроса = Запрос.Выполнить();
Обратим внимание: если требуется найти только те позиции, которые гарантированно есть в базе, используется ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Если же необходимо сохранить все строки загружаемой таблицы независимо от наличия номенклатуры в базе — применяется ЛЕВОЕ СОЕДИНЕНИЕ.
Разберем, как применить полученные результаты для изменения элементов справочника. Мы обходим выборку, получаем объект номенклатуры, заполняем реквизиты и записываем изменения. Для повышения производительности при больших объемах данных используем механизм транзакций.
Выборка = РезультатЗапроса.Выбрать();
Счетчик = 0;
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
Если ЗначениеЗаполнено(Выборка.СсылкаНоменклатура) Тогда
ОбъектНоменклатура = Выборка.СсылкаНоменклатура.ПолучитьОбъект();
Модифицирован = Ложь;
Если ОбъектНоменклатура.Описание <> Выборка.ОписаниеТовара Тогда
ОбъектНоменклатура.Описание = Выборка.ОписаниеТовара;
Модифицирован = Истина;
КонецЕсли;
Если ОбъектНоменклатура._ПутьКартинкамНаСайте <> Выборка.СсылкаНаКартинку Тогда
ОбъектНоменклатура._ПутьКартинкамНаСайте = Выборка.СсылкаНаКартинку;
Модифицирован = Истина;
КонецЕсли;
Если Модифицирован Тогда
ОбъектНоменклатура.Записать();
Счетчик = Счетчик + 1;
КонецЕсли;
// Фиксация порциями для снижения нагрузки на блокировки
Если Счетчик % 200 = 0 Тогда
ЗафиксироватьТранзакцию();
НачатьТранзакцию();
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Чтобы избежать подобных ошибок в дальнейшем, рекомендуем придерживаться следующих правил:
ПО), группировках и сортировках без явного приведения через ВЫРАЗИТЬ(Поле КАК Строка(1000)).Записать(). Это существенно снижает нагрузку на базу данных и ускоряет работу обработки.