2

我需要GetData从 Delphi 调用一个使用以下参数调用的 oracle 函数:

(RESULT) NUMBER
P1 VARCHAR2 IN
P2 VARCHAR2 IN
P3 VARCHAR2 IN
P4 VARCHAR2 OUT

我尝试使用的代码是:

q := TADOQuery.Create(nil);
q.Connection := conn;
q.SQL.Add('BEGIN');
q.SQL.Add(' SELECT GetData(:IN_1,:IN_2,:IN_3,:OUT_1) into :OUT_2 from dual;');
q.SQL.Add('END;');

q.Parameters.ParamByName('IN_1').DataType:=ftString;
q.Parameters.ParamByName('IN_1').Direction:=pdInput;
q.Parameters.ParamByName('IN_1').Size:=3;
q.Parameters.ParamByName('IN_1').Value:='001';

q.Parameters.ParamByName('IN_2').DataType:=ftString;
q.Parameters.ParamByName('IN_2').Direction:=pdInput;
q.Parameters.ParamByName('IN_2').Size:=15;
q.Parameters.ParamByName('IN_2').Value:='88000000000';

q.Parameters.ParamByName('IN_3').DataType:=ftString;
q.Parameters.ParamByName('IN_3').Direction:=pdInput;
q.Parameters.ParamByName('IN_3').Size:=64;
q.Parameters.ParamByName('IN_3').Value:='';

q.Parameters.ParamByName('OUT_1').DataType:=ftString;
q.Parameters.ParamByName('OUT_1').Direction:=pdOutput;
q.Parameters.ParamByName('OUT_1').Size:=255;
q.Parameters.ParamByName('OUT_1').Value:='';

q.Parameters.ParamByName('OUT_2').DataType:=ftInteger;
q.Parameters.ParamByName('OUT_2').Direction:=pdOutput;
q.Parameters.ParamByName('OUT_2').Value:='0';

q.Open;

responseEdit.Text:=q.Parameters.ParamByName('OUT_1').Value;

但是我明白了ORA-06572: Function GETDATA has OUT arguments。据我了解,不能在 Oracle 的 SELECT 语句中使用带有 OUT 参数的函数...

那么怎么称呼呢?


例如,在 java 中,我设法使用以下语法来做到这一点:

CallableStatement cs = conn.prepareCall("{ call ? := GetData(?,?,?,?)}");

不幸的是它在Delphi中不起作用......


我还尝试使用以下语法调用它:

q.SQL.Add('BEGIN');
q.SQL.Add(' :OUT_2 := GetData(:IN_1,:IN_2,:IN_3,:OUT_1);');
q.SQL.Add('END;');

但德尔福似乎误解了:=符号作为参数。所以它涉及到 Oracle 损坏,并PLS-00103: Encountered the symbol "" when expecting one of the following := . ( @ % ; indicator抛出异常......或者如果我删除了空格,:=Delphi 已经抛出了一些一般的 EOLEException。


我试着打电话TADOStoredProc

stp := TADOStoredProc.Create(nil);
stp.Connection := conn;
//stp.ProcedureName:='GetData';    //also tried this
stp.ProcedureName:=':OUT_2 := GetData(:IN_1,:IN_2,:IN_3,:OUT_1)';
stp.Parameters.CreateParameter('OUT_2',ftInteger,pdOutput,4,0);
stp.Parameters.CreateParameter('IN_1',ftString,pdInput,3,'101');
stp.Parameters.CreateParameter('IN_2',ftString,pdInput,15,phoneEdit.Text);
stp.Parameters.CreateParameter('IN_3',ftString,pdInput,64,' ');
stp.Parameters.CreateParameter('OUT_1',ftString,pdOutput,255,' ');
stp.ExecProc;

它抛出一个未知的 OleException。当我指定 proc 名称stp.ProcedureName:='GetData';时,它会说wrong number or types of parameters specified.


我试着做同样的事情TADOCommand

cmd := TADOCommand.Create(nil);
cmd.Connection := conn;
cmd.CommandType := cmdStoredProc;
cmd.CommandText := ' :OUT_2 := GetData(:IN_1,:IN_2,:IN_3,:OUT_1); ';
...

Delphi 再次抛出一个未知的 OleException。


所以要么我对oracle和delphi都使用了不正确的语法,要么我没有将参数传递给delphi,所以它可以格式化对oracle的正确调用......

但是,当调用没有 OUT 参数的 oracle 函数或调用 ms sql 函数时(即使没有参数......),所有这些都有效。那么这里有什么问题呢?

4

1 回答 1

0

正如我在评论中所说,TADOStoredProc以无参数工作而闻名。
这是一个SSCCE:

CREATE OR REPLACE FUNCTION ADMIN.GETDATA(IN_1 IN VARCHAR2, 
                                   IN_2 IN VARCHAR2, 
                                   IN_3 IN VARCHAR2, 
                                   OUT_1 IN OUT VARCHAR2) RETURN INTEGER
IS
BEGIN
    OUT_1 := IN_1 || IN_2 || IN_3;
    RETURN 5;
END;

在 PL/SQL 上这是有效的:

BEGIN  
  DECLARE OUTVAR VARCHAR(255);
          RETVAR INTEGER;    
  BEGIN
     RETVAR:= ADMIN.GETDATA('A','B','C', OUTVAR);
     DBMS_OUTPUT.PUT_LINE(OUTVAR);     
     DBMS_OUTPUT.PUT_LINE(RETVAR);         
  END;  
END;

输出页面是:

ABC
5

现在德尔福代码:

var Proc: TADOStoredProc;
    P: TParameter;
    Results: String;
begin
  Proc := TADOStoredProc.Create(nil);
  try
    (* Set up the connection to an Oracle database *)
    Proc.Connection := MyADOConnection;

    (* Define the function Name *)
    Proc.ProcedureName := 'GETDATA';

    (* Let the FrameWork retrieve all the parameters from DataBase *)
    //Proc.Parameters.Refresh;
    //Set parameters values
    //Proc.Parameters.ParamByName('IN_1').Value := 'A';
    //Proc.Parameters.ParamByName('IN_2').Value := 'B';
    //Proc.Parameters.ParamByName('IN_3').Value := 'C';
    //Proc.Parameters.ParamByName('OUT1').Value := ''; //This will be overrided by the database.

    // **********************
    //          OR
    // **********************

    //Define it manually!
    begin
      //Defining the Return Value
      Proc.Parameters.CreateParameter('RETVAL', ftBCD (* or ftInteger *), pdReturnValue, 0, Unassigned);

      //Defining Input parameters 1, 2 and 3;

      Proc.Parameters.CreateParameter('IN1', ftString , pdInput, 4000 (* This is the Max *), 'A');

      //Collect the reference to update later
      P := Proc.Parameters.CreateParameter('IN2', ftString , pdInput, 4000 (* Length for a    *), Unassigned);

      Proc.Parameters.CreateParameter('IN3', ftString , pdInput, 4000 (* ftString param  *), 'C');

      Proc.Parameters.CreateParameter('OUT1', ftString , pdOutput, 4000, Unassigned);

      P.Value := 'B';

      Proc.ExecProc;

      // Expected : [Return Value : 5]~[Out Var: ABC]
      Results := Format('[Return Value : %s]~[Out Var: %s]',
                        [VarToStr(Proc.Parameters.ParamByName('RETVAL').Value),
                         VarToStr(Proc.Parameters.ParamByName('OUT1').Value)]);
      Proc.Close;

      // ACTUAL : [Return Value : 5]~[Out Var: ABC]
      ShowMessage(Results);


      //IT WORKS!!
    end;
  finally
    FreeAndNil(Proc);
  end;
于 2013-11-18T17:25:37.897 回答