Как в прямом SQL-запросе 1С 7.7 сделать отбор по типу документа в неопределенном реквизите

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

При работе с прямыми запросами к базе данных SQL в среде 1С:Предприятие 7.7 (обычно с использованием внешней компоненты 1С++) разработчики часто сталкиваются с необходимостью фильтрации данных по конкретному виду документа, если реквизит имеет неопределенный тип (ссылка на любой документ). Рассмотрим подробно, как устроены такие данные «под капотом» SQL Server и как правильно строить запросы для эффективной выборки.

Структура хранения составных типов в SQL

Для начала проанализируем, как 1С 7.7 хранит ссылки на объекты в таблицах SQL. Если реквизит имеет четко заданный тип (например, только Документ.Заказ), он занимает 9 символов — это уникальный IDDoc. Однако, если реквизит может содержать ссылки на разные виды документов, система использует расширенный формат хранения длиной 13 символов:

  1. Первые 4 символа — это внутренний идентификатор вида документа (IDDocDef) в кодировке Base36.
  2. Следующие 9 символов — это непосредственно идентификатор документа (IDDoc).

Разберем это на примере. Если мы видим в таблице значение ' 4TF 0000001ABC', то ' 4TF ' — это код типа документа (например, «Интернет-заказ»), а '0000001ABC' — уникальный идентификатор конкретного экземпляра этого документа.

Анализ типичных ошибок при написании запроса

Рассмотрим ситуацию, с которой столкнулся автор темы. Попытка выполнить INNER JOIN с таблицей конкретного документа без предварительной проверки типа приводит к ошибкам или некорректным результатам. Основная проблема в сообщении автора заключалась в использовании неопределенного алиаса заказ.IDDoc. Система SQL не знает о существовании таблицы «заказ», пока она не объявлена в блоке FROM или JOIN с соответствующим псевдонимом.

Также важно помнить, что при соединении таблиц по составному реквизиту нельзя просто написать ON Таблица1.Реквизит = Таблица2.IDDoc, так как длины полей (13 и 9 символов) не совпадут. Необходимо выполнять «распаковку» значения.

Способ 1: Фильтрация по типу документа (простой подход)

Если нам нужно просто отсечь лишние типы документов, мы можем использовать строковые функции SQL прямо в блоке WHERE. Рассмотрим пример решения, к которому пришел автор:

WHERE LEFT($партии.Заказ, 5) = ' 4TF '

Здесь используется функция LEFT, которая берет первые символы идентификатора. Однако обратите внимание, что идентификатор типа занимает 4 символа, а не 5. Пятый символ обычно является пробелом или частью ID, поэтому правильнее использовать 4 символа. Но у такого подхода есть огромный минус — хардкод. Идентификаторы типов документов (IDDocDef) генерируются системой динамически. Если вы перенесете код в другую базу или создадите документ заново, значение ' 4TF ' может измениться, и запрос перестанет работать.

Способ 2: Использование метафункций 1С++ (рекомендуемый)

Чтобы избежать проблем с переносимостью кода, проанализируем использование метафункций, которые предоставляет ВК 1С++. Вместо жесткого прописывания идентификатора в кавычках, воспользуемся конструкцией $ВидДокумента36.

Рассмотрим правильный вариант формирования условия:


SELECT 
    продажа.IDDoc as [док $Документ.НМпродажа],
    $партии.Заказ as [заказ $Документ]
FROM 
    $ДокументСтроки.НМпродажа as продажа
LEFT JOIN 
    $Справочник.Партии as партии ON $продажа.Партия = партии.id
WHERE 
    $партии.Заказ LIKE ($ВидДокумента36.ИнтернетЗаказ + '%')

В этом примере мы используем оператор LIKE. Это более производительно, чем использование функций LEFT или SUBSTRING, так как позволяет SQL Server использовать индексы по полю (если они есть). Метафункция $ВидДокумента36.ИнтернетЗаказ автоматически подставит нужные 4 символа типа документа при компиляции запроса.

Способ 3: Получение данных из конкретной таблицы документа

Если нам нужно не просто отфильтровать данные, а получить реквизиты из связанного документа (например, дату или номер «Заказа»), нам придется выполнить соединение (JOIN). Выясним, как это сделать правильно, учитывая составной тип поля.

Поскольку в поле может храниться ссылка на разные таблицы, нам нужно использовать LEFT JOIN для каждого интересующего нас типа:


SELECT 
    $партии.Заказ as [ЗаказСсылка $Документ],
    COALESCE(докЗаказ.DocNo, докИнтЗаказ.DocNo) as НомерЗаказа
FROM 
    $Справочник.Партии as партии
LEFT JOIN 
    $Документ.Заказ as докЗаказ ON докЗаказ.IDDoc = RIGHT($партии.Заказ, 9)
    AND LEFT($партии.Заказ, 4) = $ВидДокумента36.Заказ
LEFT JOIN 
    $Документ.ЗаказИнтернет as докИнтЗаказ ON докИнтЗаказ.IDDoc = RIGHT($партии.Заказ, 9)
    AND LEFT($партии.Заказ, 4) = $ВидДокумента36.ЗаказИнтернет

Проанализируем этот код:

  1. Мы используем RIGHT($партии.Заказ, 9), чтобы выделить чистый IDDoc для связи с таблицей документа.
  2. Мы обязательно добавляем условие по LEFT(..., 4) в секцию ON, чтобы SQL не пытался найти документ одного типа в таблице другого типа (это исключит ложные совпадения IDDoc).
  3. Функция COALESCE позволяет нам выбрать первое непустое значение из тех таблиц, которые «подцепились» по ссылке.

Оптимизация производительности

При работе с большими объемами данных в 1С 7.7 важно помнить об индексах. Прямые запросы к таблицам строк документов ($ДокументСтроки) могут работать медленно, если не ограничить выборку по дате.

Рассмотрим, почему в исходном запросе автора было соединение с таблицей _1SJourn. Это стандартный прием для 1С 7.7. Общий журнал документов (_1SJourn) содержит поле Date_Time_IDDoc, которое проиндексировано. Выполняя INNER JOIN с этой таблицей по полю IDDoc, мы можем эффективно ограничить выборку периодом:


INNER JOIN 
    _1SJourn as жур ON жур.IDDoc = продажа.IDDoc 
    AND жур.Date_Time_IDDoc BETWEEN :ДатаНач AND :ДатаКон~

Символ тильды ~ в конце параметра даты — это синтаксис 1С++, который указывает на необходимость включения всей последней секунды дня (23:59:59) в диапазон.

Подведение итогов

Для успешной реализации прямого запроса с фильтрацией по типу документа необходимо:

Соблюдение этих правил позволит создавать быстрые и надежные отчеты на прямых запросах, которые будут корректно работать даже при обновлении конфигурации или миграции базы данных.

← На главную