18.4.2.4 存儲過程編程
存儲過程也是SQL服務器上的一段程序,它接收輸入參數,在服務器端執行,並將結果返回客戶端,存儲過程是必須在客戶應用程序中顯式調用的。
對於數據庫表中大量記錄的統計和函數計算,存儲過程是很有用,這樣可以將重復性計算任務轉換到服務器,提高數據庫應用的性能。
Delphi中有兩個部件能操作遠程數據庫服務器上的存儲過程:TQuery和TStoredProc。
1. TQuery的存儲過程編程
CSDEMO中演示用TQuery調用存儲過程的窗體是TFrmQueryProc。
TFrmQueryProc中有兩個TDBGrid 部件。DBGrid1顯示EmployeeTable中的數據。DBGrid2顯示Project表中的數據。使用存儲過程的TQuery部件名為EmployeeProjectsQuery,它的作用是建立Employee 表和Project 表的連接,以實現當DBGrid1中記錄改變時,DBGrid2中的數據作相應的改變。具體的連接任務是由服務器上的存儲過程Get_Emp_Proj完成。下面是Get_Emp_Proj的程序:
PROCEDURE Get_Emp_Proj
BEGIN
FOR SELECT proj_id
FROM employee_project
WHERE emp_no = :emp_no
INTO :proj_id
DO
SUSPEND;
END
EMP_NO INPUT SMALLINT
PROJ_ID OUTPUT CHAR(5)
該過程帶兩個參數:
EMP_NO是輸入參數,類型是SMALLINT.
PROJ_ID是輸出參數,類型是CHAR(5)
相應地,EmployeeProjectsQuery的主要屬性如下:
表18. 18 EmployeeProjectsQuery部件主要屬性的取值
━━━━━━━━━━━━━━━━━━━━━━━━━━
屬 性 屬 性 值
──────────────────────────
DatabaseName EmployeeDemoDB
Params EMP_No(輸入參數,Smallint類型)
SQL Select * from
Get_Emp_Proj(:EMP_NO)
━━━━━━━━━━━━━━━━━━━━━━━━━━
TQuery部件是在SQL語句中直接調用存儲過程。
下面是客戶端的程序:
procedure TFrmQueryProc.FormShow(Sender: TObject);
begin
DmEmployee.EmployeeTable.Open;
EmployeeSource.Enabled := True;
with EmployeeProjectsQuery do if not Active then Prepare;
end;
用Prepare顯式地准備SQL語句,雖非必須,但可以優化SQL的執行。
procedure TFrmQueryProc.EmployeeDataChange(Sender: TObject; Field: TField);
begin
EmployeeProjectsQuery.Close;
EmployeeProjectsQuery.Params[0].AsInteger :=
DmEmployee.EmployeeTableEmp_No.Value;
EmployeeProjectsQuery.Open;
WriteMsg('Employee ' + DmEmployee.EmployeeTableEmp_No.AsString +
' is assigned to ' + IntToStr(EmployeeProjectsQuery.RecordCount) +
' project(s).');
end;
該事件處理過程與EmployeeSource的OnDataChange屬性相聯。用於當EmployeeTable數據記錄變化時,修正存儲過程的輸入參數,並執行SQL語句。
2. TStoredProc部件的存儲過程編程
TStoredProc Delphi 專門用來使用服務器存儲過程的部件。CSDEMO 中演示用TStoredProc調用存儲過程的窗體是TFrmExecPr
在程序運行中,當按下ShipOrder按鈕,要求對ORED_STA_TUS等字段的內容作修改以維護數據庫的一致性。字段內容的修改任務由服務器上的存儲過程SHIP_ORDER完成。SHIP_ORDE的程序如下:
PROCEDURE SHIP_ORDER
DECLARE VARIABLE ord_stat CHAR(7);
DECLARE VARIABLE hold_stat CHAR(1);
DECLARE VARIABLE cust_no INTEGER;
DECLARE VARIABLE any_po CHAR(8);
BEGIN
SELECT s.order_status, c.on_hold, c.cust_no
FROM sales s, customer c
WHERE po_number = :po_num
AND s.cust_no = c.cust_no
INTO :ord_stat, :hold_stat, :cust_no;
IF (ord_stat = "shipped") THEN
BEGIN
EXCEPTION order_already_shipped;
SUSPEND;
END
ELSE IF (hold_stat = "*") THEN
BEGIN
EXCEPTION customer_on_hold;
SUSPEND;
END
FOR SELECT po_number
FROM sales
WHERE cust_no = :cust_no
AND order_status = "shipped"
AND paid = "n"
AND ship_date < 'NOW' - 60
INTO :any_po
DO
BEGIN
EXCEPTION customer_check;
UPDATE customer
SET on_hold = "*"
WHERE cust_no = :cust_no;
SUSPEND;
END
UPDATE sales
SET order_status = "shipped", ship_date = 'NOW'
WHERE po_number = :po_num;
SUSPEND;
END
Parameters:
PO_NUM INPUT CHAR(8)
該過程只帶有一個輸入參數:PO_NUM,類型是CHAR(8)。
在客戶端使用該過程的TStoreProc部件是ShipOrderProc,其主要屬性如下表:
表18.19 ShipOrderProc部件主要屬性的取值
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
屬性名 屬 性 值
────────────────────────────
DatabaseName EmployeeDemoDB
ParamBindMode pbByName
Params PO_NUM(輸入參數,String類型)
StoredProcName SHIP_ORDER
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
客戶端執行SHIP_ORDER的程序如下:
procedure TFrmExecProc.BtnShipOrderClick(Sender: TObject);
begin
with DmEmployee do
begin
ShipOrderProc.Params[0].AsString := SalesTable['PO_NUMBER'];
ShipOrderProc.ExecProc;
SalesTable.Refresh;
end;
end;
當用戶按ShipOrder按鈕時,執行這段程序。程序中先准備輸入參數,用ExecProc方
法執行存儲過程。調用SalesTable.Refresh方法刷新數據顯示。