全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:6987
推到 Plurk!
推到 Facebook!

Delphi 執行 Oracle Stored Procedure 的問題

尚未結案
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-04-20 14:13:38 IP:218.165.xxx.xxx 未訂閱
各位好。小弟想要取回執行 Stored Procedure 的結果,參考了很多大大的文章都沒辦法解答我的問題。請各位幫我看看哪裡錯了... 作業系統:Win2000 Professional Delphi版本:Delphi 5.0 Enterprise 資料庫版本:Oracle 8.0i    Oracle部份:
CREATE OR REPLACE PACKAGE REPORT IS
 TYPE E_REF_CUR IS REF CURSOR;
 FUNCTION CALCULATE_JOB_COST(E_ID IN VARCHAR2) RETURN NUMBER; 
 PROCEDURE REPORT_JOB_COST(E_CUR OUT E_REF_CUR);
END REPORT;
CREATE OR REPLACE PACKAGE BODY REPORT IS
 FUNCTION CALCULATE_JOB_COST(E_ID IN VARCHAR2) RETURN NUMBER IS
  SUM_COST NUMBER;
 BEGIN
  SELECT SUM(RESOURCE_COST_PERMONTH)
    INTO SUM_COST
    FROM JOB_USE_RESOURCE, USE_RESOURCE
   WHERE (JUR_JOB_ID = UPPER(E_ID) AND JUR_RESOURCE_ID = RESOURCE_ID);
 
  -- if sum_cost is null, set sum_cost = -1 
  IF SUM_COST IS NULL THEN
   SUM_COST := -1;
  END IF;
 
  RETURN SUM_COST;
 END CALCULATE_JOB_COST;     PROCEDURE REPORT_JOB_COST(E_CUR OUT E_REF_CUR) IS
 BEGIN
  IF E_CUR%ISOPEN THEN
   CLOSE E_CUR;
  END IF;
  
  OPEN E_CUR FOR
   SELECT EMPLOYEE_ID,
          EMPLOYEE_NAME,
          JOB_NAME,
          CALCULATE_JOB_COST(EMPLOYEE_JOBID) AS JOB_COST
     FROM EMPLOYEE, JOB
    WHERE EMPLOYEE_JOBID = JOB_ID;
 END REPORT_JOB_COST;
END REPORT;
Delphi部份:
  ......
  with StoredProc1 do
    begin
      Close;
      StoredProcName :='REPORT.REPORT_JOB_COST';
      Params.Clear;
      Params.CreateParam(ftCursor,'E_CUR',ptOutput);
      Prepare;
      try
        Open;
      except
        on E:EDatabaseError do ShowMessage(E.Message);
      end;
      Close;
    end;
  ......
REPORT_JOB_COST 的結果筆數不只一筆。 還有,這是使用 TStoredProc --> TDataBase。如果改用 TADOConnection 和 TADOStoredProc 又該注意哪些東西。請各位多幫忙,謝謝!
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#2 引用回覆 回覆 發表時間:2004-04-20 15:37:52 IP:61.222.xxx.xxx 未訂閱
依據online help: TStoredProc reuses the Params property to hold the results returned by a stored procedure 所以,你應該是把stored procedure的參數設成output的方式來取得回傳值才是
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#3 引用回覆 回覆 發表時間:2004-04-20 15:59:07 IP:218.165.xxx.xxx 未訂閱
引言: 依據online help: TStoredProc reuses the Params property to hold the results returned by a stored procedure 所以,你應該是把stored procedure的參數設成output的方式來取得回傳值才是
對不起,我不太了解。您的意思是要把 Stored Procedure 改成
 PROCEDURE REPORT_JOB_COST(E_ID       OUT VARCHAR2,
                           E_NAME     OUT VARCHAR2,
                           E_JOB_NAME OUT VARCHAR2,
                           E_JOB_COST OUT NUMBER) IS
 BEGIN
  SELECT EMPLOYEE_ID,
         EMPLOYEE_NAME,
         JOB_NAME,
         CALCULATE_JOB_COST(EMPLOYEE_JOBID) AS JOB_COST
    INTO E_ID, E_NAME, E_JOB_NAME, E_JOB_COST
    FROM EMPLOYEE, JOB
   WHERE EMPLOYEE_JOBID = JOB_ID;
 END REPORT_JOB_COST;
這樣嗎?不過我這樣做之後會出現 exception [Oracle][ODBC][Ora]ORA-01422:精確擷取傳回的列數超過所要求的列數 這要如何解決呢??
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#4 引用回覆 回覆 發表時間:2004-04-20 16:15:52 IP:61.222.xxx.xxx 未訂閱
看起來像是store procedure的問題,你要不要把store procedure改成簡單一點測試...
CREATE OR REPLACE PROCEDURE PG_TEST
(PARAM_A IN        VARCHAR := NULL,
 RTN_MSG OUT        VARCHAR2)IS         /*輸出的參數:結果訊息*/
BEGIN
  RTN_MSG := '傳入的參數是:'||PARAM_A
END;
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#5 引用回覆 回覆 發表時間:2004-04-20 16:30:54 IP:218.165.xxx.xxx 未訂閱
引言: 看起來像是store procedure的問題,你要不要把store procedure改成簡單一點測試...
CREATE OR REPLACE PROCEDURE PG_TEST
(PARAM_A IN        VARCHAR := NULL,
 RTN_MSG OUT        VARCHAR2)IS         /*輸出的參數:結果訊息*/
BEGIN
  RTN_MSG := '傳入的參數是:'||PARAM_A
END;
這個可以用。如果我是一次要回傳整個 SELECT 之後的 RESULT (筆數不只一筆) 回來。delphi這邊的code要怎麼寫比較好呢?
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#6 引用回覆 回覆 發表時間:2004-04-20 16:32:47 IP:61.222.xxx.xxx 未訂閱
引言:
引言: 依據online help: TStoredProc reuses the Params property to hold the results returned by a stored procedure 所以,你應該是把stored procedure的參數設成output的方式來取得回傳值才是
對不起,我不太了解。您的意思是要把 Stored Procedure 改成
 PROCEDURE REPORT_JOB_COST(E_ID       OUT VARCHAR2,
                           E_NAME     OUT VARCHAR2,
                           E_JOB_NAME OUT VARCHAR2,
                           E_JOB_COST OUT NUMBER) IS
 BEGIN
  SELECT EMPLOYEE_ID,
         EMPLOYEE_NAME,
         JOB_NAME,
         CALCULATE_JOB_COST(EMPLOYEE_JOBID) AS JOB_COST
    INTO E_ID, E_NAME, E_JOB_NAME, E_JOB_COST
    FROM EMPLOYEE, JOB
   WHERE EMPLOYEE_JOBID = JOB_ID;
 END REPORT_JOB_COST;
這樣嗎?不過我這樣做之後會出現 exception [Oracle][ODBC][Ora]ORA-01422:精確擷取傳回的列數超過所要求的列數 這要如何解決呢??
錯了,因為你要回傳的是一個cursor,所以不能用一般的作法,因為你的sql指令會回傳超過一筆的資料
yachanga
資深會員


發表:24
回覆:335
積分:296
註冊:2003-09-27

發送簡訊給我
#7 引用回覆 回覆 發表時間:2004-04-20 16:41:49 IP:211.74.xxx.xxx 未訂閱
Hi zabico您好: Procedure and function 各有優缺點..在採用時應該以本身需求為主... So 依照您的需求, 使用 VIEW FUNCTION 比較理想 try following example: Create or replace view REPORT_JOB_COST as SELECT EMPLOYEE_ID, EMPLOYEE_NAME, JOB_NAME, CALCULATE_JOB_COST(EMPLOYEE_JOBID) AS JOB_COST FROM EMPLOYEE, JOB WHERE EMPLOYEE_JOBID = JOB_ID -------------------------------------- CREATE OR REPLACE FUNCTION CALCULATE_JOB_COST(E_ID IN VARCHAR2) RETURN NUMBER IS SUM_COST NUMBER; BEGIN SELECT SUM(RESOURCE_COST_PERMONTH) INTO SUM_COST FROM JOB_USE_RESOURCE, USE_RESOURCE WHERE (JUR_JOB_ID = UPPER(E_ID) AND JUR_RESOURCE_ID = RESOURCE_ID); -- if sum_cost is null, set sum_cost = -1 IF SUM_COST IS NULL THEN SUM_COST := -1; END IF; RETURN SUM_COST; END CALCULATE_JOB_COST; 發表人 - yachanga 於 2004/04/20 16:44:54
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#8 引用回覆 回覆 發表時間:2004-04-20 16:53:15 IP:61.222.xxx.xxx 未訂閱
找到了,你必需把TParameter.DataType的值設成ftCursor 你可以看你的Delphi online help,找ADO的TADOStoredProc,因為回傳cursor好像只有ADO有支援,而且只有oracle能這樣做
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#9 引用回覆 回覆 發表時間:2004-04-20 17:22:58 IP:218.165.xxx.xxx 未訂閱
引言: 找到了,你必需把TParameter.DataType的值設成ftCursor 你可以看你的Delphi online help,找ADO的TADOStoredProc,因為回傳cursor好像只有ADO有支援,而且只有oracle能這樣做
這是我改寫後的 delphi 端
  with ADOStoredProc1 do
    begin
      Close;
      ProcedureName :='REPORT_JOB_COST';
      Parameters.Clear;
      Parameters.CreateParameter('E_CUR',ftCursor,pdOutput,0,null);
      try
        ExecProc;
      except
        on E:EDatabaseError do ShowMessage(E.Message);
      end;
      Close;
    end;
不過還是有 exception EOleException: 提供不一致或不完全資訊導致參數物件不適當的被拒。
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#10 引用回覆 回覆 發表時間:2004-04-20 17:49:38 IP:61.222.xxx.xxx 未訂閱
create or replace procedure REPORT_JOB_COST
( E_ID IN VARCHAR2,
  c1 out pg_ref_cursor.rpt_cursor
)
is
begin
   open  c1 for
   SELECT EMPLOYEE_ID,
          EMPLOYEE_NAME,
          JOB_NAME,
          CALCULATE_JOB_COST(EMPLOYEE_JOBID) AS JOB_COST
     FROM EMPLOYEE, JOB
    WHERE EMPLOYEE_JOBID = E_ID; 
end;
Delphi:
QBE.Active         := FALSE;
QBE.StoredProcName := REPORT_JOB_COST';
QBE.ParamBindMode  := pbByName;
QBE.Params.CreateParam(ftString, 'E_ID', ptInput);
QBE.Params.CreateParam(ftCursor, 'C1', ptOutput);
QBE.Active         := TRUE;
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#11 引用回覆 回覆 發表時間:2004-04-20 17:51:10 IP:61.222.xxx.xxx 未訂閱
create or replace procedure REPORT_JOB_COST
( E_ID IN VARCHAR2,
  c1 out pg_ref_cursor.rpt_cursor
)
is
begin
   open  c1 for
   SELECT EMPLOYEE_ID,
          EMPLOYEE_NAME,
          JOB_NAME,
          CALCULATE_JOB_COST(EMPLOYEE_JOBID) AS JOB_COST
     FROM EMPLOYEE, JOB
    WHERE EMPLOYEE_JOBID = E_ID; 
end;
Delphi:
QBE.Active         := FALSE;
QBE.StoredProcName := REPORT_JOB_COST';
QBE.ParamBindMode  := pbByName;
QBE.Params.CreateParam(ftString, 'E_ID', ptInput);
QBE.Params.CreateParam(ftCursor, 'C1', ptOutput);
QBE.Active         := TRUE;
把DataSource連著QBE即可(QBE是TStoredProc)
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#12 引用回覆 回覆 發表時間:2004-04-20 21:24:31 IP:218.165.xxx.xxx 未訂閱
change.jian 大大,你寫的 code 我試不出來,能不能給小弟一個範例或是參考文件,謝謝。
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#13 引用回覆 回覆 發表時間:2004-04-20 22:22:37 IP:218.169.xxx.xxx 未訂閱
Oracle:
 
create or replace package pg_ref_cursor
 is
   type rpt_cursor is ref cursor;
end;
/    create or replace procedure pg_get_cursor
(
  c1 out pg_ref_cursor.rpt_cursor
)
is
begin
   open  c1 for
   select *
   from USER_OBJECTS;
end;
/ 
Delphi:
 
unit Unit1;    interface    uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids, DBGrids, Db, DBTables, StdCtrls, Buttons;    type
  TForm1 = class(TForm)
    DataSource1: TDataSource;
    StoredProc1: TStoredProc;
    DBGrid1: TDBGrid;
    BitBtn1: TBitBtn;
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;    var
  Form1: TForm1;    implementation    {$R *.DFM}    procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  StoredProc1.Active:=False;
  StoredProc1.StoredProcName:='PG_GET_CURSOR';
  StoredProc1.Params.Clear;
  StoredProc1.ParamBindMode:=pbByName;
  StoredProc1.Params.CreateParam(ftCursor,'C1',ptOutput);
  StoredProc1.Active:=TRUE;
end;    end.
畫面檔:
object Form1: TForm1
  Left = 228
  Top = 203
  Width = 544
  Height = 375
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = True
  PixelsPerInch = 96
  TextHeight = 13
  object DBGrid1: TDBGrid
    Left = 8
    Top = 40
    Width = 521
    Height = 297
    DataSource = DataSource1
    TabOrder = 0
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -11
    TitleFont.Name = 'MS Sans Serif'
    TitleFont.Style = []
  end
  object BitBtn1: TBitBtn
    Left = 88
    Top = 8
    Width = 75
    Height = 25
    Caption = 'Run'
    TabOrder = 1
    OnClick = BitBtn1Click
  end
  object DataSource1: TDataSource
    DataSet = StoredProc1
    Left = 50
    Top = 4
  end
  object StoredProc1: TStoredProc
    DatabaseName = 'REDBILL'
    StoredProcName = 'PG_GET_CURSOR'
    Left = 10
    Top = 2
    ParamData = <
      item
        DataType = ftCursor
        Name = 'C1'
        ParamType = ptOutput
      end>
  end
end
StoredProc1.DatabaseName請自行先在BDE裡建一個可以連Oracle的Alias,我是用REDBILL 補充說明,之前說連Oracle的cursor,只有ADO能做,錯了,用BDE也可以,像上面的例子我就是以BDE的TStoredProc來做的
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#14 引用回覆 回覆 發表時間:2004-04-21 11:49:47 IP:218.165.xxx.xxx 未訂閱
感謝 change.jian 大大,我已經解決了。補充一點,這個方法一定要透過 BDE 才能作。yachanga 大大的方法也可以,不過因為本身的限制(一定要使用Stored Procedure),所以沒辦法利用 yachanga 大大的方法。我現在在研究 ADO 要怎麼做,碰到一個問題。希望有興趣的人來研究一下。
with ADOStoredProc1 do
  begin
    Close;
    ProcedureName := 'REPORT_JOB_COST';
    Parameters.Clear;
    Parameters.CreateParameter('E_CUR',ftCursor,pdOutput,0,null);
    try
      ExecProc;
    except
      on E:EDatabaseError do ShowMessage(E.Message);
    end;
  end;
紅色那一行的 Length 要設多少阿?怎麼設都不對,請了解的大大回答一下。謝謝。
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#15 引用回覆 回覆 發表時間:2004-04-21 11:54:55 IP:61.222.xxx.xxx 未訂閱
引言: 感謝 change.jian 大大,我已經解決了。補充一點,這個方法一定要透過 BDE 才能作。yachanga 大大的方法也可以,不過因為本身的限制(一定要使用Stored Procedure),所以沒辦法利用 yachanga 大大的方法。我現在在研究 ADO 要怎麼做,碰到一個問題。希望有興趣的人來研究一下。
with ADOStoredProc1 do
  begin
    Close;
    ProcedureName := 'REPORT_JOB_COST';
    Parameters.Clear;
    Parameters.CreateParameter('E_CUR',ftCursor,pdOutput,0,null);
    try
      ExecProc;
    except
      on E:EDatabaseError do ShowMessage(E.Message);
    end;
  end;
紅色那一行的 Length 要設多少阿?怎麼設都不對,請了解的大大回答一下。謝謝。
create的參數如為ftCursor的話,不用設長度,看上面那個例子..,另外,用ADO也是可以的,因為我之前是用ADO做的
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#16 引用回覆 回覆 發表時間:2004-04-21 13:09:11 IP:218.165.xxx.xxx 未訂閱
引言: create的參數如為ftCursor的話,不用設長度,看上面那個例子..,另外,用ADO也是可以的,因為我之前是用ADO做的
能不能麻煩change.jian大大寫一個ADO的範例,謝謝您了。
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#17 引用回覆 回覆 發表時間:2004-04-21 14:23:12 IP:61.222.xxx.xxx 未訂閱
引言:
引言: create的參數如為ftCursor的話,不用設長度,看上面那個例子..,另外,用ADO也是可以的,因為我之前是用ADO做的
能不能麻煩change.jian大大寫一個ADO的範例,謝謝您了。
 
with ADOStoredProc1 do //這裡不就是ADO的嗎?
  begin
    Close;
    ProcedureName := 'REPORT_JOB_COST';
    Parameters.Clear;
    Parameters.CreateParameter('E_CUR',ftCursor,pdOutput,0,null);
    try
      ExecProc;
    except
      on E:EDatabaseError do ShowMessage(E.Message);
    end;
  end;
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#18 引用回覆 回覆 發表時間:2004-04-21 15:19:24 IP:218.165.xxx.xxx 未訂閱
對不起。我發問錯方向了。我想了解change.jian 大大的 ADOConnectString 怎麼設,因為不管我是設 Microsoft OLE DB Provider for Oracle 或是 Oracle Provider for OLE DB 都會產生我之前說的 exception: EOleException: 提供不一致或不完全資訊導致參數物件不適當的被拒。
Mickey
版主


發表:77
回覆:1882
積分:1390
註冊:2002-12-11

發送簡訊給我
#19 引用回覆 回覆 發表時間:2004-04-21 15:52:57 IP:218.163.xxx.xxx 未訂閱
zabico 你好:    節錄一段 TFieldType 的 Help ftCursor        Output cursor from an Oracle stored procedure (TParam only) ADO 係 TParameter 非 TParam, 所以 ADO 恐怕不行.
change.jian
版主


發表:29
回覆:620
積分:439
註冊:2003-06-02

發送簡訊給我
#20 引用回覆 回覆 發表時間:2004-04-21 16:09:40 IP:61.222.xxx.xxx 未訂閱
引言: 對不起。我發問錯方向了。我想了解change.jian 大大的 ADOConnectString 怎麼設,因為不管我是設 Microsoft OLE DB Provider for Oracle 或是 Oracle Provider for OLE DB 都會產生我之前說的 exception: EOleException: 提供不一致或不完全資訊導致參數物件不適當的被拒。
Provider=MSDAORA.1;Password=XXXXX;Persist Security Info=True;User ID=REDBILL;Data Source=REDBILL.world 我那個時候用的是Microsoft OLE DB Provider for Oracle 建議你從最簡單的試試看,直接拉個TADOConnection及TADOStoredProc,在Delphi裡直接設定TADOStoredProc的StoreProcedureName及相關的參數,應該可以正常才是...(我就是直接拖拉放的)
zabico
一般會員


發表:1
回覆:8
積分:1
註冊:2003-08-13

發送簡訊給我
#21 引用回覆 回覆 發表時間:2004-04-21 17:09:37 IP:218.165.xxx.xxx 未訂閱
感謝change.jian大大,我試驗出來了。在此我整理一下: 使用 TADOStoredProc 部份: 1.ConnectionString裡面要用 Microsoft OLE DB Provider for Oracle ,另外一個不能用。 2.如果 Stored Procedure 有回傳 Ref Cursor,那 Parameters 裡的 DataType 會是ftInterface,而不是ftCursor。 3.如果使用 TDBGrid 顯示執行後得結果。程式碼裡 ExecProc 要換成 Open 即可。 使用 TStoredProc 部份: 1.要利用 delphi 的 BDE Administrator ,選擇 "Oracle" 去產生 Link。 2.如果Stored Procedure 有回傳 Ref Cursor,Params 裡的 DataType 即為 ftCursor ,不需改變。 3.與上面第三點同,利用 TDBGrid 顯示結果。程式碼裡為 Open。    希望我的小整理能給想使用 Oracle 和 delphi 的人一點參考。
JS
一般會員


發表:22
回覆:26
積分:9
註冊:2002-07-20

發送簡訊給我
#22 引用回覆 回覆 發表時間:2004-06-25 19:34:07 IP:211.78.xxx.xxx 未訂閱
請問一下我這樣寫會error不知是何原因 ADOStoredProc1.ProcedureName:='dept_package.dept_sp'; ADOStoredProc1.Parameters.Clear; ADOStoredProc1.Parameters.CreateParameter('dept_no', ftInteger, pdInput, 2, 11); ADOStoredProc1.Parameters.CreateParameter('io_cursor', ftInterface, pdInputOutput, 0, null); ADOStoredProc1.Open; ===================================================================== Error Message:參數數目不正確 ===================================================================== Oracle package如下: CREATE OR REPLACE PACKAGE BODY dept_package AS PROCEDURE dept_sp(dept_no IN NUMBER,io_cursor IN OUT t_cursor) IS v_cursor t_cursor; BEGIN IF dept_no<>0 THEN OPEN v_cursor FOR SELECT * FROM DEPT WHERE DEPTNO=dept_no; ELSE OPEN v_cursor FOR SELECT * FROM DEPT; END IF; io_cursor:=v_cursor; END dept_sp; END dept_package;
系統時間:2024-11-24 20:16:15
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!