一、 存储过程介绍 存储过程是由流控制和SQL语句书写的过程,这个过程经编译和优化 后存储在数据库服务器中,使用时只要调用即可。在ORACLE中,若干个有联系的过程可以组合在一起构成程序包。 使用存储过程有以下的优点: * 存储过程的能力大大增强了SQL语言的功能和灵活性。存储过程可 以用流控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。 * 可保证数据的安全性和完整性。 # 通过存储过程可以使没有权限的用户在控制之下间接地存取数据库,从而保证数据的安全。 # 通过存储过程可以使相关的动作在一起发生,从而可以维护数据库的完整性。 * 再运行存储过程前,数据库已对其进行了语法和句法分析,并给出 了优化执行方案。这种已经编译好的过程可极大地改善SQL语句的性能。由于执行SQL语句的大部分工作已经完成,所以存储过程能以极快的速度执行。 * 可以降低网络的通信量。 * 使体现企业规则的运算程序放入数据库服务器中,以便: # 集中控制。 # 当企业规则发生变化时在服务器中改变存储过程即可,无须修改 任何应用程序。企业规则的特点是要经常变化,如果把体现企业规则的运算程序放入应用程序中,则当企业规则发生变化时,就需要修改应用程序工作量非常之大(修改、发行和安装应用程序)。如果把体现企业规则的运算放入存储过程中,则当企业规则发生变化时,只要修改存储过程就可以了,应用程序无须任何变化。 二、存储过程的使用 1、存储过程的定义 CREATE PROCEDURE 过程名 (参数说明1,参数说明2, 。。。) IS [局部说明] BEGIN 执行语句; END 过程名;例子一:下面举例说明ORACLE数据库存储过程的写法和用法。可以建立一个存储过程,每当用户修改数据库的重要数据时,即把用户的用户名、日期和操作类型记录下来: create procedure update_log is begin insert into update_log_tab(use_name,update_date,operation) values(user,sysdate,update end; 可以在恰当的位置调用这个存储过程来记录用户对表的修改。例如下面在表sal_comm上建立一个修改触发器,每当用户修改此表后,用户的名称、修改时间和操作即被记录在了表update_log_tab中: create trigger audit_update after update on sal_comm for each row begin update_log end 例子二:.获得返回值 create or replace function overpaid_emp return number is max_mgr_sal number; begin select Count(*) into max_mgr_sal from testtime; return (max_mgr_sal); end; 例子三:调用其他存储过程 begin other_proc(1,2,3); end; 2、游标的使用 PL/SQL 游标为程序提供了从数据库中选择多行数据,然后对每行数据单独进行处理的方法,它为 Oracle 提供了一种指示和控制 SQL 处理的各个阶段的方法。 ○1、什么是游标 Oracle 使用两种游标:显式游标和隐式游标。不管语句返回多少条纪录, PL/SQL 为使用的每一条 UPDATE 、 DELETE 和 INSERT 等 SQL 命令隐式的声明一个游标。(要管理 SQL 语句的处理,必须隐式的给它定义一个游标。)用户声明并使用显示游标处理 SELECT 语句返回的多条记录。显示的定义游标一种结构,它使用户能够为特定的语句指定内存区域,以便以后使用。 ○2、游标的作用 当 PL/SQL 游标查询返回多行数据时,这些记录组被称为活动集。 Oracle 将这种活动集存储在您创建的显示定义的已命名的游标中。 Oracle 游标是一种用于轻松的处理多行数据的机制,没有游标, Oracle 开发人员必须单独地、显式地取回并管理游标查询选择的每一条记录。游标的另一项功能事,它包含一个跟踪当前访问的记录的指针,这使您的程序能够一次处理多条记录。 ○3、使用显示游标的基本方法 A、声明游标 声明游标的语法如下: DECLARE cursor_name Is SELECT statement 其中, cursor_name 是您给游标指定的名称; SELECT statement 是给游标活动集返回记录的查询。 声明游标完成了下面两个目的: 给游标命名; 将一个查询与游标关联起来。 值得注意的是,必须在PL/SQL块的声明部分声明光标;给光标指定的名称是一个未声明的标识符,而不是一个PL/SQL变量,不能给光标名称赋值,也不能将它用在表达式中。PL/SQL块使用这个名称来引用光标查询。例: DECLARE CURSOR c1 Is SELECT VIEW_NAME FROM ALL_VIEWS WHERE ROWNUM<=10 ; 另外还可以在游标定义语句中声明游标的参数,例: CURSOR c1 ( view _nbr number ) Is SELECT VIEW_NAME FROM ALL_VIEWS WHERE ROWNUM<= view _nbr ; 游标参数只对相应的游标是可见的,不能在游标范围之外引用该游标的参数。如果试图这样做, Oracle 将返回一个错误,指出该变量没有定义。 B、打开游标 打开游标的语法: OPEN cursor_name ; 其中 cursor_name 是您以前定义的游标名称。 打开游标将激活查询并识别活动集,可是在执行游标取回命令之前,并没有真正取回记录。 OPEN 命令还初始化了游标指针,使其指向活动集的第一条记录。游标被打开后,直到关闭之前,取回到活动集的所有数据都是静态的,换句话说,游标忽略所有在游标打开之后,对数据执行的 SQL DML 命令( INSERT 、 UPDATE 、 DELETE 和 SELECT )。因此只有在需要时才打开它,要刷新活动集,只需关闭并重新打开游标即可。 C、从游标中取回数据 FETCH 命令以每次一条记录的方式取回活动集中的记录。通常将 FETCH 命令和某种迭代处理结合起来使用,在迭代处理中, FETCH 命令每执行一次,游标前进到活动集的下一条记录。 FETCH 命令的语法: FETCH cursor_name INTO record_list ; 其中, cursor_name 是前面定义的游标的名称; record_list 是变量列表,它接受活动集中的列。 FETCH 命令将活动集的结果放置到这些变量中。 执行 FETCH 命令后,活动集中的结果被取回到 PL/SQL 变量中,以便在 PL/SQL 块中使用。每取回一条记录,游标的指针就移向活动集的下一条记录。 例: FETCH C1 INTO VNAME; WHILE C1%FOUND LOOP DBMS_OUTPUT.PUT_LINE(TO_CHAR(C1%ROWCOUNT)||' '||VNAME); END LOOP; 其中,使用属性 '%FOUND' 使得当 FETCH 到达活动集的结尾时,不会引发异常。其它属性及含义见下表:属性 含量 %FOUND 布尔型属性,当最近一次该记录时成功返回,则值为 TRUE %NOTFOUND 布尔型属性,它的值总与 %FOUND 属性的值相反 %ISOPEN 布尔型属性,当游标是打开时返回 TRUE %ROWCOUNT 数字型属性,返回已从游标中读取的记录数 D、关闭游标 CLOSE 语句关闭以前打开的游标,使得活动集不确定。当用户的程序或会话结束时, Oracle 隐式关闭游标。游标被关闭后,就不能对它执行任何操作了 , 否则将引发异常。 CLOSE 语句的语法是: CLOSE cursor_name ; 其中, cursor_name 是以前打开的游标的名称。 完整的程序代码如下: DECLARE CURSOR C1 IS SELECT VIEW_NAME FROM ALL_VIEWS WHERE ROWNUM<=10 ORDER BY VIEW_NAME; VNAME VARCHAR2(40); BEGIN OPEN C1; FETCH C1 INTO VNAME; WHILE C1%FOUND LOOP DBMS_OUTPUT.PUT_LINE(TO_CHAR(C1%ROWCOUNT)||''||VNAME); END LOOP; END; …… CLOSE C1; 3、在存储过程使用游标 ○1创建包 CREATE package Test_Package AS type Test_Type is ref cursor; end; ○2过程的创建 CREATE PROCEDURE Test_Procedure ( Test_Cursor IN OUT Test_Package.Test_Type) AS BEGIN OPEN Test_Cursor FOR SELECT * FROM Test_Table END Test_Procedure; ○3PL/SQL中进行测试 declare Test_Cursor Test_Package.Test_Type; m_temp Test_Table %rowtype; BEGIN test_procedure(test_cursor); fetch Test_Cursor into m_temp; while Test_Cursor%found loop dbms_output.put_line('ID='||m_temp.ID); dbms_output.put_line('CardNumber='||m_temp.cardnumber); dbms_output.put_line('EnterTime='||m_temp.entertime); fetch Test_Cursor into m_temp; end loop; END; 三、存储过程的应用为了在ASTA中调用Oracle的存储过程,我们首先应在Oracle服务器上建立相应的存储过程。所建立的存储过程示例如下: CREATE OR REPLACE PROCEDURE "SYSTEM"."TEST_PROCEDURE"( TestCursor OUT Test_Package.Test_Type,m_ID in out number) AS TempSet TestTime%RowType; BEGIN --进行返回数据集的操作 OPEN TestCursor FOR SELECT * FROM TestTime where ID=m_ID; m_ID:=400; --返回查询结果的操作 /* OPEN TestCursor FOR SELECT * FROM TestTime where ID=m_ID; Fetch TestCursor Into TempSet; m_ID:=TempSet.EnterTime;*/ END Test_Procedure; 在我们建立好了Oracle的存储过程后,为了能够在ASTA的客户端成功调用该存储过程,我们必须在服务器上建立相应的ServerMethod。实际上就是通过在ASTA服务器上调用相应的存过程后,将得到的结果,传送给客户端。ServerMethod在AstaBusinessObjectManager1中添加。下面分别展示服务器段和客户端的例子。服务器端的方法: procedure TAstaDataModule.AstaBusinessObjectManagerkillActions0Action( Sender: TObject; ADataSet: TDataSet; ClientParams: TAstaParamList); begin if clientparams.ParamByName('inputparam').AsInteger=1 then begin { Adoquery1.Close; Adoquery1.Open; ADataSet:=Adoquery1; } if ADOStoredProckill.Active then ADOStoredProckill.Active:=false; ADOStoredProckill.ProcedureName:='Test_Procedure'; ADOStoredProckill.Prepared; if ADOStoredprockill.Parameters.Count<=0 then ADOStoredprockill.Parameters.CreateParameter('m_id',ftsmallint,pdInputOutput,1,0); ADOStoredprockill.Parameters.ParamByName('m_id').Value:= clientparams.ParamByName('inputparam1').AsInteger; ADOStoredProckill.Open; if ADOStoredProckill.Active then ADataSet:=ADOStoredprockill; ClientParams.ParamByName('Paramtest').AsInteger:=ADOStoredprockill.Parameters.ParamByName('m_id').Value; end; end; 客户端的方法: procedure TForm1.Button1Click(Sender: TObject); begin with AstaclientDataset1 do begin //ServerMethod:='DM.test2'; ParambyName('InputParam').AsInteger:=1; ParambyName('InputParam1').AsInteger:=strtoint(edit1.Text); refiresql; end; end; procedure TForm1.AstaClientDataSet1AfterOpen(DataSet: TDataSet); begin showmessage(inttostr(AstaclientDataset1.ParambyName('paramtest').AsInteger)); end; 在本次测试中,主要实现了下列三种情况: 1.对存储过程含游标的存储过程的调用。 2.对含有游标和参数并且有返回值得存储过程的调用。 3.对含有游标和返回特定数据的存储过程的调用。 4.其他测试:利用ASTADOASERVER,测试DOA的使用,在选择存储过程后,获得的参数有一个空参数,编译未能通过。结果:ASTA联合DOA来使用,即通过客户端设置存储过程的名字的方法行不通。参考:利用该种方式,使用ADO时同样有问题。(针对要返回数据集的情况) 5. 通过在ASTA客户端直接调用存储过程的方式,没有通过测试,包括利用DOA的驱动。(针对要返回数据集的情况) 6.对于无数据集返回的情况,可以在客户端直接调用Oracle的存储过程。