我有一个Oracle存储过程,该存储过程获取2个参数并返回2个参数(状态和消息)。
我正在对该仅支持执行select语句的旧应用程序进行更改,
我的问题是可以用某些函数或其他存储过程或视图或其他可能不是我想要的对象包装存储过程吗,那么我可以使用简单的select语句执行存储过程吗?
正确的执行代码如下:
DECLARE
PRINTER_ID VARCHAR2(200);
O_STATUS VARCHAR2(200);
O_MESSAGE VARCHAR2(200);
BEGIN
PRINTER_ID := '551555115';
IMPL_XEROX_PRINTER_CHECK( PRINTER_ID => PRINTER_ID, O_STATUS => O_STATUS, O_MESSAGE => O_MESSAGE );
DBMS_OUTPUT.PUT_LINE('O_STATUS = ' || O_STATUS);
DBMS_OUTPUT.PUT_LINE('O_MESSAGE = ' || O_MESSAGE);
END;
我想要得到的是这样的:
Select O_STATUS,O_MESSAGE from IMPL_XEROX_PRINTER_CHECk_WRAPPER where PRINTER_ID = '551555115';
问题是SP正在将一些数据插入临时表中... 这是表格:
CREATE TABLE "TEST_PRNT_DATA" ( "COLUMN1" VARCHAR2(20 BYTE), "COLUMN2" VARCHAR2(20 BYTE), "COLUMN3" VARCHAR2(20 BYTE) )
/
这是存储过程:
CREATE OR REPLACE PROCEDURE IMPL_XEROX_PRINTER_CHECK
(
PRINTER_ID IN VARCHAR2
, O_STATUS OUT VARCHAR2
, O_MESSAGE OUT VARCHAR2
) AS
PROC_STATUS VARCHAR2(10);
PROC_ERROR_MESSAGE VARCHAR2(4000);
rand_num number;
BEGIN
dbms_output.put_line('IMPL_XEROX_PRINTER_CHECK ');
select round(dbms_random.value(1,10)) into rand_num from dual;
insert into TEST_PRNT_DATA values(1,2,3);
IF rand_num < 5 THEN
PROC_STATUS := 'TRUE';
O_STATUS:= 'TRUE';
PROC_ERROR_MESSAGE := 'ALL IS GOOD';
O_MESSAGE:= 'ALL IS GOOD';
ELSE
PROC_STATUS := 'FALSE';
O_STATUS:= 'FALSE';
PROC_ERROR_MESSAGE := 'SOMTHING WENT WRONG!!! ';
O_MESSAGE:= 'SOMTHING WENT WRONG!!! ';
END IF;
END IMPL_XEROX_PRINTER_CHECK;
答案 0 :(得分:3)
您可以使用流水线表功能创建一个包:
CREATE OR REPLACE
PACKAGE PACKAGE1
AS
type status_t is record ( o_status varchar2(10)
, o_message varchar2(4000));
type status_tt is table of status_t;
function impl_xerox_printer_check_w(printer_id varchar2) RETURN status_tt PIPELINED;
END PACKAGE1;
/
具有以下实现方式:
CREATE OR REPLACE
PACKAGE BODY PACKAGE1 AS
function impl_xerox_printer_check_w(printer_id varchar2) RETURN status_tt PIPELINED AS
status status_t;
BEGIN
impl_xerox_printer_check(printer_id, status.o_status, status.o_message);
PIPE ROW (status);
RETURN;
END impl_xerox_printer_check_w;
END PACKAGE1;
/
并像这样使用它:
with printers as (
select dbms_random.string('X',10) printer from dual connect by level <=5
)
select *
from printers
cross apply table(package1.impl_xerox_printer_check_w(printers.printer));
示例输出或签出db<>fiddle:
PRINTER O_STATUS O_MESSAGE
--------------- ---------- ------------------------------
55FBCMHYOS TRUE ALL IS GOOD
0Z37VPOSLK TRUE ALL IS GOOD
XK1QKTZ8X2 FALSE SOMTHING WENT WRONG!!!
K0Y6TN9YTR FALSE SOMTHING WENT WRONG!!!
8D0505711L TRUE ALL IS GOOD
答案 1 :(得分:3)
根据亚历克斯(Alex)的几个答案(sys.odcivarchar2list集合以及函数)的组合,这里有几个主题变化:
与大多数示例一样,第一个返回单个行,这是通过在最后一个查询中使用数据透视表来实现的:
with function wrap(printer_id in varchar2) return sys.odcivarchar2list as
status sys.odcivarchar2list;
begin
status := new sys.odcivarchar2list();
status.extend(2);
impl_xerox_printer_check(printer_id, status(1), status(2));
return status;
end;
t1 as (
select rownum r, column_value
from wrap('551555115')
)
select *
from t1
pivot (max(column_value)
for r in ( 1 as status
, 2 as message));
/
样本输出:
STATUS MESSAGE
-------- -------------------------
FALSE SOMTHING WENT WRONG!!!
第二个示例演示了使用CROSS APPLY一次获取多台打印机的状态:
with function wrap(printer_id in varchar2) return sys.odcivarchar2list as
status sys.odcivarchar2list;
begin
status := new sys.odcivarchar2list();
status.extend(2);
impl_xerox_printer_check(printer_id, status(1), status(2));
return status;
end;
printers as (
select dbms_random.string('X',10) printer from dual connect by level <=5
), t1 as (
select printer, mod(rownum-1,2) r, w.*
from printers
cross apply wrap(printers.printer) w
)
select *
from t1
pivot (max(column_value) for r in (0 as status, 1 as message));
/
样本输出:
PRINTER STATUS MESSAGE
---------- -------- -------------------------
M6N6MZ5NG6 TRUE ALL IS GOOD
4H2WKK52V7 TRUE ALL IS GOOD
6MB7B9FRWV TRUE ALL IS GOOD
389KALS4U9 FALSE SOMTHING WENT WRONG!!!
6Y1ACVUHY6 TRUE ALL IS GOOD
答案 2 :(得分:2)
这取决于您的应用程序可以处理的内容。您可能有一个返回引用游标的包装函数:
create or replace function impl_xerox_printer_check_wrap (
printer_id in varchar2
)
return sys_refcursor as
o_status varchar2(200);
o_message varchar2(200);
o_refcursor sys_refcursor;
begin
impl_xerox_printer_check(printer_id => printer_id, o_status => o_status, o_message => o_message);
open o_refcursor for select o_status as status, o_message as message from dual;
return o_refcursor;
end;
/
select impl_xerox_printer_check_wrap('551555115') from dual;
IMPL_XEROX_PRINTER_C
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
STATUS MESSAGE
---------- ------------------------------
TRUE ALL IS GOOD
(输出如SQL Developer所示,作为脚本运行)。但是您的应用程序可能不知道该怎么办。
您可以使用集合或对象类型,但是除非您在模式级别定义自己的类型,否则解释起来会有些麻烦:
create or replace function impl_xerox_printer_check_wrap (
printer_id in varchar2
)
return sys.odcivarchar2list as
o_result sys.odcivarchar2list;
begin
o_result := new sys.odcivarchar2list();
o_result.extend(2);
impl_xerox_printer_check(printer_id => printer_id, o_status => o_result(1), o_message => o_result(2));
return o_result;
end;
/
select * from table (impl_xerox_printer_check_wrap('551555115'));
Result Sequence
------------------------------------------------
TRUE
ALL IS GOOD
或者您可以使用XML,这听起来很奇怪,但给出了一个不错的结果:
create or replace function impl_xerox_printer_check_wrap (
printer_id in varchar2
)
return xmltype as
o_status varchar2(200);
o_message varchar2(200);
o_refcursor sys_refcursor;
begin
impl_xerox_printer_check(printer_id => printer_id, o_status => o_status, o_message => o_message);
open o_refcursor for select o_status as status, o_message as message from dual;
return xmltype(o_refcursor);
end;
/
select impl_xerox_printer_check_wrap('551555115') from dual;
IMPL_XEROX_PRINTER_CHECK_WRAP('551555115')
--------------------------------------------------------------------------------
<?xml version="1.0"?>
<ROWSET>
<ROW>
<STATUS>FALSE</STATUS>
<MESSAGE>SOMTHING WENT WRONG!!! </MESSAGE>
</ROW>
</ROWSET>
好吧,这似乎没有什么帮助...但是您可以提取值:
select status, message
from xmltable(
'/ROWSET/ROW'
passing impl_xerox_printer_check_wrap('551555115')
columns status varchar2(200) path 'STATUS',
message varchar2(200) path 'MESSAGE'
);
STATUS MESSAGE
---------- ------------------------------
FALSE SOMTHING WENT WRONG!!!
您的应用程序可以运行该查询-当然将打印机ID作为绑定变量进行传递-并会返回一个简单的结果集。
当您使用12c时,可以使用添加到子查询因式分解中的PL / SQL功能,因此根本不需要创建永久性功能(尽管您可能还是愿意):
drop function IMPL_XEROX_PRINTER_CHECK_WRAP;
with
function impl_xerox_printer_check_wrap (
printer_id in varchar2
)
return xmltype as
o_status varchar2(200);
o_message varchar2(200);
o_refcursor sys_refcursor;
begin
impl_xerox_printer_check(printer_id => printer_id, o_status => o_status, o_message => o_message);
open o_refcursor for select o_status as status, o_message as message from dual;
return xmltype(o_refcursor);
end;
select impl_xerox_printer_check_wrap('551555115')
from dual
/
(如果要使用XML)(根据注释),或者如果不需要,则使用XMLTable:
IMPL_XEROX_PRINTER_CHECK_WRAP('551555115')
--------------------------------------------------------------------------------
<?xml version="1.0"?>
<ROWSET>
<ROW>
<STATUS>TRUE</STATUS>
<MESSAGE>ALL IS GOOD</MESSAGE>
</ROW>
</ROWSET>
with
function impl_xerox_printer_check_wrap (
printer_id in varchar2
)
return xmltype as
o_status varchar2(200);
o_message varchar2(200);
o_refcursor sys_refcursor;
begin
impl_xerox_printer_check(printer_id => printer_id, o_status => o_status, o_message => o_message);
open o_refcursor for select o_status as status, o_message as message from dual;
return xmltype(o_refcursor);
end;
select status, message
from xmltable(
'/ROWSET/ROW'
passing impl_xerox_printer_check_wrap('551555115')
columns status varchar2(200) path 'STATUS',
message varchar2(200) path 'MESSAGE'
)
/
STATUS MESSAGE
---------- ------------------------------
FALSE SOMTHING WENT WRONG!!!
问题是SP正在向临时表中插入一些数据
这是一个相当关键的遗漏。您不能通过选择在函数调用中执行插入或更新。文档lists restrictions on functions called from SQL和goes into more detail in this warning:
由于SQL是一种声明性语言,而不是命令性(或过程性)语言,因此您无法知道SQL语句调用的函数将运行多少次,即使该函数是用命令性语言PL / SQL编写的
如果允许该函数执行DML,则您将无法控制该DML执行了多少次。例如,如果要执行插入操作,则它可能会尝试将同一行插入两次,并重复数据或违反约束条件。
从技术上讲,您可以使用pragma autonomous_transaction
来声明函数,如this modified db<>fiddle所示,但这是一个可怕的骇客,并且由于上述原因,它最终可能会导致更多的问题而不是解决的问题。如果您仅以示例中的方式进行单行调用,您可能会摆脱,但是即使这样也不能保证正常工作;即使现在可行,将来也可能会中断。
答案 3 :(得分:1)
制作一个新的Sql触发器,该触发器监视表Table_legacyInputOutput。将您的输入插入打印机ID为PRINTER_ID ='551555115'的表中 然后触发器将调用存储过程和更新表 用于O_STATUS和O_MESSAGE。 我认为您的旧版应用程序至少可以插入并选择。它只是无法调用SP并检查返回参数
Table_legacyInputOutput structure.
PRINTER O_STATUS O_MESSAGE