При работе с прямыми запросами к базе данных SQL в среде 1С:Предприятие 7.7 (обычно с использованием внешней компоненты 1С++) разработчики часто сталкиваются с необходимостью фильтрации данных по конкретному виду документа, если реквизит имеет неопределенный тип (ссылка на любой документ). Рассмотрим подробно, как устроены такие данные «под капотом» SQL Server и как правильно строить запросы для эффективной выборки.
Для начала проанализируем, как 1С 7.7 хранит ссылки на объекты в таблицах SQL. Если реквизит имеет четко заданный тип (например, только Документ.Заказ), он занимает 9 символов — это уникальный IDDoc. Однако, если реквизит может содержать ссылки на разные виды документов, система использует расширенный формат хранения длиной 13 символов:
IDDocDef) в кодировке Base36.IDDoc).Разберем это на примере. Если мы видим в таблице значение ' 4TF 0000001ABC', то ' 4TF ' — это код типа документа (например, «Интернет-заказ»), а '0000001ABC' — уникальный идентификатор конкретного экземпляра этого документа.
Рассмотрим ситуацию, с которой столкнулся автор темы. Попытка выполнить INNER JOIN с таблицей конкретного документа без предварительной проверки типа приводит к ошибкам или некорректным результатам. Основная проблема в сообщении автора заключалась в использовании неопределенного алиаса заказ.IDDoc. Система SQL не знает о существовании таблицы «заказ», пока она не объявлена в блоке FROM или JOIN с соответствующим псевдонимом.
Также важно помнить, что при соединении таблиц по составному реквизиту нельзя просто написать ON Таблица1.Реквизит = Таблица2.IDDoc, так как длины полей (13 и 9 символов) не совпадут. Необходимо выполнять «распаковку» значения.
Если нам нужно просто отсечь лишние типы документов, мы можем использовать строковые функции SQL прямо в блоке WHERE. Рассмотрим пример решения, к которому пришел автор:
WHERE LEFT($партии.Заказ, 5) = ' 4TF '
Здесь используется функция LEFT, которая берет первые символы идентификатора. Однако обратите внимание, что идентификатор типа занимает 4 символа, а не 5. Пятый символ обычно является пробелом или частью ID, поэтому правильнее использовать 4 символа. Но у такого подхода есть огромный минус — хардкод. Идентификаторы типов документов (IDDocDef) генерируются системой динамически. Если вы перенесете код в другую базу или создадите документ заново, значение ' 4TF ' может измениться, и запрос перестанет работать.
Чтобы избежать проблем с переносимостью кода, проанализируем использование метафункций, которые предоставляет ВК 1С++. Вместо жесткого прописывания идентификатора в кавычках, воспользуемся конструкцией $ВидДокумента36.
Рассмотрим правильный вариант формирования условия:
SELECT
продажа.IDDoc as [док $Документ.НМпродажа],
$партии.Заказ as [заказ $Документ]
FROM
$ДокументСтроки.НМпродажа as продажа
LEFT JOIN
$Справочник.Партии as партии ON $продажа.Партия = партии.id
WHERE
$партии.Заказ LIKE ($ВидДокумента36.ИнтернетЗаказ + '%')
В этом примере мы используем оператор LIKE. Это более производительно, чем использование функций LEFT или SUBSTRING, так как позволяет SQL Server использовать индексы по полю (если они есть). Метафункция $ВидДокумента36.ИнтернетЗаказ автоматически подставит нужные 4 символа типа документа при компиляции запроса.
Если нам нужно не просто отфильтровать данные, а получить реквизиты из связанного документа (например, дату или номер «Заказа»), нам придется выполнить соединение (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.ЗаказИнтернет
Проанализируем этот код:
RIGHT($партии.Заказ, 9), чтобы выделить чистый IDDoc для связи с таблицей документа.LEFT(..., 4) в секцию ON, чтобы SQL не пытался найти документ одного типа в таблице другого типа (это исключит ложные совпадения IDDoc).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) в диапазон.
Для успешной реализации прямого запроса с фильтрацией по типу документа необходимо:
$ВидДокумента36 для обеспечения универсальности кода.LIKE вместо строковых функций в блоке WHERE для повышения скорости работы.RIGHT(..., 9).Соблюдение этих правил позволит создавать быстрые и надежные отчеты на прямых запросах, которые будут корректно работать даже при обновлении конфигурации или миграции базы данных.