При интеграции систем 1С:Предприятие с внешними базами данных под управлением СУБД Oracle разработчики часто используют технологию ADO (COM-объект ADODB.Command). Это гибкий и быстрый инструмент, однако при его использовании нередко возникают трудночитаемые ошибки взаимодействия. Одна из наиболее распространенных ошибок — ORA-06550 / PLS-00306: ошибочно число или типы аргументов при обращении к хранимой процедуре.
В этой статье мы подробно разберем, почему возникает эта проблема, как перехватить «невидимый» запрос, который 1С отправляет в СУБД Oracle, и как правильно настроить типы данных в коде 1С, чтобы интеграция работала стабильно.
Когда в коде 1С свойству CommandType COM-объекта ADODB.Command присваивается значение 4 (что соответствует константе adCmdStoredProc), это указывает драйверу на вызов хранимой процедуры напрямую. В этом случае драйвер не формирует классический текстовый запрос типа SELECT ... FROM ....
Вместо этого драйвер ODBC/OLE DB упаковывает вызов в удаленный вызов процедуры (RPC) или в анонимный блок PL/SQL, который выглядит примерно так:
begin
MMAST.F1CPK.PAYMENTFM(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10);
end;
Администраторы и разработчики СУБД Oracle не могут увидеть исходный код на стороне 1С и зачастую не могут понять, какие именно данные и в каком формате туда приходят. Ошибка PLS-00306 однозначно говорит о том, что сигнатура вызова со стороны клиента (количество параметров, их последовательность или типы данных) не соответствует описанию процедуры в самой базе данных Oracle. Чтобы разобраться, на каком именно параметре происходит сбой, нам необходимо перехватить передаваемый пакет.
---
Рассмотрим несколько эффективных способов, которые помогут получить точный текст запроса и значения параметров, передаваемых из 1С в СУБД.
Это самый простой способ, который не требует от вас прав администратора в СУБД Oracle и выполняется непосредственно на рабочем компьютере или сервере, где запущена 1С.
odbcad32.exe из папки System32). Если 32-разрядный — запустите 32-битную версию из папки SysWOW64.C:\temp\SQL.LOG). Убедитесь, что у пользователя, под которым запущена 1С, есть права на запись в этот каталог.В созданном лог-файле SQL.LOG будут построчно записаны все обращения к драйверу. Проанализировав его, мы увидим вызовы функций SQLBindParameter и SQLExecute, в которых будут четко видны передаваемые типы и значения параметров.
Если для подключения к базе данных используется полноценный Oracle Client (или Instant Client), мы можем настроить логирование на уровне сетевого уровня Oracle.
Для этого найдите файл конфигурации sqlnet.ora (обычно он находится в каталоге %ORACLE_HOME%\network\admin) и добавьте в него следующие строки:
TRACE_LEVEL_CLIENT = 16
TRACE_FILE_CLIENT = sql_client.trc
TRACE_DIRECTORY_CLIENT = C:\oracle_trace
После выполнения интеграционного кода в указанной папке появится подробный лог сетевого взаимодействия. В нем можно будет найти точный текст анонимного блока PL/SQL, который драйвер отправляет на сервер.
Если разработчики Oracle утверждают, что не могут перехватить запрос, они ошибаются. В СУБД Oracle есть штатные инструменты для отслеживания сессий. Мы можем попросить их включить трассировку конкретно для нашей сессии подключения.
Для этого администратор Oracle должен выполнить следующую последовательность действий:
SID и SERIAL#) в системном представлении V$SESSION.
EXEC DBMS_MONITOR.SESSION_TRACE_ENABLE(session_id => [Ваш_SID], serial_num => [Ваш_SERIAL], binds => TRUE, waits => TRUE);
.trc на сервере СУБД с помощью утилиты tkprof. Там будет виден и текст вызова, и все переданные 1С значения.---
Давайте подробно проанализируем исходный код выгрузки и выясним, почему СУБД Oracle возвращает ошибку PLS-00306 при работе с типами данных.
При вызове хранимых процедур через ADODB параметры связываются строго по порядку (позиционно), а не по их именам. Имя параметра, передаваемое первым аргументом в метод CreateParameter, драйвером Oracle игнорируется. Если в СУБД процедура PAYMENTFM ожидает параметры в одном порядке, а в 1С мы добавляем их методом Append в другом — СУБД вернет ошибку типов данных. Необходимо запросить у разработчиков Oracle точную спецификацию процедуры и проверить последовательность параметров.
Проанализируем конкретные строки создания параметров в исходном коде 1С:
Param5 = Command.CreateParameter("", 7, 1, 4);.
Здесь тип 7 соответствует константе adDate. Однако СУБД Oracle хранит в типе DATE также и время (часы, минуты, секунды). При передаче типа adDate драйвер может некорректно преобразовать дату в строку или передать ее без временной части, что вызывает конфликт. Правильным решением для передачи даты и времени в Oracle является использование типа 135 (adDBTimeStamp).
Для Param1 (идентификатор счета) в коде указан тип 3 (adInteger). Если в Oracle данное поле имеет тип NUMBER с большой разрядностью, стандартный 32-битный Integer может вызвать переполнение или ошибку приведения. Безопаснее использовать тип 131 (adNumeric).
Для сумм (Param7, Param8) указан тип 5 (adDouble). Этот тип представляет числа с плавающей запятой двойной точности. В финансовых операциях СУБД Oracle обычно использует точный тип NUMBER(15,2). Передача double-чисел часто приводит к ошибке PLS-00306 из-за неявного преобразования. Здесь также настоятельно рекомендуется использовать тип 131 (adNumeric).
В коде используется тип 200 (adVarChar) — это строка в кодировке ANSI. Платформа 1С работает исключительно с кодировкой Unicode. Если СУБД Oracle настроена на хранение национальных символов (типы параметров NVARCHAR2), передача ANSI-строки вызовет ошибку. Рекомендуется заменить тип 200 на 202 (adVarWChar), предназначенный для Юникод-строк.
---
С учетом проведенного анализа перепишем фрагмент кода 1С. Мы заменим числовые константы ADO на понятные переменные, исправим типы данных параметров и добавим проверку на пустые значения, которые часто приводят к падению СУБД.
// Определим константы типов данных ADO для наглядности
adInteger = 3;
adDouble = 5;
adDate = 7;
adNumeric = 131;
adDBTimeStamp = 135;
adVarChar = 200;
adVarWChar = 202;
adParamInput = 1;
DataBaseConnection = ПодключениеКОракл();
Command = Новый COMObject("ADODB.Command");
Command.CommandText = "MMAST.F1CPK.PAYMENTFM";
Command.ActiveConnection = DataBaseConnection;
Command.CommandType = 4; // adCmdStoredProc
// Создаем параметры с корректными типами данных
// Важно: порядок добавления должен строго соответствовать порядку аргументов в СУБД Oracle!
Param1 = Command.CreateParameter("p_id_schet", adNumeric, adParamInput); // NUMBER в Oracle
Param2 = Command.CreateParameter("p_doc_uid", adVarWChar, adParamInput, 36); // VARCHAR2(36)
Param3 = Command.CreateParameter("p_doc_num", adVarWChar, adParamInput, 20); // VARCHAR2(20)
Param4 = Command.CreateParameter("p_source", adVarWChar, adParamInput, 25); // VARCHAR2(25)
Param5 = Command.CreateParameter("p_pay_date", adDBTimeStamp, adParamInput); // DATE в Oracle (с временем)
Param6 = Command.CreateParameter("p_currency", adVarWChar, adParamInput, 20); // VARCHAR2(20)
Param7 = Command.CreateParameter("p_sum_cur", adNumeric, adParamInput); // NUMBER (валютная сумма)
Param8 = Command.CreateParameter("p_sum_rub", adNumeric, adParamInput); // NUMBER (рублевая сумма)
Param9 = Command.CreateParameter("p_clear_flag", adInteger, adParamInput); // NUMBER / INTEGER
Param10 = Command.CreateParameter("p_invoice_uid", adVarWChar, adParamInput, 36); // VARCHAR2(36)
// Настраиваем точность для числовых параметров (для типа adNumeric это критично)
Param1.Precision = 15;
Param1.NumericScale = 0;
Param7.Precision = 15;
Param7.NumericScale = 2; // 2 знака после запятой
Param8.Precision = 15;
Param8.NumericScale = 2; // 2 знака после запятой
// Добавляем параметры в коллекцию команды
Command.Parameters.Append(Param1);
Command.Parameters.Append(Param2);
Command.Parameters.Append(Param3);
Command.Parameters.Append(Param4);
Command.Parameters.Append(Param5);
Command.Parameters.Append(Param6);
Command.Parameters.Append(Param7);
Command.Parameters.Append(Param8);
Command.Parameters.Append(Param9);
Command.Parameters.Append(Param10);
// Заполняем значениями с предварительной проверкой типов
Param1.Value = ?(ЗначениеЗаполнено(ИдСчета), Число(ИдСчета), NULL);
Param2.Value = Строка(ДокументОплаты.УникальныйИдентификатор());
Param3.Value = ?(ЭтоБанк, СокрЛП(ДокументОплаты.НомерВходящегоДокумента), СокрЛП(ДокументОплаты.Номер));
Param4.Value = СокрЛП(ИсточникПоступления);
// Работа с пустой датой 1С (01.01.0001 вызывает ошибку в Oracle)
Если ЗначениеЗаполнено(ДатаОплаты) Тогда
Param5.Value = ДатаОплаты;
Иначе
Param5.Value = NULL;
КонецЕсли;
Param6.Value = СокрЛП(КодВалюты);
Param7.Value = Число(СуммаВВалюте);
Param8.Value = Число(Сумма);
Param9.Value = Число(Очищать);
Param10.Value = Строка(ДокументСчет.УникальныйИдентификатор());
// Выполняем процедуру внутри безопасного блока транзакции
Попытка
Command.Execute();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение "Ошибка выполнения операции в СУБД Oracle: " + ОписаниеОшибки();
КонецПопытки;
Для успешной интеграции 1С и СУБД Oracle с использованием COM-технологии ADO всегда придерживайтесь трех главных правил:
Command.Parameters должны добавляться в том же порядке, в котором они объявлены в сигнатуре процедуры СУБД Oracle.adDBTimeStamp (135) для дат, adNumeric (131) для точных расчетов и сумм, а также adVarWChar (202) для строковых данных в формате Unicode.01.01.0001) или пустые ссылки не должны передаваться напрямую. Преобразовывайте их в NULL или делайте проверку на стороне 1С перед отправкой пакета.