При работе с платформой 1С:Предприятие 7.7 программисты часто сталкиваются с ограничениями штатных средств при работе с большими объемами данных. Для ускорения формирования отчетов и обработки информации традиционно используются прямые запросы к SQL-серверу через внешнюю компоненту 1C++ (объект ODBCRecordset). Однако синтаксис прямых запросов значительно отличается от стандартного языка запросов 1С, что порождает специфические ошибки. Одной из самых распространенных является ошибка «Недопустимое имя столбца» (Invalid column name) при обращении к ресурсам регистров.
В рамках данной статьи мы подробно разберем, почему возникает эта ошибка, как работает механизм парсинга имен в 1С++ и как правильно составлять запросы к виртуальным таблицам остатков и оборотов.
Рассмотрим ситуацию, представленную в вопросе автора. Программист пытается получить данные из регистра остатков «ОперСклады» следующим образом:
RS = СоздатьОбъект("ODBCRecordset");
RS.УстБД1С();
ТекстЗапроса = "
|SELECT
| Рег.Товар as [Товар $Справочник.ТМЦ],
| Рег.КолЕдИзм1 as Количество
|FROM
| $РегистрОстатки.ОперСклады as Рег";
Несмотря на то что в конфигураторе ресурс действительно называется КолЕдИзм1, система выдает ошибку. Проанализируем причину. Когда мы используем макрос $РегистрОстатки, компонента 1С++ подменяет это выражение на сложный SQL-запрос к реальным таблицам базы данных (таблицам итогов RG* и движений RA*). В этих физических таблицах SQL поля называются иначе (например, SP123), и макрос должен знать, как именно вы хотите интерпретировать ресурс.
Главная особенность виртуальных таблиц в 1С++ заключается в том, что к именам ресурсов обязательно нужно добавлять суффикс, определяющий тип данных, которые вы хотите получить. Для таблицы остатков этим суффиксом является слово Остаток.
Разберем правила именования полей для разных виртуальных таблиц:
$РегистрОстатки): К имени каждого ресурса необходимо добавлять суффикс Остаток. В нашем примере поле должно называться КолЕдИзм1Остаток.$РегистрОбороты): Здесь система предоставляет больше возможностей, и вы должны явно указать, какой тип оборота вам нужен:
ИмяРесурсаПриход — сумма всех приходных движений за период;ИмяРесурсаРасход — сумма всех расходных движений за период;ИмяРесурсаОборот — разница между приходом и расходом.Исправленный фрагмент кода будет выглядеть так:
ТекстЗапроса = "
|SELECT
| Рег.Товар as [Товар $Справочник.ТМЦ],
| Рег.КолЕдИзм1Остаток as Количество
|FROM
| $РегистрОстатки.ОперСклады as Рег";
Выясним причину другой частой ошибки, упомянутой в обсуждении: «Meta name parser error: неизвестное метаимя или алиас $Рег». Она возникает, когда программист пытается поставить знак доллара перед алиасом (псевдонимом) таблицы.
Важно запомнить правило: символ $ используется только для обращения к объектам метаданных 1С (справочникам, регистрам, перечислениям) или специальным макросам. Псевдоним таблицы (алиас), который вы определили в секции FROM (например, as Рег), является чистым именем SQL и не требует префикса $ при обращении к полям.
Неправильно: $Рег.КолЕдИзм1
Правильно: Рег.КолЕдИзм1Остаток
Чтобы лучше понимать, почему нужны такие сложности, проанализируем внутреннее устройство базы данных 1С 7.7 на SQL. Остатки не хранятся в одной таблице на каждую секунду времени. Они хранятся в:
RG*) — на начало каждого месяца.RA*) — записи о каждом изменении (приход/расход).Когда вы вызываете макрос $РегистрОстатки, 1С++ выполняет следующие действия:
RA, произошедшие между точкой итогов и вашей датой.Именно поэтому использование виртуальных таблиц гораздо эффективнее, чем попытка самостоятельно «склеивать» таблицы RG и RA в своем SQL-запросе.
Рассмотрим, как сделать наш запрос профессиональным и быстрым. Нам часто требуется получить остатки не по всем товарам, а на конкретную дату или по конкретному складу. Макрос $РегистрОстатки поддерживает передачу параметров прямо «внутрь» себя.
Синтаксис макроса: $РегистрОстатки.ИмяРегистра(:Дата, , (СписокИзмерений), (СписокРесурсов), )
Посмотрим на пример продвинутого использования:
// Устанавливаем дату, на которую нужны остатки
RS.УстановитьТекстовыйПараметр("ВыбДата", ДатаКонцаПериода);
ТекстЗапроса = "
|SELECT
| Рег.Товар as [Товар $Справочник.ТМЦ],
| Рег.Склад as [Склад $Справочник.Склады],
| Рег.КолЕдИзм1Остаток as Количество
|FROM
| $РегистрОстатки.ОперСклады(:ВыбДата~, , (Товар, Склад), (КолЕдИзм1), ) as Рег
|WHERE
| Рег.КолЕдИзм1Остаток <> 0"; // Отсекаем нулевые остатки
В этом примере мы добавили несколько важных элементов:
:ВыбДата~ — передача даты в макрос. Символ ~ (тильда) часто используется для корректной обработки границ периодов.(Товар, Склад) — указывает SQL-серверу, что группировку нужно делать именно по этим полям.(КолЕдИзм1) — ограничивает выборку только нужным ресурсом, что ускоряет запрос.Проанализируем конструкцию as [Товар $Справочник.ТМЦ]. Это мощный инструмент 1С++, который решает проблему типизации. В SQL-таблицах ссылки хранятся в виде 9-символьных или 13-символьных идентификаторов (ID). Если вы просто выберете поле Товар, вы получите строку вида " 2Z ". Инструкция $Справочник.ТМЦ заставляет 1С++ автоматически конвертировать этот ID в объект 1С, который потом можно будет открыть в форме или использовать в методах 1С.
Однако помните: если вам нужно просто посчитать сумму или количество, типизация не обязательна и даже вредна для производительности. Если же данные выводятся в ТаблицуЗначений для пользователя — без типизации не обойтись.
Для успешного написания прямых запросов к регистрам 1С 7.7 следуйте этим правилам:
Остаток к именам ресурсов в $РегистрОстатки.$ перед псевдонимами таблиц.WHERE.Применяя эти знания, вы сможете создавать высокопроизводительные отчеты, которые будут работать в десятки раз быстрее стандартных запросов 1С 7.7.