При работе с прямыми SQL-запросами в 1С 7.7 (используя внешние компоненты, такие как 1C++ или Rainbow) программисты часто сталкиваются с проблемой типизации. Когда запрос выбирает данные из одного конкретного справочника, типизация проходит гладко. Однако задача существенно усложняется, если в одну колонку результирующей таблицы должны попадать значения из разных объектов метаданных — например, контрагенты из одного вида документа и склады из другого.
В этой статье мы подробно разберем, как заставить 1С воспринимать строковые идентификаторы SQL как полноценные объекты (справочники), и как правильно формировать составные поля для корректной типизации.
Рассмотрим стандартную ситуацию: мы пишем прямой запрос к таблице журнала документов _1SJourn и присоединяем таблицы конкретных документов. Допустим, у нас есть «Приходный Ордер», где поле «Отправитель» — это справочник «Контрагенты», и «Отгрузка», где «Отправитель» — это справочник «Склады».
Если мы просто выберем идентификаторы этих объектов через CASE, SQL вернет нам девятисимвольные строки (ID). Для SQL-сервера это просто набор символов. Компонента 1C++ при попытке типизировать такое поле как $Справочник.Контрагенты проигнорирует значения, которые не относятся к этому справочнику. А если указать общий тип $Справочник, система не поймет, из какой именно таблицы брать объект, так как ей не хватает информации о виде справочника.
Для того чтобы решить задачу, нам нужно понять, как 1С хранит ссылки на объекты «неопределенного» типа. В системе 7.7 для этого используется строка длиной 13 символов. Она формируется по следующим правилам:
Следовательно, наша задача в SQL-запросе — не просто выбрать ID, а «склеить» префикс вида объекта с его идентификатором.
Проанализируем правильный подход к написанию запроса. Для получения четырехсимвольного префикса вида справочника в 1C++ используется специальная макроподстановка $ВидСправочника36.ИмяСправочника. Рассмотрим, как это выглядит в коде запроса.
ТекстЗапроса = "
|SELECT
| Жур.IDDoc as [Док $Документ],
| Жур.IDDocDef Док_вид,
|
|CASE Жур.IDDocDef
| WHEN $ВидДокумента.ПриходныйОрдер THEN $ВидСправочника36.Контрагенты + $ДокПО.Контрагент
| WHEN $ВидДокумента.Отгрузка THEN $ВидСправочника36.Склады + $ДокОтгр.Склад
| ELSE $ПустойИД13
|END AS [Отправитель $Справочник]
|
|FROM
| _1SJourn as Жур (nolock)
|LEFT JOIN
| $Документ.ПриходныйОрдер as ДокПО ON ДокПО.IDDoc = Жур.IDDoc
|LEFT JOIN
| $Документ.Отгрузка as ДокОтгр ON ДокОтгр.IDDoc = Жур.IDDoc
|WHERE
| Жур.Date_Time_IDDoc BETWEEN :НачДата AND :КонДата~ AND
| Жур.IDDocDef IN ($ВидДокумента.ПриходныйОрдер, $ВидДокумента.Отгрузка)
|";
В данном примере мы используем конструкцию CASE, чтобы определить вид документа. В зависимости от вида мы берем соответствующий 36-ричный префикс и прибавляем к нему ID объекта. Обратите внимание на типизацию колонки: AS [Отправитель $Справочник]. Теперь 1C++ получит строку из 13 символов, успешно ее распарсит и вернет в 1С полноценный объект (элемент справочника).
Выясним некоторые технические детали, которые могут повлиять на результат:
VARCHAR, рекомендуется использовать CAST(результат AS CHAR(13)), чтобы избежать проблем с «хвостами» или лишними пробелами.CASE не срабатывает, лучше возвращать специальную константу пустого значения. В 1C++ это часто реализуется через 13 пробелов или макрос $ПустойИД13. Если вернуть просто пустую строку или NULL, типизация всей колонки может работать нестабильно.AS [Поле $ТипЛюбой]. В этом случае к префиксу вида объекта (4 символа) нужно будет добавить еще и префикс самого метатипа.Иногда нам не нужен сам объект 1С, а достаточно лишь вывести его текстовое представление (наименование). Это может быть полезно для ускорения работы отчета или если типизация вызывает сложности. Проанализируем, как это реализовать эффективно.
Для этого нам придется соединиться с таблицами справочников и выбрать поле DESCR. Посмотрим на пример реализации через дополнительные LEFT JOIN:
|SELECT
| CASE Жур.IDDocDef
| WHEN $ВидДокумента.ПриходныйОрдер THEN СпрКонтр.DESCR
| WHEN $ВидДокумента.Отгрузка THEN СпрСклады.DESCR
| ELSE ''
| END AS ОтправительНаименование
|FROM
| _1SJourn as Жур (nolock)
|LEFT JOIN
| $Документ.ПриходныйОрдер as ДокПО ON ДокПО.IDDoc = Жур.IDDoc
|LEFT JOIN
| $Справочник.Контрагенты as СпрКонтр ON СпрКонтр.ID = $ДокПО.Контрагент
|LEFT JOIN
| $Документ.Отгрузка as ДокОтгр ON ДокОтгр.IDDoc = Жур.IDDoc
|LEFT JOIN
| $Справочник.Склады as СпрСклады ON СпрСклады.ID = $ДокОтгр.Склад
Важный момент: При получении наименований всегда используйте ISNULL(..., ''). Если SQL встретит NULL при попытке отображения данных, это может привести к пустым ячейкам в результате всего отчета.
Использование множества LEFT JOIN и условий CASE в блоке SELECT может замедлять выполнение запроса на очень больших объемах данных. Рассмотрим вариант оптимизации через UNION ALL. Вместо того чтобы соединять всё в одном гигантском запросе, мы можем выполнить выборку по каждому виду документа отдельно и объединить результаты:
|SELECT
| $ДокПО.Контрагент as [Объект $Справочник.Контрагенты]
|FROM $Документ.ПриходныйОрдер as ДокПО
|
|UNION ALL
|
|SELECT
| $ДокОтгр.Склад as [Объект $Справочник.Склады]
|FROM $Документ.Отгрузка as ДокОтгр
Такой подход позволяет SQL-серверу строить более простые планы выполнения. Однако стоит помнить, что при использовании UNION ALL типы колонок должны быть совместимы. Для 1C++ это означает, что типизация должна быть приведена к общему виду, например, $Справочник или $ТипЛюбой, с добавлением тех самых префиксов $ВидСправочника36, которые мы разобрали выше.
Подведем итог: правильная типизация в 1С 7.7 требует понимания внутренней структуры ID. Используя $ВидСправочника36, вы сможете создавать гибкие и мощные отчеты, объединяющие данные из самых разных объектов метаданных.