При написании сложных запросов в 1С вручную (без использования конструктора запросов) очень легко допустить досадные синтаксические ошибки, на которые платформа будет ругаться не всегда понятными сообщениями. Одной из таких распространенных ошибок является «Ожидается выражение ")"» или «Ожидается имя таблицы». В этой статье мы подробно разберем, почему возникают подобные ошибки, проанализируем реальный пример некорректного запроса и рассмотрим правильные методы оптимизации сложных запросов с использованием временных таблиц.
Давайте обратим внимание на фрагмент кода, который вызывал ошибку у автора темы. В тексте запроса присутствовало левое соединение со следующей конструкцией:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыПокупателей.Остатки(&ДатаОстатков,ЗП.ЗаказПокупателя.СкладГруппа В (&Параметр5) КАК ЗП
Проанализируем эту строку. Здесь используется обращение к виртуальной таблице РегистрНакопления.ЗаказыПокупателей.Остатки. У этой таблицы открывается круглая скобка для передачи параметров: (&ДатаОстатков, .... Однако закрывающая скобка для параметров виртуальной таблицы отсутствует. Вместо этого сразу за условием фильтрации идет объявление псевдонима КАК ЗП.
Из-за этого платформа 1С выдает ошибку синтаксиса в последующих строках (в секции ГДЕ), так как парсер запроса ожидает закрытия скобки виртуальной таблицы. Чтобы исправить эту ошибку, необходимо правильно расставить скобки:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыПокупателей.Остатки(&ДатаОстатков, ЗаказПокупателя.СкладГруппа В (&Параметр5)) КАК ЗП
Обратите внимание, что закрывающая скобка теперь стоит сразу после условия фильтрации и перед ключевым словом КАК.
Исправление скобки решит проблему с синтаксической ошибкой, но в данном запросе скрыто еще несколько серьезных проблем, которые могут привести к падению производительности или неверной работе логики.
1. Использование псевдонима таблицы внутри параметров виртуальной таблицы
В исходном запросе внутри параметров виртуальной таблицы остатков написано условие ЗП.ЗаказПокупателя.СкладГруппа. Псевдоним ЗП объявляется только в конце конструкции (после параметров). Использование псевдонима внутри круглых скобок самой виртуальной таблицы недопустимо и приведет к ошибке выполнения. Внутри параметров виртуальной таблицы следует обращаться к полям напрямую, без псевдонима:
ЗаказПокупателя.СкладГруппа В (&Параметр5)
2. Неоднозначность полей в условии соединения
Рассмотрим условие связи таблиц:
ПО ТНС.Номенклатура = Номенклатура И ТНС.ХарактеристикаНоменклатуры = ХарактеристикаНоменклатуры
В правой части равенства поля Номенклатура и ХарактеристикаНоменклатуры указаны без псевдонимов таблиц-источников. Так как эти поля присутствуют и в таблице ВН (Номенклатура), и в таблице ВХН (Характеристики), СУБД выдаст ошибку неоднозначности имени поля. Необходимо явно указать псевдонимы:
ПО ТНС.Номенклатура = ВН.Ссылка И ТНС.ХарактеристикаНоменклатуры = ВХН.Ссылка
3. Избыточное разыменование (обращение через точку)
В условии первого соединения написано: ПО ВХН.Владелец.Ссылка = ВН.Ссылка. Реквизит Владелец в справочнике ХарактеристикиНоменклатуры сам по себе является ссылочным типом, ссылающимся на справочник Номенклатура. Добавление конструкции .Ссылка к ссылочному полю избыточно и создает дополнительную нагрузку. Достаточно написать:
ПО ВХН.Владелец = ВН.Ссылка
4. Фильтрация в секции ГДЕ вместо параметров виртуальной таблицы
Во вложенном запросе к таблице ТоварыНаСкладах.ОстаткиИОбороты фильтр по складу наложен в секции ГДЕ:
ГДЕ ТоварыНаСкладахОстаткиИОбороты.Склад В (&Параметр5)
Это классическая ошибка, сильно снижающая производительность. При таком написании СУБД сначала выберет остатки и обороты по абсолютно всем складам в базе данных, а затем отфильтрует их. Условие по складу обязательно должно передаваться в качестве третьего параметра виртуальной таблицы:
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаНачала, &ДатаКонца, Регистратор, ДвиженияИГраницыПериода, Номенклатура = &Параметр6 И Склад В (&Параметр5)) КАК ТоварыНаСкладахОстаткиИОбороты
Для повышения читабельности, упрощения отладки и значительного ускорения работы запроса лучшим решением будет отказаться от вложенных запросов и объединения их через ПОЛНОЕ СОЕДИНЕНИЕ в одном пакете. Перепишем запрос по шагам с использованием временных таблиц.
Шаг 1. Получение остатков и оборотов товаров во временную таблицу
Сначала получим данные по товарам на складах, применив все необходимые фильтры в параметрах виртуальной таблицы:
ВЫБРАТЬ
ТоварыНаСкладах.Период КАК Период,
ТоварыНаСкладах.Склад КАК Склад,
ТоварыНаСкладах.Номенклатура КАК Номенклатура,
ТоварыНаСкладах.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
ТоварыНаСкладах.КоличествоНачальныйОстаток КАК КоличествоНачальныйОстаток,
ТоварыНаСкладах.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток,
ТоварыНаСкладах.КоличествоПриход КАК КоличествоПриход,
ТоварыНаСкладах.КоличествоРасход КАК КоличествоРасход
ПОМЕСТИТЬ ВТ_ТоварыНаСкладах
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаНачала, &ДатаКонца, Регистратор, ДвиженияИГраницыПериода, Номенклатура = &Параметр6 И Склад В (&Параметр5)) КАК ТоварыНаСкладах
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
ХарактеристикаНоменклатуры
;
Шаг 2. Получение остатков по заказам покупателей во временную таблицу
Аналогично выберем остатки заказов покупателей, предварительно ограничив выборку складами:
ВЫБРАТЬ
Заказы.Номенклатура КАК Номенклатура,
Заказы.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
Заказы.КоличествоОстаток КАК КоличествоОстаток
ПОМЕСТИТЬ ВТ_ЗаказыПокупателей
ИЗ
РегистрНакопления.ЗаказыПокупателей.Остатки(&ДатаОстатков, ЗаказПокупателя.СкладГруппа В (&Параметр5)) КАК Заказы
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
ХарактеристикаНоменклатуры
;
Шаг 3. Соединение данных в финальном запросе
Теперь мы можем легко и быстро соединить подготовленные временные таблицы со справочниками номенклатуры и характеристик:
ВЫБРАТЬ
ВН.Ссылка КАК Номенклатура,
ВХН.Ссылка КАК ХарактеристикаНоменклатуры,
ТНС.Период КАК Период,
ТНС.Склад КАК Склад,
ЕСТЬNULL(ТНС.КоличествоНачальныйОстаток, 0) КАК КоличествоНачальныйОстаток,
ЕСТЬNULL(ТНС.КоличествоКонечныйОстаток, 0) КАК КоличествоКонечныйОстаток,
ЕСТЬNULL(ТНС.КоличествоПриход, 0) КАК КоличествоПриход,
ЕСТЬNULL(ТНС.КоличествоРасход, 0) КАК КоличествоРасход,
ЕСТЬNULL(ЗП.КоличествоОстаток, 0) КАК ЗаказаноОстаток
ИЗ
Справочник.Номенклатура КАК ВН
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ХарактеристикиНоменклатуры КАК ВХН
ПО ВХН.Владелец = ВН.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ТоварыНаСкладах КАК ТНС
ПО ТНС.Номенклатура = ВН.Ссылка
И ТНС.ХарактеристикаНоменклатуры = ВХН.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ЗаказыПокупателей КАК ЗП
ПО ЗП.Номенклатура = ВН.Ссылка
И ЗП.ХарактеристикаНоменклатуры = ВХН.Ссылка
ГДЕ
ВН.Ссылка = &Параметр6
Использование предложенной структуры на основе временных таблиц не только избавит вас от проблем со скобками и логических ошибок соединения (таких как неверная интерпретация значений NULL при полном соединении), но и позволит оптимизатору СУБД построить максимально эффективный план выполнения запроса.