从Select语句调用Oracle存储过程吗?

时间:2020-02-19 19:03:08

标签: oracle12c

我有一个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;

4 个答案:

答案 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!!!        

db<>fiddle

您的应用程序可以运行该查询-当然将打印机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 SQLgoes 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  
相关问题