ВЫБОР, ЕСТЬNULL и приведение типов
Три инструмента, которые превращают сырой результат запроса в аккуратные данные: условие прямо в запросе, замена пустот и приведение ссылочных типов.
ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ — это условное выражение внутри запроса: оно вычисляет разные значения поля в зависимости от условия, не выходя в код на встроенном языке.
Часто данные из базы нужно не просто достать, а слегка «причесать» прямо в запросе: подставить текст вместо кода, заменить пустое значение нулём, привести поле составного типа к нужной ссылке. Делать это в цикле после запроса — медленно и многословно. Язык запросов 1С умеет всё перечисленное сам. Разберём три ключевые конструкции: ВЫБОР, ЕСТЬNULL и ВЫРАЗИТЬ.
Зачем это нужно на практике
Пример из жизни: отчёт по заказам, где нужна колонка «Статус оплаты» — «Оплачен», «Частично» или «Не оплачен» в зависимости от суммы оплаты. Или ведомость остатков, где по товарам без движений вместо пустоты должен стоять честный ноль. Или журнал, где в одном поле лежат ссылки разных типов, а нам нужен реквизит, который есть только у одного из них. Все три задачи решаются выражениями в запросе — быстро и в одном месте, без постобработки в коде.
ВЫБОР: условие внутри запроса
Конструкция ВЫБОР КОГДА условие ТОГДА значение ИНАЧЕ значение КОНЕЦ — это аналог тернарного оператора, только внутри запроса. Условий КОГДА может быть несколько, они проверяются сверху вниз; сработает первое истинное, а если ни одно не подошло — вернётся ветка ИНАЧЕ. Сделаем колонку статуса оплаты:
ВЫБРАТЬ
Заказы.Ссылка КАК Заказ,
Заказы.СуммаДокумента КАК Сумма,
Заказы.Оплачено КАК Оплачено,
ВЫБОР
КОГДА Заказы.Оплачено >= Заказы.СуммаДокумента ТОГДА "Оплачен"
КОГДА Заказы.Оплачено > 0 ТОГДА "Частично"
ИНАЧЕ "Не оплачен"
КОНЕЦ КАК СтатусОплаты
ИЗ
Документ.ЗаказПокупателя КАК Заказы
Результат:
Заказ Сумма Оплачено СтатусОплаты
Заказ №12 50000 50000 Оплачен
Заказ №13 80000 30000 Частично
Заказ №14 12000 0 Не оплачен
Важная тонкость: все ветки должны возвращать значения одного типа. Если в одной ветке строка, а в другой число — платформа приведёт результат к общему типу или выдаст ошибку. Держите ветки согласованными.
ВЫБОР по значению поля
Есть и короткая форма — сравнение конкретного поля с вариантами. Она удобна, когда «раскрашиваете» перечисление в текст:
ВЫБОР Заказы.Приоритет
КОГДА ЗНАЧЕНИЕ(Перечисление.Приоритеты.Высокий) ТОГДА "!!! Срочно"
КОГДА ЗНАЧЕНИЕ(Перечисление.Приоритеты.Обычный) ТОГДА "В план"
ИНАЧЕ "Когда-нибудь"
КОНЕЦ КАК Метка
Функция ЗНАЧЕНИЕ(...) подставляет в запрос предопределённый элемент (значение перечисления, пустую ссылку и т.п.) без параметра — это правильный способ сравнивать с константами метаданных прямо в тексте запроса.
ЕСТЬNULL: замена пустот после левого соединения
Когда вы делаете ЛЕВОЕ СОЕДИНЕНИЕ, у строк левой таблицы, которым не нашлось пары справа, поля правой таблицы становятся NULL — «значение отсутствует». NULL коварен: он не равен нулю, не равен пустой строке, и в арифметике «заражает» результат (число + NULL = NULL). Функция ЕСТЬNULL(поле, значение_по_умолчанию) подставляет запасное значение там, где пришёл NULL:
ВЫБРАТЬ
Товары.Ссылка КАК Товар,
Товары.Наименование КАК Наименование,
ЕСТЬNULL(ОстаткиНаСкладе.КоличествоОстаток, 0) КАК Остаток
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, ) КАК ОстаткиНаСкладе
ПО Товары.Ссылка = ОстаткиНаСкладе.Номенклатура
Без ЕСТЬNULL товары, у которых нет ни одного движения по складу, показали бы в колонке «Остаток» пустоту (NULL), и любая последующая сумма по этой колонке сломалась бы. С ЕСТЬNULL(..., 0) вместо пустоты стоит честный ноль, а итоги считаются корректно. Это, пожалуй, самая частая функция в отчётах с левыми соединениями и виртуальными таблицами.
ВЫРАЗИТЬ: приведение ссылочных типов
В 1С встречаются поля составного типа — например, поле «Регистратор» движения может ссылаться и на «ЗаказПокупателя», и на «РеализациюТоваров». Пока платформа видит составной тип, она не даёт обратиться к реквизиту через точку — ведь у разных типов реквизиты разные. Функция ВЫРАЗИТЬ(поле КАК Тип) говорит: «здесь трактуй значение как ссылку именно этого типа», и после неё точечное обращение к реквизитам становится доступным:
ВЫБРАТЬ
Продажи.Регистратор КАК Документ,
ВЫРАЗИТЬ(Продажи.Регистратор КАК Документ.РеализацияТоваров).Склад КАК Склад
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваров
Здесь мы сузили тип регистратора до РеализацияТоваров и достали её реквизит Склад. Условие ССЫЛКА Документ.РеализацияТоваров в ГДЕ оставляет только те строки, где регистратор действительно этого типа, — иначе ВЫРАЗИТЬ для «чужих» строк вернёт NULL. Приём «ВЫРАЗИТЬ + проверка ССЫЛКА» — стандартный способ работать с составными полями.
У ВЫРАЗИТЬ есть и вторая роль — задать длину строки или точность числа, например ВЫРАЗИТЬ(Комментарий КАК СТРОКА(100)). Это помогает, когда неограниченную строку нужно, скажем, сгруппировать (по полю неограниченной длины группировать нельзя).
Как это работает
ВЫБОР транслируется в конструкцию CASE WHEN языка SQL и вычисляется на стороне СУБД — поэтому он дёшев и не требует выгрузки данных в код. ЕСТЬNULL превращается в ISNULL/COALESCE: СУБД сама подставляет значение по умолчанию. А NULL появляется именно из-за механики соединений: левое (и полное) соединение сохраняет строки без пары, заполняя недостающие поля маркером «нет значения». ВЫРАЗИТЬ для ссылок — это, по сути, приведение типа (CAST) с проверкой: если фактический тип значения не совпал с запрошенным, вернётся NULL, а не ошибка. Понимание, что всё это выполняет СУБД, объясняет, почему причёсывать данные в запросе почти всегда выгоднее, чем в цикле после него.
Частые ошибки
- Сравнение с NULL через «равно». Проверка
Поле = NULLникогда не даёт «истина» —NULLни с чем не равен. Для замены используйтеЕСТЬNULL, а для проверки на отсутствие —Поле ЕСТЬ NULL. - Разные типы в ветках ВЫБОР. Одна ветка возвращает строку, другая — число или ссылку. Результат либо приводится к неожиданному типу, либо запрос падает. Следите за единообразием веток.
- ВЫРАЗИТЬ без проверки ССЫЛКА. Применили
ВЫРАЗИТЬко всем строкам составного поля, включая «чужие» типы, — для них получилиNULLи удивились пустотам. Ограничивайте выборку условиемССЫЛКА ТипДокумента. - Забыли ЕСТЬNULL после левого соединения. Посчитали
СУММАпо полю из левосоединённой таблицы и получилиNULLили заниженный итог, потому что часть строк была пустой. Оборачивайте такие поля вЕСТЬNULL(..., 0)до агрегации.
Итоги
ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦвычисляет значение поля по условию прямо в запросе; все ветки должны быть одного типа.ЕСТЬNULL(поле, умолчание)заменяетNULL(частый гость после левого соединения) на осмысленное значение — обычно 0 или пустую строку.NULLне равен нулю и «заражает» арифметику; сравнивать через= NULLнельзя, толькоЕСТЬ NULL.ВЫРАЗИТЬ(поле КАК Тип)приводит составную ссылку к конкретному типу, открывая доступ к её реквизитам; страхуйтесь условиемССЫЛКА.- Все три конструкции считаются на стороне СУБД — причёсывать данные в запросе выгоднее, чем в цикле после него.