Delphi 執行 Oracle Stored Procedure 的問題 |
尚未結案
|
zabico
一般會員 發表:1 回覆:8 積分:1 註冊:2003-08-13 發送簡訊給我 |
各位好。小弟想要取回執行 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 發送簡訊給我 |
|
zabico
一般會員 發表:1 回覆:8 積分:1 註冊:2003-08-13 發送簡訊給我 |
引言: 依據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 發送簡訊給我 |
|
zabico
一般會員 發表:1 回覆:8 積分:1 註冊:2003-08-13 發送簡訊給我 |
引言: 看起來像是store procedure的問題,你要不要把store procedure改成簡單一點測試...這個可以用。如果我是一次要回傳整個 SELECT 之後的 RESULT (筆數不只一筆) 回來。delphi這邊的code要怎麼寫比較好呢?CREATE OR REPLACE PROCEDURE PG_TEST (PARAM_A IN VARCHAR := NULL, RTN_MSG OUT VARCHAR2)IS /*輸出的參數:結果訊息*/ BEGIN RTN_MSG := '傳入的參數是:'||PARAM_A END; |
change.jian
版主 發表:29 回覆:620 積分:439 註冊:2003-06-02 發送簡訊給我 |
引言:錯了,因為你要回傳的是一個cursor,所以不能用一般的作法,因為你的sql指令會回傳超過一筆的資料引言: 依據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:精確擷取傳回的列數超過所要求的列數 這要如何解決呢?? |
yachanga
資深會員 發表:24 回覆:335 積分:296 註冊:2003-09-27 發送簡訊給我 |
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 發送簡訊給我 |
|
zabico
一般會員 發表:1 回覆:8 積分:1 註冊:2003-08-13 發送簡訊給我 |
引言: 找到了,你必需把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 發送簡訊給我 |
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 發送簡訊給我 |
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 發送簡訊給我 |
|
change.jian
版主 發表:29 回覆:620 積分:439 註冊:2003-06-02 發送簡訊給我 |
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 endStoredProc1.DatabaseName請自行先在BDE裡建一個可以連Oracle的Alias,我是用REDBILL 補充說明,之前說連Oracle的cursor,只有ADO能做,錯了,用BDE也可以,像上面的例子我就是以BDE的TStoredProc來做的 |
zabico
一般會員 發表:1 回覆:8 積分:1 註冊:2003-08-13 發送簡訊給我 |
感謝 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 發送簡訊給我 |
引言: 感謝 change.jian 大大,我已經解決了。補充一點,這個方法一定要透過 BDE 才能作。yachanga 大大的方法也可以,不過因為本身的限制(一定要使用Stored Procedure),所以沒辦法利用 yachanga 大大的方法。我現在在研究 ADO 要怎麼做,碰到一個問題。希望有興趣的人來研究一下。create的參數如為ftCursor的話,不用設長度,看上面那個例子..,另外,用ADO也是可以的,因為我之前是用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 要設多少阿?怎麼設都不對,請了解的大大回答一下。謝謝。 |
zabico
一般會員 發表:1 回覆:8 積分:1 註冊:2003-08-13 發送簡訊給我 |
|
change.jian
版主 發表:29 回覆:620 積分:439 註冊:2003-06-02 發送簡訊給我 |
引言:引言: 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 發送簡訊給我 |
|
Mickey
版主 發表:77 回覆:1882 積分:1390 註冊:2002-12-11 發送簡訊給我 |
|
change.jian
版主 發表:29 回覆:620 積分:439 註冊:2003-06-02 發送簡訊給我 |
引言: 對不起。我發問錯方向了。我想了解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 發送簡訊給我 |
感謝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 發送簡訊給我 |
請問一下我這樣寫會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;
|
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |