При написании сложных запросов в 1С, особенно когда они собираются динамически в коде модуля, программисты часто сталкиваются с синтаксическими ошибками. Одной из самых коварных является ошибка «Ожидается выражение ")"» или «Синтаксическая ошибка ")"». Давайте разберем по шагам, почему система «ругается» на скобки, как правильно использовать вложенные запросы и на что обратить внимание при объединении нескольких таблиц через ОБЪЕДИНИТЬ ВСЕ.
Разберем ситуацию, представленную в первом сообщении темы. Автор пытается назначить псевдоним сразу после условия в секции ГДЕ. Проанализируем ошибочный фрагмент кода:
| ГДЕ | КорректировкаЗаказаПокупателя.Ссылка.ЗаказПокупателя = &ТекущийДокумент
| И (КорректировкаЗаказаПокупателя.Ссылка.Проведен = Истина) КАК ВложенныйЗапрос
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК Штрихкоды
Выясним причину ошибки: в языке запросов 1С ключевое слово КАК (назначение псевдонима) может относиться либо к полю в списке выбора, либо к источнику данных в секции ИЗ. Псевдоним нельзя присваивать логическому выражению внутри ГДЕ. Система ожидает, что после закрывающей скобки в условии пойдет продолжение логики (например, И или ИЛИ), а встречает определение имени таблицы.
Как исправить: Псевдоним вложенного запроса должен стоять строго после всей конструкции подзапроса. Рассмотрим правильную структуру:
ВЫБРАТЬ ...
ИЗ (
ВЫБРАТЬ ...
ИЗ Документ.ЗаказПокупателя
ГДЕ Ссылка = &Параметр
) КАК ВложенныйЗапрос // Псевдоним назначается ВСЕМУ результату подзапроса
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ...
Рассмотрим ситуацию, когда мы используем оператор ОБЪЕДИНИТЬ ВСЕ. Часто требуется собрать данные из разных табличных частей: «Товары», «Услуги», «Возвратная тара». При добавлении нового поля (например, Штрихкод) только в одну из частей, запрос выдаст ошибку выполнения.
Проанализируем правила объединения:
ОБЪЕДИНИТЬ должно быть абсолютно одинаковое количество колонок.Число, то и во всех остальных частях она должна быть числовой.Если нам нужно добавить штрихкод из соединения только для товаров, а для услуг его нет, мы должны вставить «заглушку» в секции услуг. Посмотрим на пример:
// Часть 1: Товары
ВЫБРАТЬ
Товары.Номенклатура,
Штрихкоды.Штрихкод КАК Штрихкод
ИЗ
Документ.ЗаказПокупателя.Товары КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК Штрихкоды ПО ...
ОБЪЕДИНИТЬ ВСЕ
// Часть 2: Услуги (здесь штрихкодов нет, ставим NULL или пустую строку)
ВЫБРАТЬ
Услуги.Номенклатура,
NULL // Это поле-заглушка для соблюдения структуры
ИЗ
Документ.ЗаказПокупателя.Услуги КАК Услуги
Проанализируем способ соединения, который выбрал автор темы: ПО ВложенныйЗапрос.Номенклатура.Код = Штрихкоды.Владелец.Код. Это решение содержит две серьезные проблемы производительности и логики:
1. Соединение по коду. Сравнение строк (кодов) в SQL работает значительно медленнее, чем сравнение ссылок (GUID). Кроме того, обращение Номенклатура.Код заставляет систему выполнять неявное соединение с таблицей справочника «Номенклатура», что создает лишнюю нагрузку.
Правильный подход: Соединяйте таблицы напрямую по ссылкам: ПО ВложенныйЗапрос.Номенклатура = Штрихкоды.Владелец.
2. Внутреннее соединение (INNER JOIN). Использование ВНУТРЕННЕЕ СОЕДИНЕНИЕ приведет к тому, что если у товара в базе не задан штрихкод, этот товар вообще исчезнет из результата запроса. Для печатных форм и отчетов документов это критическая ошибка.
Правильный подход: Используйте ЛЕВОЕ СОЕДИНЕНИЕ. Это позволит вывести все позиции документа, а поле штрихкода заполнить только там, где данные действительно есть.
Когда мы собираем ТекстЗапроса через конкатенацию строк (+) и условия (Если...Тогда), легко потерять закрывающую скобку вложенного запроса. Рассмотрим, как структурировать код, чтобы избежать путаницы.
Рассмотрим пример построения надежного запроса:
Запрос = Новый Запрос;
Текст =
"ВЫБРАТЬ
| Вложенный.Номенклатура,
| СУММА(Вложенный.Количество) КАК Кол
|ИЗ (
| ВЫБРАТЬ Таб.Номенклатура, Таб.Количество ИЗ Документ.Заказ.Товары КАК Таб
| ГДЕ Таб.Ссылка = &Док";
// Динамически добавляем части через ОБЪЕДИНИТЬ
Если ДобавлятьУслуги Тогда
Текст = Текст + "
| ОБЪЕДИНИТЬ ВСЕ
| ВЫБРАТЬ Усл.Номенклатура, Усл.Количество ИЗ Документ.Заказ.Услуги КАК Усл
| ГДЕ Усл.Ссылка = &Док";
КонецЕсли;
// ЗАКРЫВАЕМ вложенный запрос и назначаем псевдоним
Текст = Текст + "
| ) КАК Вложенный
| СГРУППИРОВАТЬ ПО Вложенный.Номенклатура";
Важный нюанс: Обратите внимание на пробелы и переносы строк при склейке. Часто ошибка возникает из-за того, что конец одной строки сливается с началом другой, образуя нечитаемое для парсера 1С слово, например ИСТИНАВНУТРЕННЕЕ СОЕДИНЕНИЕ.
Когда во вложенном запросе мы объединяем данные, а во внешнем — соединяем их с регистром сведений (штрихкодами), важно помнить о кратности. У одного товара может быть несколько штрихкодов. В этом случае обычное соединение приведет к дублированию строк и, как следствие, к удвоению сумм и количеств при группировке.
Чтобы этого избежать, выполним одно из следующих действий:
МАКСИМУМ).Если ваш запрос выдает ошибку на скобку или количество полей, проверьте его по этому списку:
ОБЪЕДИНИТЬ ВСЕ имеют одинаковое количество колонок? (Добавьте NULL там, где данных не хватает).КАК ВложенныйЗапрос сразу после закрывающей скобки блока ИЗ (...)?ГДЕ, которые не имеют пары?" |").Номенклатура = Владелец вместо сравнения кодов)?Соблюдение этих структурных правил позволит вам создавать сложные и производительные механизмы выборки данных в 1С без досадных синтаксических ошибок.