При работе с прямыми запросами в 1С 7.7 (с использованием внешней компоненты 1С++) программисты часто сталкиваются с необходимостью получения данных, которые находятся «через несколько точек» от основного объекта. Ситуация осложняется, когда целевой реквизит является периодическим. В «черных» (стандартных) запросах 1С система берет на себя всю работу по поиску актуального значения на нужную дату, но в SQL нам приходится прописывать эту логику самостоятельно.
В данной статье мы разберем, как эффективно построить запрос к журналу документов, объединить данные из нескольких таблиц и вытащить периодический реквизит справочника, связанного с документом.
Представим ситуацию: нам нужно сформировать отчет по различным типам документов (Реализация, ЗаявкаПокупателя, КоммПредложение, СтрокаВыпискиПриход). Для каждого документа необходимо получить контрагента и его менеджера (реквизит ВашМенеджер справочника Проекты), причем менеджер является периодическим реквизитом справочника Контрагенты.
Рассмотрим структуру базового запроса к таблице журнала документов _1SJOURN. Для объединения данных из разных типов документов мы будем использовать LEFT JOIN к каждой таблице документа и функцию COALESCE для выбора первого непустого значения контрагента.
SELECT
Журнал.IDDoc AS [ТекДок $Документ],
Журнал.IDDocDef AS ТекДок_вид,
coalesce($Реализация.Контрагент, $ЗаявкаПокупателя.Контрагент, $КоммПредложение.Контрагент, $СтрокаВыпискиПриход.Контрагент) AS [Контрагент $Справочник.Контрагенты]
FROM
_1SJOURN AS Журнал With (NOLOCK)
LEFT JOIN
$Документ.Реализация AS Реализация With (NOLOCK) ON Журнал.IDDOC = Реализация.IDDOC
LEFT JOIN
$Документ.ЗаявкаПокупателя AS ЗаявкаПокупателя With (NOLOCK) ON Журнал.IDDOC = ЗаявкаПокупателя.IDDOC
-- Дополнительные LEFT JOIN для других документов...
WHERE
Журнал.date_time_iddoc between :НачДата and :КонДата~
Если бы реквизит ВашМенеджер был обычным (не периодическим), мы бы просто добавили еще один LEFT JOIN к таблице $Справочник.Контрагенты и обратились бы к полю $Контрагенты.ВашМенеджер. Однако периодические значения в 1С 7.7 хранятся не в основной таблице справочника, а в общей таблице _1SCONST. Прямое обращение к ней требует написания сложных подзапросов для поиска значения, актуального на конкретную дату (с учетом времени и позиции документа).
К счастью, компонента 1С++ предоставляет мощный инструмент — макрос $ПоследнееЗначение, который автоматизирует генерацию подзапроса к таблице констант.
Проанализируем правильный синтаксис получения периодического реквизита. Нам нужно получить ID менеджера из справочника Контрагенты. Мы можем сделать это прямо в блоке SELECT или в условии связи JOIN.
Рассмотрим по шагам, как добавить получение менеджера для документа Реализация:
$ПоследнееЗначение, передавая в него имя справочника, имя реквизита, ID объекта (контрагента) и дату, на которую нужно получить значение.Пример реализации в составе общего запроса:
SELECT
Журнал.IDDoc AS [ТекДок $Документ],
$Реализация.Контрагент AS [Контрагент $Справочник.Контрагенты],
-- Получаем ID менеджера через макрос
($ПоследнееЗначение.Контрагенты.ВашМенеджер($Реализация.Контрагент, :КонДата)) AS [Менеджер $Справочник.Проекты]
FROM
_1SJOURN AS Журнал With (NOLOCK)
LEFT JOIN
$Документ.Реализация AS Реализация With (NOLOCK) ON Реализация.IDDOC = Журнал.IDDOC
WHERE
Журнал.iddocdef = $ВидДокумента.Реализация
AND Журнал.date_time_iddoc between :НачДата and :КонДата~
В данном примере :КонДата — это параметр запроса (дата конца периода). Если нам нужно получить менеджера, который был закреплен за контрагентом на момент создания документа, вместо фиксированной даты следует использовать поле журнала Журнал.date_time_iddoc.
Поскольку в исходной задаче документов несколько, мы можем применить функцию COALESCE не только к самому контрагенту, но и к результатам макросов. Однако более эффективным и читаемым способом будет сначала вычислить ID контрагента, а затем один раз вызвать получение периодического реквизита. Для этого можно обернуть основной запрос во вложенный или использовать временную таблицу.
Но если мы хотим остаться в рамках одного плоского запроса, решение будет выглядеть так:
SELECT
Журнал.IDDoc AS [ТекДок $Документ],
Журнал.IDDocDef AS ТекДок_вид,
-- Собираем контрагента из разных таблиц
coalesce($Реализация.Контрагент, $ЗаявкаПокупателя.Контрагент, $КоммПредложение.Контрагент) AS КонтрИд,
-- Получаем периодический реквизит для найденного контрагента
($ПоследнееЗначение.Контрагенты.ВашМенеджер(
coalesce($Реализация.Контрагент, $ЗаявкаПокупателя.Контрагент, $КоммПредложение.Контрагент),
:КонДата
)) AS [ВашМенеджер $Справочник.Проекты]
FROM
_1SJOURN AS Журнал With (NOLOCK)
LEFT JOIN $Документ.Реализация AS Реализация With (NOLOCK) ON Журнал.IDDOC = Реализация.IDDOC
LEFT JOIN $Документ.ЗаявкаПокупателя AS ЗаявкаПокупателя With (NOLOCK) ON Журнал.IDDOC = ЗаявкаПокупателя.IDDOC
-- ... остальные соединения
Разберем несколько критических моментов, которые помогут избежать пустых значений в результате:
ID объекта в макрос $ПоследнееЗначение убедитесь, что вы передаете именно поле из таблицы (например, $Реализация.Контрагент), а не алиас колонки из SELECT, так как SQL обрабатывает SELECT после формирования выборки.$ПоследнееЗначение внутри большого цикла JOIN может замедлить запрос на очень больших объемах данных. В таких случаях иногда выгоднее предварительно отобрать список контрагентов, получить их менеджеров во временную таблицу и затем соединить ее с основным журналом.WITH (NOLOCK) при обращении к таблицам 1С 7.7 в MS SQL. Это исключит взаимные блокировки с активными пользователями, работающими в интерфейсе программы.$ПоследнееЗначение работает только для тех реквизитов, которые помечены как «периодический» в конфигураторе. Для обычных реквизитов следует использовать стандартный LEFT JOIN к таблице справочника.Таким образом, прямой запрос позволяет гибко управлять получением данных, предоставляя производительность, недоступную штатным средствам 1С 7.7, при условии правильного использования механизмов 1С++.