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