При работе с большими объемами данных в 1С:Предприятии 7.7 стандартных механизмов запросов часто становится недостаточно. В таких случаях программисты прибегают к использованию внешних компонент, таких как 1С++, для выполнения прямых SQL-запросов к базе данных. Однако синтаксис прямых запросов существенно отличается от встроенного языка 1С, что часто приводит к ошибкам. Рассмотрим типичную ситуацию: попытку отобрать остатки по регистру, где условие фильтрации накладывается не на само измерение регистра, а на реквизит справочника, который является этим измерением.
Проанализируем ситуацию, когда разработчик пытается выполнить запрос к виртуальной таблице $РегистрОстатки.ОперСклады и наложить условие на реквизит справочника "МестаХранения". Типичная ошибка выглядит так: Meta name parser error: неизвестное метаимя или алиас "$Скл". Выясним причину этой ошибки.
В SQL-базе данных 1С 7.7 таблица регистра остатков содержит только идентификаторы (ID) элементов справочников. Она не содержит полей (реквизитов) этих справочников. Когда вы пишете в условии WHERE $Скл.РазрешенаОтгрузка = :ВыбСклад, препроцессор 1С++ не понимает, откуда взять таблицу $Скл, так как в секции FROM указан только регистр. Для SQL-сервера это выглядит так, будто вы пытаетесь обратиться к столбцу несуществующей таблицы.
Чтобы получить доступ к реквизитам справочника, нам необходимо соединить таблицу остатков регистра с таблицей самого справочника. Рассмотрим, как это сделать структурно. Мы будем использовать конструкцию INNER JOIN, которая объединит только те записи, для которых найдены соответствия в обеих таблицах.
Проанализируем пример корректного связывания:
|FROM
| $РегистрОстатки.ОперСклады as Рег
|INNER JOIN
| $Справочник.МестаХранения as Скл ON Рег.Склад = Скл.id
В этом фрагменте мы указываем SQL-серверу: "Возьми таблицу остатков (алиас Рег) и соедини её с таблицей справочника МестаХранения (алиас Скл) по правилу: значение в поле Склад регистра должно совпадать с уникальным идентификатором ID в справочнике". Только после такого соединения нам станут доступны реквизиты справочника Склад для фильтрации в секции WHERE.
В 1С 7.7 перечисления хранятся в базе данных как специфические строковые идентификаторы. Напрямую сравнивать их в запросе с привычными "Да" или "Нет" нельзя. Однако компонента 1С++ предоставляет удобный метод УстановитьТекстовыйПараметр, который автоматически преобразует объект 1С в понятный для SQL вид.
Рассмотрим пример установки параметра для системного перечисления Булево:
RS = СоздатьОбъект("ODBCRecordset");
RS.УстановитьТекстовыйПараметр("флОтгрузка", Перечисление.Булево.Да);
Важно помнить, что в самом тексте запроса перед именем параметра ставится двоеточие: :флОтгрузка. Препроцессор сам подставит нужный внутренний идентификатор (ID перечисления) вместо этого маркера.
Для того чтобы 1С понимала, что возвращаемые SQL-сервером строки — это не просто наборы символов, а объекты 1С (элементы справочников), мы используем специальную типизацию в секции SELECT. Посмотрим на итоговый пример кода:
ТекстЗапроса = "
|SELECT
| Рег.Товар as [Тов $Справочник.ТМЦ],
| Рег.Склад as [Скл $Справочник.МестаХранения],
| Рег.КолЕдИзм1Остаток as Кол
|FROM
| $РегистрОстатки.ОперСклады as Рег
|INNER JOIN
| $Справочник.МестаХранения as Скл ON Рег.Склад = Скл.id
|WHERE
| $Скл.РазрешенаОтгрузка = :флОтгрузка
|";
RS.УстановитьТекстовыйПараметр("флОтгрузка", Перечисление.Булево.Да);
ТЗ = RS.ВыполнитьИнструкцию(ТекстЗапроса);
Обратите внимание на конструкцию [Тов $Справочник.ТМЦ]. Это указание препроцессору 1С++, что поле Товар нужно преобразовать в объект типа Справочник.ТМЦ при загрузке результата в Таблицу Значений. Без этого вы получите в таблице только внутренние идентификаторы (строки типа " 1ZQ ").
Разберем ситуацию более глубоко. В прямых запросах к 1С++ существует возможность накладывать фильтры прямо в макросе виртуальной таблицы. Это работает быстрее, чем фильтрация через WHERE, так как SQL-сервер будет обсчитывать остатки только по тем данным, которые прошли первичный фильтр.
Сравним два подхода:
Однако, если условие накладывается на реквизит справочника (как в нашем случае — РазрешенаОтгрузка), такой фильтр нельзя просто так засунуть внутрь макроса регистра, так как реквизита нет в таблице итогов регистра. В этом случае INNER JOIN — единственный верный путь.
Проанализируем еще несколько важных моментов, которые помогут ускорить работу ваших прямых запросов:
1. Использование NOLOCK. В среде 1С 7.7 крайне важно не блокировать таблицы во время чтения. Рекомендуется добавлять указание (NOLOCK) после каждой таблицы в секции FROM и JOIN. Это позволит выполнять запрос, не дожидаясь окончания транзакций записи других пользователей.
2. Проблема пустых значений. Помните, что в SQL 1С 7.7 пустая ссылка — это не NULL и не пустая строка "". Это строка, заполненная пробелами (обычно 9 или 13 штук). Чтобы корректно проверить на пустоту, используйте передачу пустого значения через параметр: RS.УстановитьТекстовыйПараметр("ПустойТовар", ПолучитьПустоеЗначение("Справочник.ТМЦ")).
3. Индексы. Проверьте, стоит ли в конфигураторе галочка "Сортировка" у того реквизита, по которому вы делаете отбор (в нашем примере — РазрешенаОтгрузка). Если галочки нет, SQL-серверу придется каждый раз сканировать всю таблицу справочника складов, что на больших справочниках приведет к значительным задержкам.
Таким образом, мы выяснили, что ключ к успеху в прямых запросах 1С 7.7 — это четкое понимание структуры хранения данных в SQL и умелое использование механизмов соединения таблиц и типизации результатов, предоставляемых компонентой 1С++.