Как получить ссылку на документ и значения реквизитов регистра в прямых запросах 1С 7.7

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

Работа с прямыми SQL-запросами в 1С:Предприятии 7.7 через внешнюю компоненту 1C++ открывает огромные возможности по ускорению отчетов, но вместе с тем подбрасывает разработчику ряд нетривиальных задач. Одной из самых частых сложностей является получение ссылки на документ (объекта типа "Документ") из виртуальных таблиц остатков и оборотов, а также доступ к дополнительным реквизитам регистра, которые не входят в состав измерений или ресурсов. В рамках этой статьи мы подробно разберем, как правильно типизировать поля запроса, почему виртуальные таблицы "не видят" реквизиты и как оптимизировать такие запросы для высокой производительности.

Проблема первая: Типизация документов и ошибка типизирующего поля

При попытке выбрать документ из виртуальной таблицы $РегистрОстаткиОбороты начинающие разработчики часто используют конструкцию RIGHT(Рег.ПозицияДокумента, 9). Однако при попытке присвоить этому полю алиас типа [Документ1 $Документ] возникает ошибка: "Для типизации поля "Документ1", не найдено дополнительное типизирующее поле". Разберем по шагам, почему это происходит.

В архитектуре 1С 7.7 на SQL-сервере ссылка на документ не является единым значением. Она состоит из двух составляющих:

  1. IDDoc — уникальный 9-символьный идентификатор документа в рамках его таблицы.
  2. IDDocDef (Вид документа) — 4-символьный код, определяющий метаданные документа (например, что это "ПриходнаяНакладная" или "РасходныйОрдер").

Поле ПозицияДокумента в виртуальной таблице — это составная строка длиной 23 символа (Дата + Время + IDDoc). В ней содержится уникальный идентификатор, но отсутствует информация о виде документа. Чтобы типизатор 1C++ смог корректно собрать объект "Документ", ему нужно передать идентификатор вида документа в специальное поле с суффиксом _вид.

Проанализируем правильный вариант выборки документа, предоставленный в обсуждении:


|SELECT                     
|  Рег.Товар as [Товар $Справочник.Товары],
|  RIGHT(Рег.ПозицияДокумента, 9) as [Документ1 $Документ], 
|  Рег.ВидДокумента as [Документ1_вид],
|  Рег.ОстатокТовараПриход as ДокКоличествоПриход
|FROM
|  $РегистрОстаткиОбороты.ОстаткиТоваров(:НачДата, :КонДата~, Документ) as Рег

Обратим внимание: поле Рег.ВидДокумента автоматически предоставляется виртуальной таблицей, если в параметре Периодичность указано значение Документ. Название алиаса для вида документа обязательно должно соответствовать шаблону: [ИмяОсновногоПоля_вид] в нижнем регистре.

Проблема вторая: Доступ к реквизитам регистра

Часто возникает ситуация, когда в отчете по движениям нужно вывести не только товар и количество, но и, например, КодОперации или Автор. Выясним причину, по которой прямой запрос к виртуальной таблице $РегистрОстаткиОбороты.ИмяРегистра(...) выдает ошибку "Invalid column name" при обращении к реквизиту.

Рассмотрим внутреннее устройство регистра остатков. Данные хранятся в двух физических таблицах:

Виртуальная таблица $РегистрОстаткиОбороты — это сложный программный механизм, который агрегирует данные из итогов и движений. Поскольку реквизиты регистра не участвуют в расчете итогов (они не суммируются), их просто нет в этой "виртуальной проекции".

Чтобы получить реквизит, нам необходимо выполнить соединение (JOIN) виртуальной таблицы с физической таблицей движений регистра (RA). Посмотрим, как это реализовано в работающем примере:


|SELECT                     
|  Рег.Товар as [Товар $Справочник.Товары],
|  RIGHT(Рег.ПозицияДокумента, 9) as [Документ1 $Документ], 
|  Рег.ВидДокумента as [Документ1_вид], 
|  $РегП.КодОперации as КодОперации,
|  Рег.ОстатокТовараПриход as ДокКоличествоПриход
|FROM
|  $Регистр.ОстаткиТоваров as РегП (nolock)  
|Inner Join
|  $РегистрОстаткиОбороты.ОстаткиТоваров(:НачДата, :КонДата~, Документ) as Рег On
|        РегП.IdDoc = RIGHT(Рег.ПозицияДокумента, 9) 
|        and $РегП.Товар = Рег.Товар 

Важный нюанс: при обращении к полям физической таблицы регистра (алиас РегП) необходимо использовать префикс $ перед именем поля: $РегП.КодОперации. Это связано с тем, что парсер 1C++ должен заменить метаимя реквизита на реальное имя колонки в SQL (например, SP123).

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

Хотя соединение через INNER JOIN по функции RIGHT() является рабочим, оно может быть медленным на больших объемах данных. SQL-сервер не может эффективно использовать индексы, когда условие соединения содержит функции преобразования строк.

Проанализируем более профессиональный подход, предложенный участниками форума — использование UNION ALL. Если вам нужен отчет, содержащий и начальные остатки, и детальные движения с реквизитами, и конечные остатки, лучше разделить запрос на три части:

  1. Начальный остаток: выбираем из $РегистрОстатки.ИмяРегистра(:НачДата).
  2. Движения: выбираем напрямую из таблицы $Регистр.ИмяРегистра (RA-таблица). Здесь у нас есть прямой доступ ко всем реквизитам, IDDoc и IDDocDef без лишних преобразований строк.
  3. Конечный остаток: выбираем из $РегистрОстатки.ИмяРегистра(:КонДата~).

Затем все части объединяются через UNION ALL и группируются. Такой подход позволяет SQL-серверу использовать индексы таблиц итогов и движений максимально эффективно.

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

Для успешной работы с документами и реквизитами в прямых запросах 1С 7.7 придерживайтесь следующих правил:

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

← На главную