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方法刷新数据显示。