При разработке сложных отчетов или обработок в системе 1С:Предприятие 8 программисты часто сталкиваются с необходимостью передать результат одного запроса во второй для фильтрации данных. Одной из самых распространенных ошибок начинающих разработчиков является попытка обратиться к колонке временной таблицы напрямую в условии ГДЕ, как если бы это был массив или параметр. В этой статье мы подробно разберем, почему возникает такая ошибка, и рассмотрим профессиональные способы решения этой задачи.
Рассмотрим ситуацию, с которой столкнулся автор темы. В коде была попытка сделать следующее:
ВЫБРАТЬ
Договор.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Договор
ИЗ
Справочник.ДоговорыКонтрагентов КАК Договор
ГДЕ
Договор.Владелец В (ВТ_ФизЛица.ПлательщикУченика);
При попытке выполнить такой запрос система выдаст ошибку "Поле не найдено". Давайте выясним причину. В языке запросов 1С конструкция В (...) ожидает внутри либо список значений (переданный через параметр-массив), либо вложенный запрос. Временная таблица — это физическая таблица в базе данных (хранящаяся в TempDB сервера SQL). Прямое обращение к ней через точку в условии фильтрации синтаксически недопустимо, так как движок запросов не понимает, из какой именно строки временной таблицы нужно взять значение.
Рассмотрим по шагам, как можно реализовать этот отбор правильно.
Самый простой и логичный способ — превратить обращение к временной таблице в полноценный подзапрос. Это позволяет системе однозначно понять, какие данные мы хотим получить из ВТ_ФизЛица.
Проанализируем исправленный код:
ВЫБРАТЬ
Договор.Ссылка КАК Ссылка,
Договор.Владелец КАК Родитель
ПОМЕСТИТЬ ВТ_Договор
ИЗ
Справочник.ДоговорыКонтрагентов КАК Договор
ГДЕ
Договор.Владелец В (ВЫБРАТЬ ВТ_ФизЛица.ПлательщикУченика ИЗ ВТ_ФизЛица КАК ВТ_ФизЛица)
Преимущества этого метода:
ВТ_ФизЛица одно и то же лицо встречается несколько раз, основной запрос не "размножит" строки. Он просто проверит наличие совпадения.Второй способ — это использование ЛЕВОГО СОЕДИНЕНИЯ или ВНУТРЕННЕГО СОЕДИНЕНИЯ. Как отмечают опытные разработчики, для оптимизатора СУБД соединение часто является более предпочтительным и предсказуемым вариантом, чем подзапрос в условии ГДЕ.
Давайте разберем пример использования соединения:
ВЫБРАТЬ
Договор.Ссылка КАК Ссылка,
ВТ_ФизЛица.ПлательщикУченика КАК Родитель
ПОМЕСТИТЬ ВТ_Договор
ИЗ
ВТ_ФизЛица КАК ВТ_ФизЛица
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ДоговорыКонтрагентов КАК Договор
ПО ВТ_ФизЛица.ПлательщикУченика = Договор.Владелец
Важный нюанс: если вам нужно сохранить всех учеников, даже тех, у кого нет договора, используйте ЛЕВОЕ СОЕДИНЕНИЕ. Если же нужны только те, по кому найдены договоры — используйте ВНУТРЕННЕЕ СОЕДИНЕНИЕ или дополнительный фильтр ГДЕ НЕ Договор.Ссылка ЕСТЬ NULL.
Разберем ситуацию более глубоко. В сообщениях форума возник спор: что быстрее? С одной стороны, подзапрос В (ВЫБРАТЬ...) кажется фильтром, а соединение — полной обработкой. На самом деле, современные СУБД (SQL Server, PostgreSQL) часто преобразуют эти конструкции в идентичные планы выполнения.
Однако есть критические различия:
ВТ_ФизЛица есть дубли, соединение увеличит количество строк в результате. Для фильтрации это может быть критично и привести к неверным итогам в отчете. Чтобы этого избежать, используйте ВЫБРАТЬ РАЗЛИЧНЫЕ при формировании временной таблицы.В (...) лишен проблемы дублей по своей сути (он работает по логике "найдено/не найдено").О чем часто забывают при работе с временными таблицами — это производительность на больших объемах данных. Если в вашей таблице ВТ_ФизЛица более 100-200 записей, обязательно индексируйте поле связи. Без индекса СУБД будет выполнять полное сканирование таблицы (Table Scan) для каждой проверки условия, что катастрофически замедлит работу системы.
Рассмотрим, как правильно добавить индекс:
ВЫБРАТЬ
ФизическиеЛица.ПлательщикУченика КАК ПлательщикУченика,
ФизическиеЛица.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ФизЛица
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица
ГДЕ
ФизическиеЛица.Ссылка В (&МассивУчеников)
ИНДЕКСИРОВАТЬ
ПлательщикУченика
;
Добавление ключевого слова ИНДЕКСИРОВАТЬ превратит вашу временную таблицу в эффективный инструмент, с которым соединение будет работать мгновенно.
Проанализируем еще одну тонкость. Если временная таблица ВТ_ФизЛица окажется пустой (например, по заданным параметрам не нашлось ни одного ученика), то условие ГДЕ Договор.Владелец В (ВЫБРАТЬ...) приведет к тому, что весь итоговый запрос вернет пустой результат. В некоторых бизнес-сценариях это может быть нежелательно. Если вам нужно видеть "всех, но с пометкой о договоре", используйте только ЛЕВОЕ СОЕДИНЕНИЕ от основной таблицы к договорам.
Подведем итог нашему разбору. Для того чтобы использовать данные из временной таблицы в качестве фильтра, придерживайтесь следующих правил:
ГДЕ.В (ВЫБРАТЬ ИмяПоля ИЗ ИмяТаблицы), если вам нужна простая фильтрация и вы хотите избежать дублирования строк.СОЕДИНЕНИЕ, если вам нужно вытащить дополнительные данные из связанных таблиц.В.ЛЕВОЕ СОЕДИНЕНИЕ и хотите отсечь пустые значения, не забывайте про проверку ЕСТЬ NULL.Применяя эти подходы, вы обеспечите не только правильность работы ваших запросов в 1С, но и их высокую производительность даже на крупных информационных базах.