Технические вопросы
Разработка

Возвращаемое значение хранимой процедуры Oracle.

ЕСть вот такое определение ХП в БД Oracle:

FUNCTION f_add(in_site_type_id IN INTEGER,
               in_name IN VARCHAR2,
               in_desc IN VARCHAR2 DEFAULT NULL,
               in_adress_id IN INTEGER DEFAULT NULL,
               in_developer_id IN INTEGER,
               in_owner_id IN INTEGER,
               in_management_id IN INTEGER,
               in_site_status_id IN INTEGER,
               in_comments IN VARCHAR2 DEFAULT NULL,
               in_id IN INTEGER DEFAULT NULL) RETURN INTEGER;

каким образом ее вызвать из скрипта TSCRM чтобы получить возвращаемое значение?

Нравится

53 комментария

SSV, можно создать SelectQuery на таблицу с одной записью (tbl_DatabaseInfo) где создать CustomSQL колонку и там вызвать Вашу функцию. В итоге должно получиться что-то типа такого:
[sql]
SELECT
f_add(:pin_site_type_id, ...) "Column1"
FROM
"tbl_DatabaseInfo"
[/sql]
Получить значение можно так:
[javascript]
...
var sq = Services.GetNewItemByUSI("f_addQuery");
sq.Parameters.ItemsByName("in_site_type_id").Value = 1;
...
var ds = sq.Open();
Log.Write(1, ds.ValAsInt("Column1"));
...
[/javascript]

Писал по памяти, без проверки, так что если будут ошибки компиляции, думаю подправите :)

Спасибо, Александр, но после Вашего ответа я понял, что некорректно задал свой вопрос. Дело в том, что CRM работает с MS SQL сервером, а процедура должны вызываться из БД Oracle. Подозреваю, что в данном случае SelectQuery не сможет подключиться к серверу Oracle. Это нужно для синхронизации неких данных в двух БД.

Можно попробовать через ADOQuery или создать linked-server на Oracle и попробовать вызвать функцию.

Именно ADO я и хотел использовать. Вот и возник основной вопрос - как с помощью ADO получить возвращаемое значение?

Точно так же как я описал в первом посте. Только можно делать выборку не с tbl_DatabaseInfo, а из DUAL.

Что то ничего не получается. Не проходит в SelectQuery запрос вида:

[sql]
SELECT
TestOut(1,0) as "Column1"
FROM
"tbl_DatabaseInfo"
[/sql]

создал тестовую процедуру на MS SQL и попытался выполнить, пишет "'TestOut' is not a recognized built-in function name"...

Вы не указали owner для функции и не заменили кавычки на квадратные скобки. Попробуйте так:
[sql]
SELECT
dbo.TestOut(1,0) AS [Column1]
FROM
[tbl_DatabaseInfo]
[/sql]
Только как это Вам поможет? У Вас же функция на Oracle :)

"SSV" написал:создал тестовую процедуру на MS SQL

Процедуру или функцию?

"Cannot find either column "dbo" or the user-defined function or aggregate "dbo.TestOut", or the name is ambiguous"

Сначала сделал процедуру с OUTPUT параметром - не получилось. Переделал в функцию - получилось. Спасибо.

[javascript]
var SiteBalDataset = Services.GetNewItemByUSI('ADOC_Site');
SiteBalDataset.SQLText = 'select f_add("'+pNameSite+'"'+','+'"'+pAddressSite+'") as IDBalSys from DUAL';
SiteBalDataset.Open;
var IDBalSys = SiteBalDataset.ValAsFloat('IDBalSys');
[/javascript]

На последней строке выдает ошибку - "Объект не поддерживает это свойство или метод."
В чем может быть дело?

"SSV" написал:SiteBalDataset.Open;

А так?
[javascript]
var ds = SiteBalDataset.Open();
var IDBalSys = ds.ValAsFloat('IDBalSys');
[/javascript]

"SSV" написал:SiteBalDataset.SQLText = 'select f_add("'+pNameSite+'"'+','+'"'+pAddressSite+'") as IDBalSys

Никогда так не делайте, с этим много проблем. Используйте параметры.

[javascript]
var SiteBalDataset = Services.GetNewItemByUSI('ADOC_Site');
SiteBalDataset.Parameters.ItemsByName('pNameSite').ValAsStr = pNameSite;
SiteBalDataset.Parameters.ItemsByName('pAddressSite').ValAsStr = pAddressSite;
var ds = SiteBalDataset.Open();
var IDBalSys = ds.ValAsFloat('IDBalSys'); //SiteBalDataset.ValAsFloat('IDBalSys');
[/javascript]

Та же самая ошибка на строке - [javascript]var ds = SiteBalDataset.Open();[/javascript]

SSV, а что такое ADOC_Site? ADODataSet? У него есть ADOCommand?
Если это ADODataSet, то тогда Ваш код подходит больше:
[javascript]
var SiteBalDataset = Services.GetNewItemByUSI('ADOC_Site');
SiteBalDataset.Parameters.ItemsByName('pNameSite').ValAsStr = pNameSite;
SiteBalDataset.Parameters.ItemsByName('pAddressSite').ValAsStr = pAddressSite;
SiteBalDataset.Open(); // Вызвать метод
var IDBalSys = SiteBalDataset.ValAsFloat('IDBalSys');
[/javascript]

SSV, а какой у Вас запрос сейчас? И какой провайдер Вы используете при подключении к Oracle?

Даже не знаю почему я использовал ADOCommand... ADOC_Site был типа ADOCommand, сейчас передела в ADODataSet, стало что-то получаться. Спасибо.

Запрос - [sql]select sites_pkg.f_add(:pNameSite,:pAddressSite) as IDBalSys from DUAL[/sql]

Провайдер - MSDAORA.1

Что интересно - средствами Oracle выполнить этот запрос не получается - ругается на команду Insert в теле функции, а из скрипта CRM выполняется, но пишет, что "Object 'DataField IDBalSys' is not assigned"...

"SSV" написал:[sql]
SELECT sites_pkg.f_add(:pNameSite,:pAddressSite) AS IDBalSys FROM DUAL
[/sql]

А Вы выполняли его в какой либо утилите по работе с Oracle? Он работает? Возвращает данные для Ваших параметров? Попробуйте еще указать имя колонки IDBalSys в кавычках - "IDBalSys"

"SSV" написал:Что интересно - средствами Oracle выполнить этот запрос не получается - ругается на команду Insert в теле функции, а из скрипта CRM выполняется, но пишет, что "Object 'DataField IDBalSys' is not assigned"...

Все правильно, набор данных ничего не вернул, поэтому и ругается...

Мда... А есть еще способы выполнить из CRM функцию Oracle и вернуть результат ее работы?

Есть :) Исправить ошибку в функции sites_pkg.f_add.

Тут даже не в том проблема что ничего не вернул запрос, а в том, запрос некорректен.

Каким то образом добились выполнения запроса средствами Oracle. Из CRM так и не выполняется - "Object 'DataField IDBalSys' is not assigned".

А в дебаггере проверяли, DataFields у SiteBalDataset содержит элементы?
Попробуйте еще так:
[javascript]
...
var IDBalSys = SiteBalDataset.DataFields.Items[0].Value;
...
[/javascript]

"Осауленко Александр" написал:
[javascript]
var IDBalSys = SiteBalDataset.DataFields.Items[0].Value;
[/javascript]

"Ошибка выполнения Microsoft JScript: Недопустимое число аргументов или присвоение значения свойства"

Ну так у DataSet нет DataField. Вы их в дизайнере добавили?

"Осауленко Александр" написал:
Ну так у DataSet нет DataField. Вы их в дизайнере добавили?

:sad:
это что-то меня совсем переклинило. Теперь добавил, всё проходит без видимых ошибок, но такое впечатление, что ничего не происходит - функция запись не добавляет и возвращает 0...

Наверное у Вас функция все-таки не корректна... Или параметры не те приходят. Тут только отлаживать функцию необходимо на стороне Oracle.

Попробовал помониторить с помощью Statement Tracer for Oracle, получил следующую ошибку:

Class: Error message
Time stamp: 13/08/2010 14:15:09
SQL statement: ORA-06503: PL/SQL: Возврат функции без значения
ORA-06512: на "NSI.SITES_PKG", line 63
ORA-06512: на line 1

Опубликуйте код создания пакета.

[sql]
FUNCTION f_add(in_site_type_id IN INTEGER DEFAULT NULL,
in_name IN VARCHAR2,
in_desc IN VARCHAR2 DEFAULT NULL,
in_adress_id IN INTEGER DEFAULT NULL,
in_developer_id IN INTEGER DEFAULT NULL,
in_owner_id IN INTEGER DEFAULT NULL,
in_management_id IN INTEGER DEFAULT NULL,
in_site_status_id IN INTEGER DEFAULT NULL,
in_comments IN VARCHAR2 DEFAULT NULL,
in_id IN INTEGER DEFAULT NULL) RETURN INTEGER AS PRAGMA AUTONOMOUS_TRANSACTION;
v_id INTEGER;
v_error VARCHAR2(4000);
BEGIN
IF in_id IS NULL THEN
SELECT MAX(id)+1
INTO v_id
FROM sites;
ELSE
v_id:=in_id;
END IF;
INSERT INTO sites
VALUES
(v_id,
in_site_type_id,
in_name,
in_desc,
in_adress_id,
in_developer_id,
in_owner_id,
in_management_id,
in_site_status_id,
in_comments);
COMMIT;
RETURN v_id;
EXCEPTION
WHEN OTHERS THEN
v_error:=SQLERRM(SQLCODE);
INSERT INTO test_s VALUES (SYSDATE,'ERROR:'||v_error); COMMIT;
END;

[/sql]

А что вернется в случае ошибки?

"SSV" написал:SELECT MAX(id)+1
       INTO v_id
       FROM sites;

Это конечно Вы зря. А почему не последовательности?

Дело в том, что я не работаю с Oracle, функции пишет девочка. Меня тоже удивил этот кусочек кода. Я попросил упростить функцию - сократить количество параметров до двух самых необходимых - in_name и in_desc. В случае ошибки сейчас что-то пишется в таблицу test_s, что именно еще нужно выяснить у девочки.

[sql]
FUNCTION add2(in_name IN VARCHAR2,
in_desc IN VARCHAR2 DEFAULT NULL) RETURN INTEGER AS PRAGMA AUTONOMOUS_TRANSACTION;
v_id INTEGER;
v_error VARCHAR2(4000);
BEGIN
SELECT MAX(id)+1
INTO v_id
FROM sites;
INSERT INTO sites (id,name,description)
VALUES
(v_id,
in_name,
in_desc);
COMMIT;
RETURN v_id;
EXCEPTION
WHEN OTHERS THEN
v_error:=SQLERRM(SQLCODE);
INSERT INTO test_s VALUES (SYSDATE,'ERROR:'||v_error);
COMMIT;
END;
[/sql]

Теперь Statement Tracer for Oracle выдает такую ошибку:

Class: Error message
Time stamp: 16/08/2010 07:45:12
SQL statement: ORA-01722: неверное число

Только сейчас заметил, что изменилось имя функции...

[javascript]
var SiteBalDataset = Services.GetNewItemByUSI('ADOC_Site');
SiteBalDataset.Parameters.ItemsByName('pin_name').ValAsStr = pNameSite;
SiteBalDataset.Parameters.ItemsByName('pin_desc').ValAsStr = pAddressSite;
SiteBalDataset.Open();
var IDBalSys = SiteBalDataset.ValAsFloat('IDBalSys');
[/javascript]

на строке SiteBalDataset.Open(); выдает ошибку:

"TSADOEngineLibrary.ADODataset: Не удается вновь задать положение в наборе строк"

Может в ADODataSet нужно что-то поменять в настройках курсора?

SSV,
1. Вам необходимо обязательно уйти от SELECT MAX - это не подход к клиент-серверным базам
2. Вернуть какое-то число в случае ошибки

Какая структура таблиц sites и test_s?

"SSV" написал:"TSADOEngineLibrary.ADODataset: Не удается вновь задать положение в наборе строк"

Ну так у Вас же функция еще не скомпилированная, как Вы можете делать выборку из нее?

У меня нет полного доступа к БД Oracle, но предполагаю, что структура sites идентична параметрам функции f_add. Каких либо реляционных связей с другими таблицами нет. Таблица test_s, опять же судя по коду в функции состоит из двух полей - дата и строка, она создавалась только для проверки данной функции.
Не совсем понял про не скомпилированную функцию. Почему, в таком случае, такая ошибка не всплывала с функцией f_add?

А Вы тестировали add2 на Oracle? Она рабочая?

Девочка тестировала. Говорит - работает. Да там, на мой взгляд, и работать то особо нечему.

У Вас есть какая-то утилита для работы с Oracle? TOAD, PL/SQL Developer? Можете там выполнить запрос, который Вы запускаете из Terrasoft?

Скажите разработчику функции:
1. MAX это не подход и это надо обязательно переписать
2. Вернуть значение в случае ошибки. Сейчас Функция "глушит" все ошибки и ничего не возвращает.

Запрос, который выполняет CRM, работает в Oracle. Более того - при выполнении метода SiteBalDataset.Open(); данные добавляются в таблицу, но всё равно в CRM вываливается ошибка - "TSADOEngineLibrary.ADODataset: Не удается вновь задать положение в наборе строк" и соответственно строка - var IDBalSys = SiteBalDataset.ValAsFloat('IDBalSys'); не выполняется и никакого возвращаемого значения я не получаю.

А Вы как-то меняли ADODaatSet - настройки курсора и т.п.?

Если у Вас серверный курсор, попробуйте клиентский.

Нет, настройки курсора пока не менял.

"Осауленко Александр" написал:Если у Вас серверный курсор, попробуйте клиентский.

Вот! Теперь получилось! Большое спасибо, Александр!

Если можно еще один вопрос:
как присвоить полю SiteBalDataset.Parameters.ItemsByName('pin_site_type_id').ValAsInt значение NULL?
По простому SiteBalDataset.Parameters.ItemsByName('pin_site_type_id').ValAsInt = null; не проходит, пишет о несоответствии типов.

Если параметры не типизировать, то значение вроде бы присваивается -
[javascript]
SiteBalDataset.Parameters.ItemsByName('pin_site_type_id').Value = null;
[/javascript]

но при этом, при попытке выполнить метод Open(), вылезает ошибка - "TSADOEngineLibrary.ADODataset: Неправильно определен объект Parameter. Предоставлены несовместимые или неполные сведения".

"SSV" написал:как присвоить полю SiteBalDataset.Parameters.ItemsByName('pin_site_type_id').ValAsInt значение NULL?

Посмотрите это свойство Value

Показать все комментарии