При написании запросов в системе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда созданное в секции ВЫБРАТЬ поле (например, расчет возраста или суммы) невозможно использовать в секции ГДЕ. При попытке выполнить такой запрос система выдает ошибку «Поле не найдено», даже если в конструкторе запросов псевдоним задан верно.
В этой статье мы подробно разберем, почему возникает эта ошибка, как работает механизм исполнения запросов и какие существуют способы решения — от простого дублирования кода до профессиональной оптимизации производительности.
Давайте проанализируем, как СУБД (будь то MS SQL Server или PostgreSQL) обрабатывает наш запрос. Проблема заключается в том, что операторы запроса выполняются не в том порядке, в котором они написаны в тексте. Рассмотрим упрощенную схему порядка выполнения:
Как мы видим, секция ГДЕ выполняется гораздо раньше, чем секция ВЫБРАТЬ. На этапе фильтрации строк СУБД еще «не знает», что вы назвали выражение (ГОД(&Сегодня) - ГОД(ДатаРождения)) словом Возраст. Именно поэтому попытка обратиться к полю Возраст в условии приводит к ошибке «Поле не найдено».
Самый простой и очевидный способ заставить запрос работать — это повторить всё вычисляемое выражение непосредственно в секции ГДЕ. Разберем на примере автора темы:
Запрос.Текст = "ВЫБРАТЬ
| ФизическиеЛица.Наименование КАК Наименование,
| ФизическиеЛица.ДатаРождения,
| (ГОД(&Сегодня) - ГОД(ФизическиеЛица.ДатаРождения)) КАК Возраст
|ИЗ
| Справочник.ФизическиеЛица КАК ФизическиеЛица
|ГДЕ
| НЕ ФизическиеЛица.ЭтоГруппа
| И (ГОД(&Сегодня) - ГОД(ФизическиеЛица.ДатаРождения)) < 55";
Обратите внимание на количество скобок: часто ошибка возникает из-за их нехватки при написании сложных математических формул внутри запроса. Этот метод рабочий, но он делает текст запроса громоздким и трудночитаемым, если формула сложная.
Для работы с датами в 1С предусмотрена специальная функция РАЗНОСТЬДАТ. Она позволяет вычислить разницу между двумя датами в различных периодах (день, месяц, квартал, год). Посмотрим на пример использования:
ГДЕ
РАЗНОСТЬДАТ(ФизическиеЛица.ДатаРождения, &Сегодня, ГОД) < 55
Важный нюанс: функция РАЗНОСТЬДАТ работает по принципу количества пересечений границ периодов. Если человек родился 31 декабря 2022 года, а мы сравниваем с 1 января 2023 года, функция вернет 1 год разницы, хотя фактически прошел всего один день. Для точного расчета возраста до дня этот метод может потребовать дополнительных проверок.
Рассмотрим ситуацию с точки зрения производительности. Если мы используем функции типа ГОД() или РАЗНОСТЬДАТ() в условии ГДЕ, СУБД не может использовать индексы по полю ДатаРождения. Ей приходится прочитать каждую запись справочника, вычислить функцию и только потом сравнить результат. Если в базе миллионы физлиц, запрос будет очень медленным.
Проанализируем лучший способ решения — расчет граничной даты на языке 1С перед выполнением запроса:
// В коде 1С вычисляем дату, позже которой должны были родиться люди
ДатаПорога = ДобавитьМесяц(ТекущаяДата(), -55 * 12);
Запрос.УстановитьПараметр("ДатаПорога", ДатаПорога);
Запрос.Текст = "ВЫБРАТЬ
| ФизическиеЛица.Наименование,
| ФизическиеЛица.ДатаРождения
|ИЗ
| Справочник.ФизическиеЛица КАК ФизическиеЛица
|ГДЕ
| ФизическиеЛица.ДатаРождения > &ДатаПорога";
Этот подход называется обеспечением SARGability (Search Argumentable). В этом случае запрос отработает мгновенно, так как система просто обратится к индексу по полю ДатаРождения.
Если вам все же необходимо использовать именно псевдоним поля (например, если расчет крайне сложный и используется в нескольких местах), можно воспользоваться вложенным запросом. Во внутреннем запросе мы производим вычисление, а во внешнем — обращаемся к результату по имени.
ВЫБРАТЬ
ВТ_Данные.Наименование,
ВТ_Данные.Возраст
ИЗ
(ВЫБРАТЬ
ФизическиеЛица.Наименование КАК Наименование,
(ГОД(&Сегодня) - ГОД(ФизическиеЛица.ДатаРождения)) КАК Возраст
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица) КАК ВТ_Данные
ГДЕ
ВТ_Данные.Возраст < 55
Этот метод помогает сохранить «чистоту» кода и избежать дублирования длинных формул, хотя и не решает проблему использования индексов так эффективно, как использование заранее подготовленных параметров.
Если вы разрабатываете отчет на СКД, ситуация упрощается. Вы можете добавить вычисляемое поле на специальной вкладке «Вычисляемые поля». В этом случае СКД сама корректно преобразует ваш отбор в SQL-запрос, и вы сможете накладывать условия на Возраст в пользовательских настройках так же легко, как на любое другое поле.
Подводя итог, при возникновении ошибки «Поле не найдено» в запросе, всегда проверяйте, не пытаетесь ли вы использовать псевдоним в секции ГДЕ. Помните о порядке выполнения операторов и старайтесь выносить расчеты в параметры запроса для ускорения работы системы.