При переходе на прямые запросы с использованием внешней компоненты 1С++ и класса ODBCRecordset разработчики периодически сталкиваются с синтаксическими ошибками СУБД MS SQL Server. Особенно остро проблемы проявляются при выгрузке списков отбора во временные таблицы и при работе с виртуальными таблицами регистров. Рассмотрим по шагам распространенные ошибки, выясним их причины и проанализируем правильные варианты построения запросов.
Частая ошибка при попытке передать список объектов (например, список контрагентов или номенклатуры) во временную таблицу выглядит следующим образом:
Internal error: State 42000, native 156, message [Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near the keyword 'table'.
Проанализируем типовую ошибочную строку кода:
RS.УложитьСписокОбъектов(спсКлиенты, "#Klienti", "Контрагенты");
Причина возникновения ошибки заключается в неправильной передаче второго аргумента. Метод УложитьСписокОбъектов (или PutObjectList) принимает три параметра:
"#Klienti") нельзя.Разберем правильный вызов метода:
ВрТаблГрупп = "";
RS.УложитьСписокОбъектов(спсКлиенты, ВрТаблГрупп, "Контрагенты");
После вызова переменная ВрТаблГрупп будет содержать реальное сгенерированное имя временной таблицы в базе данных (например, #vt_1_12345), которое затем необходимо подставить в текст SQL-запроса.
После того как временная таблица сформирована компонентой, мы используем ее в основном запросе. Временная таблица содержит колонку Val, в которой хранятся идентификаторы объектов (9-символьные строки ID). Посмотрим на пример фильтрации документов:
ВрТаблГрупп = "";
RS.УложитьСписокОбъектов(ВыбКонтр, ВрТаблГрупп, "Контрагенты");
ТекстЗапроса = "
|SELECT
| Док.IDDoc as [Док $Документ.Счет],
| $Док.Контрагент as [Контрагент $Справочник.Контрагенты]
|FROM
| $Документ.Счет as Док
|WHERE
| $Док.Контрагент IN (SELECT Val FROM " + ВрТаблГрупп + ")
|";
табЗапрос = RS.ВыполнитьИнструкцию(ТекстЗапроса);
Если в запросе используется явное соединение JOIN, важно корректно указывать псевдонимы (алиасы) таблиц для полей соединения во избежание неоднозначности и ошибок сопоставления. Рассмотрим подробнее:
|FROM $РегистрОстаткиОбороты.ВзаиморасчетыПокупателей(...) AS Рег
|INNER JOIN " + ВрТаблГрупп + " AS ФильтрКлиенты ON ФильтрКлиенты.Val = Рег.Контрагент
При передаче дат в виртуальные таблицы регистров разработчики часто сталкиваются с ошибкой:
Meta name parser error: это метаимя разрешить невозможно "$РегистрОстаткиОбороты.ИмяРегистра" State 42000, native 137, message [Microsoft][ODBC SQL Server Driver][SQL Server]Необходимо объявить скалярную переменную "@_CP_2".
Выясним причину этой проблемы. В синтаксисе метапарсера 1С++ суффикс тильды ~ (например, :ДатаКонца~) означает смещение границы периода на конец дня (23:59:59). Метапарсер автоматически генерирует для таких конструкций внутренние переменные контекста с префиксом @_CP_.
Если параметр устанавливается методом SetTextParam, происходит простая текстовая подстановка значения в текст команды до работы метапарсера, и внутренняя переменная DECLARE @_CP_... не формируется. Чтобы избежать сбоя, необходимо соблюдать следующие правила:
~) используем строго метод УстПараметр (или SetParameter), а не SetTextParam.RS.УстПараметр("ДатаКонца", Дата2); связывается в тексте как :ДатаКонца~.YYYYMMDD через SetTextParam, рассчитывая конец дня вручную.При выполнении запросов к виртуальным таблицам итогов на старых версиях СУБД (MS SQL Server 7.0) может возникать ошибка:
State 42000, native 2750, message [Microsoft][ODBC SQL Server Driver][SQL Server]Column or parameter: Specified column precision 38 is greater than the maximum precision of 28.
В MS SQL Server 7.0 максимальная точность для типов NUMERIC и DECIMAL по умолчанию составляет 28 знаков (в отличие от MS SQL 2000 и новее, где предел равен 38). Метапарсер 1С++ для предотвращения переполнения при расчете сумм и остатков по умолчанию разворачивает виртуальные таблицы с точностью 38 разрядов.
Разберем способы обхода этого ограничения:
-p38. Этот ключ переключает сервер на поддержку 38-значной точности вычислений. После перезапуска службы ошибка 2750 исчезает.$РегистрОстаткиОбороты строим собственный запрос через объединение (UNION ALL) физической таблицы итогов ($РегистрИтоги.ИмяРегистра) и таблицы движений ($Регистр.ИмяРегистра). Это дает полный контроль над типами данных и разрядностью полей.Применение корректного синтаксиса вызова УложитьСписокОбъектов, правильная типизация параметров через УстПараметр и учет ограничений разрядности числовых полей СУБД обеспечивают стабильную работу прямых запросов 1С++ на любых конфигурациях SQL Server.