Как в 1С 7.7 с помощью 1С++ получить реквизиты объекта из поля с неконкретным типом Документ

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок на платформе 1С 7.7 с применением библиотеки 1С++ и прямых SQL-запросов нередко возникает задача: в табличной части или шапке документа присутствует реквизит с составным (неопределенным) типом Документ. В этом поле могут храниться документы разных видов (например, Полуфабрикат1 или Полуфабрикат2), и из каждого вида требуется получить значение конкретного реквизита (например, НомерИзвещения).

Разберем детально, как устроено хранение таких данных на уровне СУБД, почему нельзя просто соединять таблицы по 9-значному идентификатору и как правильно составить SQL-запрос с использованием специализированных макросов 1С++.

Особенности хранения неконкретного типа «Документ» в базе данных 1С 7.7

Проанализируем внутреннее представление реквизитов составного типа в таблицах 1С 7.7:

  1. Формат хранения: поле с типом Документ без указания конкретного вида хранится в базе как фиксированная строка CHAR(13).
  2. Структура строки: первые 4 символа (LEFT(Поле, 4)) содержат 36-ричный идентификатор вида документа (код метаданных), а оставшиеся 9 символов (RIGHT(Поле, 9)) представляют собой уникальный идентификатор конкретного документа — IDDOC.
  3. Пустые значения: незаполненное значение представляет собой строку из 13 пробелов.
  4. Опасность совпадения ID: в 1С 7.7 идентификаторы IDDOC уникальны только в рамках одного вида документа или в журнале документов, но между таблицами шапок разных видов документов значения IDDOC могут пересекаться. Поэтому соединение только по 9 правым символам без проверки вида объекта является грубой ошибкой и может привести к дублированию или искажению данных.

Макросы 1С++: разница между $ВидДокумента и $ВидДокумента36

Библиотека 1С++ предоставляет макросы для работы с метаданными. Важно понимать различие между ними:

  1. $ВидДокумента.ИмяВида — в среде MS SQL возвращает числовое значение типа int, используемое для фильтрации поля IDDOCDEF в общей таблице журнала _1SJOURN.
  2. $ВидДокумента36.ИмяВида — возвращает строку длиной 4 символа (CHAR(4)) в 36-ричной системе счисления. Именно этот макрос предназначен для сравнения с первыми 4 символами составного поля.

Основное решение: соединение с таблицами через LEFT JOIN и $ВидДокумента36

Поскольку в SQL невозможно динамически подставить имя таблицы в секцию JOIN с помощью конструкции CASE, мы должны явно соединить каждую потенциальную таблицу документа через LEFT JOIN. В условии соединения обязательно проверяем как 4 символа вида документа, так и 9 символов его идентификатора.

Посмотрим на практический пример запроса, который выбирает строки табличной части документа ВыпускПродукции и извлекает НомерИзвещения из документов Полуфабрикат1 или Полуфабрикат2:


SELECT 
    ДокВыпСтр.IDDOC AS [Док $Документ.ВыпускПродукции],
    ДокВыпСтр.$Полуфабрикат AS [ПФ $Документ],
    COALESCE(ПФ1.$НомерИзвещения, ПФ2.$НомерИзвещения, '') AS НомерИзвещения
FROM $Документ.ВыпускПродукцииСтроки AS ДокВыпСтр
LEFT JOIN _1SJOURN AS Жур ON Жур.IDDOC = ДокВыпСтр.IDDOC
LEFT JOIN $Документ.Полуфабрикат1 AS ПФ1 
    ON LEFT(ДокВыпСтр.$Полуфабрикат, 4) = $ВидДокумента36.Полуфабрикат1 
    AND RIGHT(ДокВыпСтр.$Полуфабрикат, 9) = ПФ1.IDDOC
LEFT JOIN $Документ.Полуфабрикат2 AS ПФ2 
    ON LEFT(ДокВыпСтр.$Полуфабрикат, 4) = $ВидДокумента36.Полуфабрикат2 
    AND RIGHT(ДокВыпСтр.$Полуфабрикат, 9) = ПФ2.IDDOC
WHERE Жур.DATE_TIME_IDDOC BETWEEN :НачДата AND :КонДата~
  AND Жур.CLOSED & 1 = 1

Разберем ключевые элементы этого запроса:

  1. Типизация полей: конструкция [ПФ $Документ] сообщает 1С++, что 13-символьную строку необходимо типизировать в объект типа «Документ» при выгрузке данных в ТаблицуЗначений.
  2. Проверка вида и ID: в условиях соединений LEFT(ДокВыпСтр.$Полуфабрикат, 4) = $ВидДокумента36.ИмяВида и RIGHT(ДокВыпСтр.$Полуфабрикат, 9) = ПФ.IDDOC обеспечивается точная привязка конкретной строки только к нужной таблице метаданных.
  3. Сведение значений: функция COALESCE(...) (или конструкция CASE WHEN ... THEN ... END) выбирает первое непустое значение реквизита из успешно присоединенной таблицы.

Альтернативные способы решения

Рассмотрим дополнительные сценарии в зависимости от структуры вашей конфигурации:

  1. Через общий реквизит журнала: если реквизит НомерИзвещения является общим реквизитом документов и включен для хранения в общем журнале _1SJOURN, присоединять таблицы шапок документов $Документ.Полуфабрикат1 и $Документ.Полуфабрикат2 не требуется. Достаточно выполнить LEFT JOIN _1SJOURN по условию _1SJOURN.IDDOC = RIGHT(ДокВыпСтр.$Полуфабрикат, 9) и выбрать поле общего реквизита.
  2. Использование UNION ALL: если видов документов много (более 5–10) и множественные LEFT JOIN начинают снижать производительность запроса, эффективнее разбить выборку на несколько запросов с фильтрацией WHERE LEFT(ДокВыпСтр.$Полуфабрикат, 4) = $ВидДокумента36... и объединить результаты через UNION ALL.
  3. Исключение незаполненных ссылок: чтобы оптимизатор СУБД не выполнял лишних проверок для пустых строк, рекомендуется добавить в блок WHERE или в условие соединения проверку на заполненность поля: ДокВыпСтр.$Полуфабрикат <> ' '.
← На главную