При написании сложных запросов в системе 1С:Предприятие программисты часто сталкиваются с необходимостью фильтрации данных не по одному, а по сочетанию нескольких полей. Если условие по одному полю кажется интуитивно понятным, то попытка использовать кортеж (группу полей) во вложенном запросе часто приводит к синтаксическим ошибкам. В этой статье мы разберем, как правильно оформлять такие условия, почему возникают ошибки и как сделать запрос максимально производительным.
Рассмотрим ситуацию: у нас есть временная таблица ВТ_Пользователи, и нам нужно выбрать из нее только те записи, у которых комбинация полей ФизическоеЛицо и Пользователь совпадает с записями в другой таблице — ВТ_Соответствие. Типичная ошибка новичка выглядит следующим образом:
ВЫБРАТЬ
ВТ_Пользователи.ФизическоеЛицо КАК ФизическоеЛицо,
ВТ_Пользователи.Пользователь КАК Пользователь
ИЗ
ВТ_Пользователи КАК ВТ_Пользователи
ГДЕ
ВТ_Пользователи.ФизическоеЛицо, ВТ_Пользователи.Пользователь В
(ВЫБРАТЬ
ВТ_Соответствие.ФизическоеЛицо,
ВТ_Соответствие.Пользователь
ИЗ
ВТ_Соответствие)
При попытке выполнить такой код система выдаст ошибку: «Синтаксическая ошибка ","». Давайте выясним причину. Дело в том, что в языке запросов 1С запятая вне скобок в секции ГДЕ воспринимается парсером как разделитель или окончание выражения. Чтобы система поняла, что мы сравниваем именно набор полей (кортеж), их обязательно нужно заключить в круглые скобки.
Правильный синтаксис будет выглядеть так:
ВЫБРАТЬ
ВТ_Пользователи.ФизическоеЛицо КАК ФизическоеЛицо,
ВТ_Пользователи.Пользователь КАК Пользователь
ИЗ
ВТ_Пользователи КАК ВТ_Пользователи
ГДЕ
(ВТ_Пользователи.ФизическоеЛицо, ВТ_Пользователи.Пользователь) В
(ВЫБРАТЬ
ВТ_Соответствие.ФизическоеЛицо,
ВТ_Соответствие.Пользователь
ИЗ
ВТ_Соответствие)
Теперь запрос отработает корректно. Важно помнить, что количество и порядок полей в левой части (перед оператором В) должны строго соответствовать количеству и порядку полей в списке выборки вложенного запроса.
Проанализируем ситуацию глубже. Хотя на уровне языка 1С мы пишем красивое условие с кортежами, СУБД воспринимают его по-разному. Это критично для понимания производительности вашей системы.
(Field1, Field2) IN (SELECT ...). Когда платформа 1С транслирует такой запрос, она превращает его в условие EXISTS. Внутри этого условия создается вложенный подзапрос, где поля связываются через логическое AND. Это превращается в достаточно «тяжелую» конструкцию, которая может замедлить выполнение, если данных много.Рассмотрим подробнее работу с неопределенными значениями. В языке SQL (и 1С не исключение) сравнение с NULL никогда не дает результат «Истина». Если в ваших таблицах ВТ_Пользователи или ВТ_Соответствие хотя бы в одном из полей кортежа встретится NULL, такая строка не попадет в результат запроса.
Даже если в обеих таблицах в поле ФизическоеЛицо стоит NULL, система посчитает, что они не равны. Если ваша логика предполагает сопоставление записей с пустыми значениями, вам придется предварительно обрабатывать их функцией ЕСТЬNULL(Поле, ЗначениеЗамены).
Несмотря на то, что использование оператора В по нескольким полям выглядит элегантно, эксперты 1С рекомендуют использовать альтернативный подход в высоконагруженных участках кода. Разберем по шагам наиболее производительный метод — использование ВНУТРЕННЕГО СОЕДИНЕНИЯ с временной таблицей.
Вместо одного сложного запроса с вложенным условием, мы проделаем следующие действия:
ВТ_Фильтр).ВТ_Фильтр через ВНУТРЕННЕЕ СОЕДИНЕНИЕ по нужным полям.Пример оптимизированного кода:
// 1. Создаем индексированную временную таблицу фильтра
ВЫБРАТЬ
ВТ_Соответствие.ФизическоеЛицо КАК ФизическоеЛицо,
ВТ_Соответствие.Пользователь КАК Пользователь
ПОМЕСТИТЬ ВТ_Фильтр
ИЗ
ВТ_Соответствие КАК ВТ_Соответствие
ИНДЕКСИРОВАТЬ ПО
ФизическоеЛицо,
Пользователь
;
// 2. Выполняем соединение
ВЫБРАТЬ
ВТ_Пользователи.ФизическоеЛицо КАК ФизическоеЛицо,
ВТ_Пользователи.Пользователь КАК Пользователь
ИЗ
ВТ_Пользователи КАК ВТ_Пользователи
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Фильтр КАК ВТ_Фильтр
ПО ВТ_Пользователи.ФизическоеЛицо = ВТ_Фильтр.ФизическоеЛицо
И ВТ_Пользователи.Пользователь = ВТ_Фильтр.Пользователь
Такой подход гарантирует предсказуемый план выполнения запроса на уровне СУБД. Использование Hash Match или Merge Join при наличии индексов отработает значительно быстрее, чем трансформированное условие EXISTS с вложенным подзапросом.
Выясним причину, почему индексация так важна. При сравнении нескольких полей СУБД эффективнее всего использует составные индексы. Если вы используете конструкцию со скобками в ГДЕ, СУБД может перейти к полному сканированию таблицы (Index Scan) вместо быстрого поиска (Index Seek), особенно если порядок полей в индексе не совпадает с порядком полей в вашем условии.
Также проанализируем типы данных. В 1С ФизическоеЛицо и Пользователь — это ссылочные типы. Платформа строго следит за совместимостью типов в кортежах. Если в левой части у вас поле типа СправочникСсылка.Сотрудники, а во вложенном запросе — СправочникСсылка.ФизическиеЛица, запрос не скомпилируется, даже если по смыслу это одно и то же. Всегда проверяйте соответствие типов данных в обеих частях условия.
Подводя итог, можно выделить основные правила работы с вложенными запросами по нескольким полям:
(Поле1, Поле2) В (...).Следуя этим рекомендациям, вы сможете писать не только синтаксически правильные, но и высокопроизводительные запросы, которые будут стабильно работать на любых СУБД.