При работе с внешними базами данных через компоненту 1С++ в среде 1С:Предприятие 7.7 разработчики часто используют класс ODBCRecordset. Одной из удобных возможностей этого класса является механизм текстовых параметров, которые позволяют динамически подставлять значения в SQL-запрос. Однако на практике возникает вопрос: как «прочитать» обратно значение, которое мы уже передали в объект с помощью метода УстановитьТекстовыйПараметр? Рассмотрим эту проблему подробно, разберем внутреннюю логику работы компоненты и найдем эффективные способы решения задачи.
Начнем с того, что штатный программный интерфейс (API) класса ODBCRecordset спроектирован по принципу «установил и забыл». Метод УстановитьТекстовыйПараметр записывает значение во внутреннюю коллекцию объекта, но метода для обратного чтения, например ПолучитьТекстовыйПараметр, в стандартной поставке компоненты не существует. Это связано с тем, что текстовые параметры рассматриваются как временные данные, необходимые исключительно для формирования итоговой строки SQL-запроса непосредственно перед его отправкой на сервер.
Проанализируем жизненный цикл параметра. Когда мы вызываем метод УстановитьТекстовыйПараметр("Имя", Значение), компонента сохраняет пару «ключ — значение» во внутреннем хеше. При вызове ВыполнитьИнструкцию() или Открыть() компонента сканирует текст запроса на наличие конструкций вида :Имя и заменяет их на соответствующие значения. Важный нюанс: сразу после выполнения запроса эта коллекция принудительно очищается. Именно об этом предупреждают опытные разработчики: параметры «превращаются в тыкву» сразу после отработки запроса.
Если нам критически важно получить значение параметра именно средствами самого объекта ODBCRecordset, мы можем прибегнуть к небольшой хитрости, предложенной участниками сообщества. Мы можем заставить SQL-сервер вернуть нам этот параметр как результат запроса. Разберем этот пример на коде.
// Создаем объект
RS = CreateObject("ODBCRecordset");
// Устанавливаем текстовый параметр
RS.УстановитьТекстовыйПараметр("МойПараметр", "Привет, 1С!");
// Выполняем пустой запрос, который просто возвращает значение параметра
ТекстЗапроса = "SELECT :МойПараметр";
Результат = RS.ВыполнитьИнструкцию(ТекстЗапроса);
// Теперь значение параметра находится в первой колонке первой строки таблицы результатов
Сообщить("Значение параметра из запроса: " + Результат.ПолучитьЗначение(1, 1));
Рассмотрим подробнее, что происходит в этом коде. Мы подставляем имя параметра непосредственно в секцию SELECT. Компонента 1С++ заменяет :МойПараметр на его текстовое представление, и SQL-сервер выполняет команду типа SELECT 'Привет, 1С!'. В итоге мы получаем таблицу из одной ячейки, содержащую наше значение.
Важное предостережение: данный метод требует полноценного обращения к базе данных. Если вы используете его внутри высоконагруженных циклов, это может привести к снижению производительности из-за лишних сетевых пакетов между 1С и SQL-сервером. Используйте этот способ преимущественно для отладки.
Для профессиональной разработки более правильным подходом будет создание класса-обертки над ODBCRecordset. Это позволит нам не только сохранять значения параметров для чтения, но и расширять функционал объекта под свои нужды. Проанализируем ситуацию: если объект не отдает данные, значит, мы должны сохранить их сами в момент передачи.
Выясним, как реализовать такую обертку упрощенно. Мы можем создать отдельный класс (например, Класс_Запрос) или просто использовать вспомогательный объект СписокЗначений или ИндексированнаяТаблица для кэширования параметров.
// Пример использования вспомогательного списка для хранения параметров
ПараметрыЗапроса = CreateObject("СписокЗначений");
Процедура УстановитьПараметрИСохранить(ОбъектRS, Имя, Значение)
// Устанавливаем в Recordset
ОбъектRS.УстановитьТекстовыйПараметр(Имя, Значение);
// Сохраняем для себя
ПараметрыЗапроса.Установить(Имя, Значение);
КонецПроцедуры
// Теперь мы всегда можем получить значение
ТекЗначение = ПараметрыЗапроса.Получить("НачДата");
Такой подход гарантирует, что даже после того, как ODBCRecordset очистит свою внутреннюю память после выполнения инструкции, у нас останется копия всех параметров, которые были использованы.
Разберем еще один важный технический аспект. В 1С++ существует два способа передачи параметров:
УстановитьТекстовыйПараметр): Работают как макроподстановка текста. Если вы передаете дату, она преобразуется в строку вида 'YYYYMMDD'. Это просто, но менее безопасно и эффективно.ДобПараметр): Используют нативный механизм ODBC (знаки вопроса ? в запросе). Эти параметры передаются в бинарном виде, что исключает ошибки форматирования и защищает от SQL-инъекций.Посмотрим на пример с типизированными параметрами:
RS = CreateObject("ODBCRecordset");
RS.УстановитьТекст("SELECT * FROM СпрТовары WHERE ID = ?");
RS.ДобПараметр(1, 1, 10, ТоварСсылка); // Тип, направление, длина, значение
Результат = RS.Открыть();
В случае с ДобПараметр ситуация с чтением еще сложнее, так как параметры привязываются напрямую к драйверу ODBC. Поэтому использование внешнего хранилища (как в Способе 2) становится единственным надежным вариантом для отслеживания переданных данных.
Часто программисты хотят получить значения параметров, чтобы увидеть итоговый SQL-код, который отправляется на сервер (с уже подставленными значениями дат, ссылок и чисел). Посмотрим, как это можно сделать правильно:
ПолучитьТекстЗапроса(): Позволяет увидеть текущий шаблон запроса. Однако помните, что до выполнения ВыполнитьИнструкцию() параметры в нем могут отображаться как :Имя.Подводя итог, можно сказать: прямого метода чтения параметров из ODBCRecordset нет по причине архитектурных особенностей компоненты. Однако, используя SQL-запрос значения или внешнее кэширование параметров, вы легко сможете контролировать данные, передаваемые в ваши прямые запросы.