При работе с запросами в платформе «1С:Предприятие 8» разработчики часто сталкиваются с необходимостью связать несколько таблиц с помощью левого соединения (ЛЕВОЕ СОЕДИНЕНИЕ) и проверить, заполнены ли данные в присоединенной таблице. Особую сложность вызывают поля со строками неограниченной длины (например, адреса, ссылки на внешние ресурсы или комментарии). Если просто попытаться сравнить такое поле с пустой строкой в секции ВЫБОР, СУБД вернет ошибку или запрос отработает некорректно.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем типичные ошибки разработчиков и рассмотрим три различных решения: от исправления исходного кода до максимально эффективного и быстрого алгоритма с точки зрения производительности СУБД.
Давайте проанализируем ситуацию, с которой столкнулась автор темы. Нам необходимо связать документ с таблицей присоединенных файлов Google Drive и определить, прикреплен ли файл к документу. Поле СсылкаНаФайл имеет тип «Строка неограниченной длины».
Изначально разработчик пытался использовать следующую конструкцию в запросе:
ВЫБОР
КОГДА (ВЫРАЗИТЬ(Кос_ФайлыGDrive.СсылкаНаФайл КАК СТРОКА(1000))) = ""
ТОГДА 0
КОГДА (ВЫРАЗИТЬ(Кос_ФайлыGDrive.СсылкаНаФайл КАК СТРОКА(1000))) <> ""
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ КАК Кос_GDrive
Этот код содержит критическую логическую ошибку, из-за которой запрос работает не так, как ожидалось. Дело в механизме работы операции ЛЕВОЕ СОЕДИНЕНИЕ. Если для документа в присоединенной таблице файлов не будет найдено соответствие, то СУБД во всех полях присоединенной таблицы вернет значение NULL.
Когда СУБД пытается выполнить конструкцию ВЫРАЗИТЬ(NULL КАК СТРОКА(1000)), результатом также становится значение NULL. В языке SQL любое сравнение значения NULL с чем-либо (даже проверка вида NULL = "" или NULL <> "") всегда возвращает ложь (точнее, логическое значение UNKNOWN). В результате выполнение запроса всегда уходит в ветку ИНАЧЕ 0, из-за чего система считает, что файла нет, даже если запись просто отсутствовала в соединении.
Чтобы заставить эту логику работать, нам нужно явно обработать значение NULL, заменив его на пустую строку до выполнения сравнения. Для этого используется функция ЕСТЬNULL (в англоязычном варианте — ISNULL).
Разберем по шагам правильное построение этой функции:
ЕСТЬNULL(Кос_ФайлыGDrive.СсылкаНаФайл, ""). Если поле содержит NULL, СУБД подставит вместо него пустую строку."" в блоке КОГДА. Нам обязательно нужно использовать оператор приведения типов ВЫРАЗИТЬ.Посмотрим на пример правильного кода, к которому пришли участники обсуждения:
ВЫБОР
КОГДА ВЫРАЗИТЬ(ЕСТЬNULL(Кос_ФайлыGDrive.СсылкаНаФайл, "") КАК СТРОКА(1)) = ""
ТОГДА 0
ИНАЧЕ 1
КОНЕЦ КАК Кос_GDrive
Этот вариант полностью работоспособен. Сначала функция ЕСТЬNULL вернет либо реальную строку ссылки, либо пустую строку. Затем оператор ВЫРАЗИТЬ(... КАК СТРОКА(1)) усечет результат до 1 символа, превратив неограниченную строку в строку ограниченной длины. После этого сравнение с "" отработает корректно на уровне СУБД.
Хотя первое решение является рабочим, с точки зрения стандартов разработки и оптимизации запросов 1С оно далеко не самое лучшее. В сообщении форума автор пишет важную фразу: «Мне нужно — есть он или нет, сама ссылка не нужна».
Если нам нужно только проверить сам факт физического наличия записи в присоединенной таблице, то нам вообще не требуется обращаться к тяжелым текстовым полям неограниченной длины! Считывание полей неограниченной длины (блоков данных типа BLOB в СУБД) — это крайне ресурсоемкая операция.
Вместо этого мы можем проверить на NULL ключевое поле присоединенной таблицы — её уникальный идентификатор (Ссылка). Если запись в таблице файлов существует, её Ссылка гарантированно заполнена. Если записи нет — Ссылка будет равна NULL.
Рассмотрим, как будет выглядеть этот лаконичный и быстрый запрос:
ВЫБОР
КОГДА Кос_ФайлыGDrive.Ссылка ЕСТЬ NULL
ТОГДА 0
ИНАЧЕ 1
КОНЕЦ КАК Кос_GDrive
Почему этот вариант гораздо лучше:
ВЫРАЗИТЬ(ЕСТЬNULL(...)), код становится понятным для любого разработчика, который будет поддерживать ваше решение в будущем.В профессиональной разработке на платформе 1С принято возвращать логический тип (ИСТИНА или ЛОЖЬ), если поле отвечает на вопрос «Да/Нет» или «Есть/Нет». Использование чисел 1 и 0 усложняет дальнейшую работу с результатом запроса в коде конфигурации.
Давайте перепишем наш оптимизированный запрос с использованием правильного типа данных:
ВЫБОР
КОГДА Кос_ФайлыGDrive.Ссылка ЕСТЬ NULL
ТОГДА ЛОЖЬ
ИНАЧЕ ИСТИНА
КОНЕЦ КАК Кос_GDrive
Теперь при обходе результата запроса во встроенном языке 1С вы сможете писать красивый, лаконичный код без лишних сравнений:
Пока Выборка.Следующий() Цикл
Если Выборка.Кос_GDrive Тогда
// Файл прикреплен, выполняем логику
КонецЕсли;
КонецЦикла;
Если в других задачах вам все же потребуется преобразовывать строки неограниченной длины к ограниченному типу для вывода в отчеты или дальнейшей обработки, помните о важных правилах платформы 1С:
ВЫРАЗИТЬ(Поле КАК СТРОКА(Х)), составляет 1024 символа. Попытка указать длину больше (например, 1500) приведет к ошибке синтаксиса еще на этапе сохранения текста запроса.ПОДСТРОКА(Поле, 1, НужнаяДлина). Однако помните, что функция ВЫРАЗИТЬ более предпочтительна для длин до 1024 символов, так как оптимизатор СУБД строит под нее более эффективные планы запросов.ЕСТЬNULL всегда определяет тип возвращаемого значения по своему первому аргументу. Если первый аргумент — это неограниченная строка, то даже конструкция ЕСТЬNULL(Поле, "") вернет тип «Строка неограниченной длины». Именно поэтому ее дальнейшее приведение через ВЫРАЗИТЬ в сложных условиях сравнения остается обязательным.