При написании прямых запросов к базе данных 1С:Предприятия 7.7 с использованием внешней компоненты 1С++ разработчики часто сталкиваются с сообщением: Meta name parser error: поле таблицы не найдено "$Спр.Заказчик". Разберем детально, почему парсер метаимен выдает такое исключение, как устроен механизм псевдонимов таблиц и как правильно объединять таблицы справочников и документов.
Рассмотрим фрагмент запроса, который привел к ошибке:
FROM
_1SJourn as Жур
INNER JOIN
$Документ.АктОбъемаРабот as Док ON Док.IDDoc = Жур.IDDoc
INNER JOIN
$Справочник.Объект as Спр.Заказчик ON Спр.ID = $Док.Объект
$Справочник.Объект as Спр.Район ON Спр.ID = $Док.Объект
$Справочник.Объект as Спр.ИстФин ON Спр.ID = $Док.Объект
Выясним причину сбоя. В компоненте 1С++ специальный препроцессор (Meta name parser) преобразует макроимена вида $Таблица.Имя и $Алиас.Реквизит в реальные имена таблиц и полей SQL-сервера (например, _1SJOURN, DH1234, SP5678). Логика работы парсера метаимен строго регламентирована:
$Имя1.Имя2 разделяется точкой. При этом Имя1 интерпретируется парсером исключительно как псевдоним (алиас) таблицы, объявленный в предложениях FROM или JOIN, а Имя2 — как идентификатор реквизита из конфигуратора.as Спр.Заказчик. Это в корне нарушает синтаксис как самого SQL, так и транслятора 1С++. Встречая конструкцию $Спр.Заказчик в блоке SELECT или WHERE, парсер ищет таблицу с алиасом Спр и реквизит Заказчик внутри нее. Поскольку алиаса Спр в секции соединений нет (там объявлен некорректный алиас Спр.Заказчик), парсер сообщает, что поле таблицы не найдено.JOIN повторно указаны соединения без ключевого слова INNER JOIN и с условием Спр.ID, где префикс Спр опять же не существует как отдельный источник данных.Исправим структуру запроса. Для каждого соединения одной и той же или разных таблиц необходимо задавать уникальный, простой буквенно-цифровой псевдоним без точек и спецсимволов. Если нам необходимо получить данные из справочника Объект, привязанного к реквизиту шапки документа $Док.Объект, достаточно одного соединения с этим справочником:
ТекстЗапроса = "
|SELECT
| Жур.IDDoc as [Док $Документ],
| Жур.IDDocDef as Док_вид,
| Спр.Code as Код,
| Спр.Descr as Наименование,
| $Док.Объект as [Объект $Справочник.Объект],
| $Док.Сумма as Сумма,
| $Док.ДолгПоОплате as ДолгПоОплате,
| $Док.ПризнакПодпис as [ПризнакПодпис $Перечисление.Признак],
| $Док.ВидАкта as [ВидАкта $Справочник.ВидАкт],
| $Спр.Заказчик as [Заказчик $Справочник.Заказчик],
| $Спр.Район as [Район $Справочник.Области],
| $Спр.ИстФин as [ИстФин $Справочник.ИстФин]
|FROM
| _1SJourn as Жур
|INNER JOIN
| $Документ.АктОбъемаРабот as Док ON Док.IDDoc = Жур.IDDoc
|INNER JOIN
| $Справочник.Объект as Спр ON Спр.ID = $Док.Объект
|WHERE
| Жур.Date_Time_IDDoc BETWEEN :НачДата AND :КонДата~
| AND Жур.Closed & 1 = 1
| AND $Док.ВидАкта <> :ВидАктов
| AND $Док.ДолгПоОплате <> 0
| AND $Спр.Заказчик = :ТекЗаказчик
|";
Разберем, как теперь работает парсер: таблице $Справочник.Объект присвоен простой алиас Спр. В секции SELECT макрос $Спр.Заказчик корректно распознается: парсер берет псевдоним Спр, находит в метаданных справочника Объект реквизит Заказчик и подставляет нужное физическое имя колонки (например, SP123).
Рассмотрим ситуацию, когда необходимо получить данные не только самого реквизита, но и реквизитов связанного справочника (например, если ИстФин — это ссылка на отдельный справочник, из которого нужно вытащить наименование). В этом случае каждое новое соединение должно иметь свой понятный алиас:
|INNER JOIN
| $Справочник.Объект as СпрОбъект ON СпрОбъект.ID = $Док.Объект
|LEFT JOIN
| $Справочник.ИстФин as СпрИстФин ON СпрИстФин.ID = $СпрОбъект.ИстФин
Обратите внимание на синтаксис: мы используем алиасы СпрОбъект и СпрИстФин. Никаких точек в названиях таблиц-источников быть не должно.
Посмотрим на сопутствующую ошибку, о которой упоминалось в ходе отладки. Когда разработчик добавил поле $Спр.ИстФин as [ИстФин $Справочник.ИстФин], выполнение запроса завершилось штатно, но при последующей работе в коде 1С возникло исключение: Поле агрегатного объекта не обнаружено (ТекущийЭлемент).
Проанализируем механику работы типизатора 1С++:
[ИстФин $Справочник.ИстФин] указывает 1С++, что строковый 9-значный идентификатор (ID) из базы данных SQL нужно преобразовать во встроенный тип 1С Справочник.ИстФин.NULL (например, реквизит в объекте не был заполнен), типизатор возвратит не объект контекста справочника, а пустое значение (или пустую ссылку).Выборка.ИстФин.ТекущийЭлемент()) либо колонка таблицы на форме привязана к элементу управления некорректным образом, платформа 1С 7.7 генерирует ошибку отсутствия метода/поля у пустого или ссылочного типа.Для предотвращения подобных ситуаций при выборке ссылочных полей через внешние соединения рекомендуется защищать поля от NULL с помощью SQL-функции ISNULL(), либо получать непосредственно строковое представление данных (код или наименование) прямо в запросе через LEFT JOIN:
|SELECT
| СпрИстФин.Descr as НаименованиеИстФин,
| ISNULL($СпрОбъект.ИстФин, ' ') as [ИстФин $Справочник.ИстФин]
Соблюдение этих правил гарантирует правильную трансляцию метаимен компонентой 1С++ и исключает синтаксические ошибки при работе с базой данных на уровне SQL.