В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда, казалось бы, простой и логичный запрос начинает возвращать избыточные данные, полностью игнорируя установленные фильтры. Особенно это заметно при работе с временными таблицами и вложенными конструкциями В (ВЫБРАТЬ ...). В данной статье мы разберем классическую ошибку, связанную с использованием коррелированных подзапросов, и выясним, почему система может "забыть" про условие по дате или организации.
Рассмотрим пример запроса, который вызвал затруднения. Задача была простой: выбрать первый попавшийся документ за определенный период, поместить его во временную таблицу, а затем получить данные его табличной части.
Исходный (проблемный) код выглядел так:
// Пакет №1: Выбираем один документ в интервале дат
ВЫБРАТЬ ПЕРВЫЕ 1
Таблица.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Шапка
ИЗ
Документ.ТабельУчетаРабочегоВремени КАК Таблица
ГДЕ
Таблица.Дата >= &Дата1 И Таблица.Дата <= &Дата2;
////////////////////////////////////////////////////////////////////////////////
// Пакет №2: Пытаемся получить ТЧ только этого документа
ВЫБРАТЬ
ТаблицаТЧ.Сотрудник,
ТаблицаТЧ.Дни
ИЗ
Документ.ТабельУчетаРабочегоВремени.РабочееВремя КАК ТаблицаТЧ
ГДЕ
ТаблицаТЧ.Ссылка В (ВЫБРАТЬ ТаблицаТЧ.Ссылка ИЗ ВТ_Шапка) // ОШИБКА ЗДЕСЬ
Проблема заключалась в том, что во второй пакет попадали записи документов за все года, хотя в ВТ_Шапка лежал только один документ за 2024 год. На первый взгляд, фильтр ГДЕ ТаблицаТЧ.Ссылка В (...) должен был отсечь лишнее, но он этого не делал.
Давайте проанализируем, что происходит внутри конструкции (ВЫБРАТЬ ТаблицаТЧ.Ссылка ИЗ ВТ_Шапка). В языке запросов 1С (как и в стандарте SQL) существует понятие области видимости. Подзапрос имеет доступ не только к тем таблицам, которые указаны в его собственной секции ИЗ, но и к полям таблиц внешнего запроса.
Посмотрим на логику выполнения системы по шагам:
Документ.ТабельУчетаРабочегоВремени.РабочееВремя (псевдоним ТаблицаТЧ).ВЫБРАТЬ ТаблицаТЧ.Ссылка ИЗ ВТ_Шапка.ТаблицаТЧ.Ссылка. В таблице ВТ_Шапка такого поля нет (там есть просто Ссылка).ТаблицаТЧ во внешнем запросе.ВТ_Шапка не пуста, он всегда возвращает ссылку текущей строки, которую мы сейчас обрабатываем.Таким образом, условие превращается в ГДЕ ТаблицаТЧ.Ссылка = ТаблицаТЧ.Ссылка. Это выражение всегда истинно (True), если во временной таблице есть хотя бы одна любая запись. Именно поэтому фильтр перестает работать, и мы получаем все документы из базы данных.
Для того чтобы запрос отработал корректно, необходимо явно указать, что мы хотим выбирать данные именно из временной таблицы, используя её псевдоним или обращаясь к её полям напрямую. Рассмотрим правильный вариант написания второго пакета:
ВЫБРАТЬ
ТаблицаТЧ.Сотрудник,
ТаблицаТЧ.Дни
ИЗ
Документ.ТабельУчетаРабочегоВремени.РабочееВремя КАК ТаблицаТЧ
ГДЕ
ТаблицаТЧ.Ссылка В (ВЫБРАТЬ ВТ_Шапка.Ссылка ИЗ ВТ_Шапка КАК ВТ_Шапка)
Здесь мы четко указали, что поле Ссылка берется из ВТ_Шапка. Теперь условие будет работать как ожидалось: сравнивать ссылку из основной таблицы со списком ссылок, хранящихся во временной таблице.
Проанализируем еще один важный момент в первом пакете запроса автора: ВЫБРАТЬ ПЕРВЫЕ 1 Таблица.Ссылка ... без использования УПОРЯДОЧИТЬ ПО.
Важно помнить: В реляционных базах данных (MS SQL, PostgreSQL) порядок строк в таблице не гарантирован. Без явной сортировки СУБД вернет "первую попавшуюся" запись, которая может быть разной при каждом выполнении запроса.
Если ваша задача — получить именно последний документ или документ с конкретным номером, обязательно добавляйте УПОРЯДОЧИТЬ ПО. Например:
ВЫБРАТЬ ПЕРВЫЕ 1
Таблица.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Шапка
ИЗ
Документ.ТабельУчетаРабочегоВремени КАК Таблица
ГДЕ
Таблица.Дата >= &Дата1 И Таблица.Дата <= &Дата2
УПОРЯДОЧИТЬ ПО
Таблица.Дата УБЫВ,
Таблица.Ссылка УБЫВ
Хотя использование оператора В (...) допустимо, профессиональным стандартом в разработке на 1С считается использование ВНУТРЕННЕГО СОЕДИНЕНИЯ (INNER JOIN). Это не только исключает риск создания случайного коррелированного подзапроса, но и зачастую работает быстрее на больших объемах данных, так как оптимизатор СУБД лучше справляется с соединениями таблиц.
Перепишем наш запрос в оптимальном виде:
ВЫБРАТЬ
ТаблицаТЧ.Сотрудник,
ТаблицаТЧ.Дни
ИЗ
ВТ_Шапка КАК Шапка
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ТабельУчетаРабочегоВремени.РабочееВремя КАК ТаблицаТЧ
ПО Шапка.Ссылка = ТаблицаТЧ.Ссылка
Такой подход делает структуру запроса прозрачной: мы сразу видим, какие таблицы соединяются и по каким полям. Ошибиться с контекстом видимости полей здесь практически невозможно.
Для того чтобы избежать подобных логических ловушек в будущем, рекомендуем придерживаться следующих правил:
Таблица или Т. Используйте осмысленные имена, например, ТЧ_РабочееВремя или ВТ_ВыбранныеДокументы.ИмяТаблицы.ИмяПоля даже внутри вложенных запросов. Это защитит вас от "проброса" полей из внешнего контекста.Подводя итог, отметим, что "магия" неработающего фильтра ГДЕ чаще всего объясняется именно тем, что разработчик неявно создал коррелированный подзапрос. Понимание механизмов видимости имен в языке запросов 1С — это ключ к созданию надежных и производительных отчетов и механизмов проведения документов.