Тип значения в запросе 1С: как узнать и сравнить
Содержание
Чтобы узнать тип значения прямо в тексте запроса, в языке запросов 1С 8.3 есть функция ТИПЗНАЧЕНИЯ(<выражение>) — она возвращает тип любого поля или выражения. Сравнить этот тип с конкретным помогает функция ТИП(<ИмяТипа>), а проверить, принадлежит ли ссылка нужной таблице, — оператор ССЫЛКА.
Тип определяют в двух местах: внутри самого запроса (когда надо отфильтровать строки или разложить составное поле) и уже на стороне встроенного языка через ТипЗнч() — когда запрос выполнен и вы перебираете выборку. Ниже — обе ветки, с готовыми примерами и разбором подводных камней вроде составных реквизитов, NULL и временных таблиц.
Функция ТИПЗНАЧЕНИЯ: тип выражения внутри запроса
ТИПЗНАЧЕНИЯ (в англоязычном синтаксисе — VALUETYPE) возвращает тип переданного ей выражения. Чаще всего её применяют к полям составного типа — таким, которые могут хранить значения разных типов: Регистратор в движениях регистров, Субконто в проводках, поле Значение в регистрах сведений, реквизиты плана видов характеристик.
ВЫБРАТЬ
Движения.Регистратор КАК Регистратор,
ТИПЗНАЧЕНИЯ(Движения.Регистратор) КАК ТипРегистратора
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
Для каждой строки поле ТипРегистратора вернёт реальный тип значения в этой строке: для одной — ДокументСсылка.ПоступлениеТоваровУслуг, для другой — ДокументСсылка.РеализацияТоваровУслуг.
💡 Для полей простого типа ТИПЗНАЧЕНИЯ тоже работает: у числового поля вернётся тип «Число», у строкового — «Строка». Это удобно, когда реквизит объявлен составным (например, Число + Строка) и нужно разделить строки по фактическому содержимому.
Отдельный случай — NULL. Если в строке получилось пустое значение от соединения, ТИПЗНАЧЕНИЯ вернёт специальный тип NULL. Это не то же самое, что незаполненная ссылка, — к различию NULL и Неопределено вернёмся ниже.
Типичный рабочий пример — посчитать движения в разрезе типов регистратора, не перебирая документы вручную. ТИПЗНАЧЕНИЯ можно поставить прямо в группировку:
ВЫБРАТЬ
ТИПЗНАЧЕНИЯ(Движения.Регистратор) КАК ТипДокумента,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Движения.Регистратор) КАК КолвоДокументов
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
СГРУППИРОВАТЬ ПО
ТИПЗНАЧЕНИЯ(Движения.Регистратор)
Результат — сводка: сколько документов каждого вида двигали регистр. Тот же приём работает для субконто в проводках регистра бухгалтерии: группировка по ТИПЗНАЧЕНИЯ(СубконтоДт1) покажет, в разрезе каких видов аналитики формировались обороты.
Сравнение типа: функция ТИП и оператор ССЫЛКА
Сам по себе тип — значение, которое не с чем сравнивать, пока нет эталона. Эталон даёт функция ТИП (TYPE). В скобки пишут имя таблицы метаданных или ключевое слово простого типа.
ВЫБРАТЬ
Движения.Регистратор
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
ТИПЗНАЧЕНИЯ(Движения.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг)
Такой запрос оставит только движения, сделанные документом реализации. Для простых типов внутри ТИП пишут ЧИСЛО, СТРОКА, ДАТА, БУЛЕВО:
ГДЕ ТИПЗНАЧЕНИЯ(Регистр.Значение) = ТИП(ЧИСЛО)
Когда нужно просто проверить, относится ли ссылка к конкретной таблице, короче и нагляднее оператор ССЫЛКА (REFS). Он возвращает Истина или Ложь:
ВЫБРАТЬ
Движения.Регистратор,
ВЫБОР
КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА "Реализация"
КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
ТОГДА "Поступление"
ИНАЧЕ "Прочее"
КОНЕЦ КАК ВидДокумента
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
Разница между подходами по сути в удобстве:
- 🔹
ТИПЗНАЧЕНИЯ(...) = ТИП(...)— когда тип нужен как значение: вывести его в колонку, сгруппировать, сравнить два поля между собой. - 🔹
Поле ССЫЛКА Таблица— когда нужен только ответ «да/нет» в условииГДЕилиВЫБОР. Читается легче.
Оператор ССЫЛКА применим только к ссылочным типам. Для поля, которое может быть числом или строкой, подойдёт лишь связка ТИПЗНАЧЕНИЯ + ТИП.
Приведение составного типа функцией ВЫРАЗИТЬ
Знать тип — половина дела. Часто после проверки нужно обратиться к реквизитам объекта: получить номер документа, дату, контрагента. Вот тут новички спотыкаются.
Через точку от поля составного типа напрямую обращаться нельзя — вернее, можно, но платформа при этом соединяет запрос со ВСЕМИ таблицами, входящими в составной тип. Если в составной тип входит два десятка документов, запрос получит два десятка левых соединений и будет работать медленно.
Решение — функция ВЫРАЗИТЬ (CAST). Она приводит составное поле к одному конкретному типу, и платформа соединяет запрос только с одной таблицей:
ВЫБРАТЬ
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Номер КАК Номер,
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент КАК Контрагент
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
Логика такая:
- Оператором
ССЫЛКАв условииГДЕотбираем только нужные документы. - Функцией
ВЫРАЗИТЬприводим поле к этому типу. - Через точку от результата
ВЫРАЗИТЬберём реквизиты — уже без лишних соединений.
Важная деталь: если фактический тип значения не совпадает с указанным в ВЫРАЗИТЬ, функция вернёт NULL, а не ошибку. Поэтому ВЫРАЗИТЬ без предварительного фильтра тоже работает как способ отбора — несовпавшие строки дадут NULL, и их можно отсечь.
💡 ВЫРАЗИТЬ применяют и к простым типам: ВЫРАЗИТЬ(Регистр.Значение КАК ЧИСЛО) вытащит из составного поля только числовые значения, остальные станут NULL.
NULL и Неопределено: в чём разница при проверке типа
Два «пустых» значения в 1С путают постоянно, а в запросах они ведут себя по-разному.
| Значение | Откуда берётся | Как проверить в запросе |
|---|---|---|
NULL | левое соединение без пары, несуществующий реквизит, несовпавший ВЫРАЗИТЬ | Поле ЕСТЬ NULL, функция ЕСТЬNULL(Поле, Замена) |
Неопределено | пустое значение составного реквизита, незаполненная составная ссылка | Поле = НЕОПРЕДЕЛЕНО, ТИПЗНАЧЕНИЯ(Поле) = ТИП("Неопределено") |
NULL — это отсутствие данных на уровне результата запроса. Он не равен ни нулю, ни пустой строке, ни даже самому себе: сравнение NULL = NULL даёт не Истина, а снова неопределённый результат. Поэтому проверяют его только оператором ЕСТЬ NULL:
ВЫБРАТЬ
Товары.Номенклатура,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Остатки.Номенклатура = Товары.Ссылка
ГДЕ
Остатки.Номенклатура ЕСТЬ NULL
Неопределено — заполненное значение, просто «пустое по смыслу». Незаполненная составная ссылка хранит именно Неопределено, и ТИПЗНАЧЕНИЯ для неё вернёт тип «Неопределено», а не NULL.
Если перепутать и написать ГДЕ Поле = НЕОПРЕДЕЛЕНО там, где на самом деле NULL (например, после левого соединения), условие молча не сработает — строки не отберутся, ошибки не будет. Это классический источник «пропавших» данных в отчётах.
Определение типа на стороне встроенного языка
Когда запрос выполнен и вы перебираете выборку, тип значения определяют функцией ТипЗнч(<Значение>). Это уже встроенный язык, а не язык запросов — не путайте с ТИПЗНАЧЕНИЯ.
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Если ТипЗнч(Выборка.Регистратор) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
// обработка реализации
КонецЕсли;
КонецЦикла;
Эталонный тип здесь задаёт функция Тип(<СтрокаИмениТипа>) — в кавычках пишут полное имя: "ДокументСсылка.РеализацияТоваровУслуг", "СправочникСсылка.Номенклатура", либо простой тип "Число", "Строка", "Дата", "Булево".
Полезные приёмы на стороне языка:
- 🔹 Строковое представление типа. Если нужно вывести тип текстом (в лог, в сообщение), оберните результат в
Строка(ТипЗнч(Значение))— получите читаемое название типа. - 🔹 Имя объекта метаданных. Для ссылки
Значение.Метаданные().Имявернёт имя справочника или документа как строку — удобно, когда типов много и перебирать их черезЕслинеудобно. - 🔹 Проверка на пустую ссылку. Метод
Значение.Пустая()отвечает, пустая ли ссылка, аЗначениеЗаполнено(Значение)— универсальная проверка заполненности для любого типа.
💡 Когда перебираете строки таблицы значений или результат запроса с составной колонкой, определять тип на стороне языка проще, чем городить ВЫБОР … КОГДА в самом запросе. Но помните: сам запрос при этом вытащит составное поле целиком, и если дальше вы дёргаете реквизиты через точку уже в цикле — каждое обращение к .Реквизит это отдельный запрос к базе. Для больших выборок лучше получить реквизиты сразу в запросе через ВЫРАЗИТЬ.
Ещё один частый вопрос — как сравнить значение сразу с несколькими типами. Длинную цепочку Если … ИначеЕсли заменяет проверка через соответствие: заранее складываете нужные типы в Соответствие или проверяете вхождение в массив типов. А если типов у реквизита много и важно только, ссылка это или примитив, короче спросить ТипЗнч(Значение) = Тип("Строка") для простых и ЗначениеЗаполнено(Значение) для ссылок — последняя функция сама корректно отрабатывает и пустую ссылку, и Неопределено, и NULL, и пустую строку.
Куда определить тип: короткая подсказка
ССЫЛКА в условии ГДЕ (для ссылок) или ТИПЗНАЧЕНИЯ(...) = ТИП(...) для любых типов.ТИПЗНАЧЕНИЯ(Поле) прямо в списке выборки — она вернёт тип как значение.ВЫРАЗИТЬ(Поле КАК Документ.Имя) и обращайтесь через точку — без лишних соединений.ТипЗнч(Значение) встроенного языка в связке с Тип("...").ЕСТЬ NULL — для результатов соединений; = НЕОПРЕДЕЛЕНО — для незаполненных составных ссылок.Типизация во временных таблицах и производительность
Временные таблицы — отдельная история. Когда вы кладёте в такую таблицу поле составного типа, его тип сохраняется: платформа запоминает, какие типы были у исходного выражения.
Проблема возникает при дальнейшем соединении и обращении к реквизитам. Если во временной таблице лежит составной Регистратор, то последующее соединение по нему или получение .Номер через точку снова развернётся в множество соединений со всеми таблицами типа.
Правило простое:
- Приводите тип при заполнении. Если точно знаете, что в таблицу попадут только реализации, сделайте
ВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг)уже на этапеПОМЕСТИТЬ. Тогда во временной таблице поле будет одного конкретного типа. - Индексируйте то, по чему соединяете. В конструкции
ПОМЕСТИТЬ ВремТаблицадобавляйтеИНДЕКСИРОВАТЬ ПО. Для составного поля индекс работает, но выигрыш меньше, чем по полю одного типа, — ещё один довод сузить тип заранее. - Не тащите составное поле «на всякий случай». Если дальше по тексту запроса реквизиты конкретного типа не нужны, оставляйте только ссылку, а разворачивайте её через
ВЫРАЗИТЬв самом конце, в финальной выборке.
ВЫБРАТЬ
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг) КАК Документ,
Движения.Количество
ПОМЕСТИТЬ ВтДвижения
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ИНДЕКСИРОВАТЬ ПО
Документ
После такого ВЫРАЗИТЬ поле Документ во временной таблице уже одного типа — соединения по нему и получение реквизитов через точку пойдут без разрастания плана запроса.
Проверить, во что развернулся ваш запрос, помогает технологический журнал и замер производительности в конфигураторе: если в плане вдруг десятки соединений там, где вы ждали одно, почти наверняка виновато необёрнутое составное поле.
Проверьте себя
Частые вопросы
Чем отличается ТИПЗНАЧЕНИЯ от ТипЗнч?
ТИПЗНАЧЕНИЯ — функция языка запросов, работает в тексте запроса. ТипЗнч — функция встроенного языка, работает с уже полученными значениями в коде модуля. Имена похожи, но это разные механизмы в разных местах.Можно ли в запросе получить тип значения как строку?
ТИПЗНАЧЕНИЯ возвращает значение типа «Тип», а не строку. Если нужно текстовое название, получите тип на стороне встроенного языка и оберните в Строка(ТипЗнч(Значение)) либо используйте Значение.Метаданные().Имя для ссылок.Почему не получается обратиться к Регистратор.Номер через точку?
Регистратор составного типа. Обращение через точку заставляет платформу соединиться со всеми таблицами типа, а иногда реквизит вообще недоступен без приведения. Правильно — ВЫРАЗИТЬ(Регистратор КАК Документ.Имя).Номер с предварительным отбором по ССЫЛКА.Как в запросе оставить только строковые значения составного реквизита?
ГДЕ ТИПЗНАЧЕНИЯ(Поле) = ТИП(СТРОКА) либо через ВЫРАЗИТЬ(Поле КАК СТРОКА) — во втором случае нестроковые значения станут NULL, и их отсекают ЕСТЬ NULL.Что быстрее: оператор ССЫЛКА или ТИПЗНАЧЕНИЯ = ТИП?
ССЫЛКА, для вывода типа в колонку или сравнения двух полей нужна связка ТИПЗНАЧЕНИЯ и ТИП.