Qracle PL/SQL --存储过程 1.PL/SQL SQL是数据库普通话,每个数据库在实现SQL国际标准之外,也有自己特有的语句。(Hibernate dialect) 通讯的标准:ODBC(ado , ado.net),JDBC(jdo , Hibernate) ODBC、JDBC是最有效率的,但是开发繁琐,才有后来括号中的高度函数化的拓展 PL/SQL:Procudural Language Extension to SQL. 在SQL语句基础上,加上了结构化流程控制,可用多条SQL语句完成一个功能。 PL/SQL功能强大,经常用来书写复杂的业务逻辑。 语法: set serveroutput on; //打开控制台输出 declare [变量名] [变量数据类型]; //定义变量,写在begin之前 begin //代码块 end; //代码编辑区 := //赋值符号 dbms_output.put_line('内容'||变量); //控制台输出,和Java不同的是连接使用的是||符号而不是加 号,注意字符串必须用单引号来修饰 范例: declare result int; begin select x into result from ttt where rownum=1; result:=result+1; dbms_output.put_line('result='||result); end; 学生表的创建 create table my_student ( stuno int primary key, stuname varchar2(20) not null, stuage int not null ) 范例2 declare v_stuname varchar2(20); v_stuage int; begin select stuname, stuage into v_stuname,v_stuage from my_student where stuno=2; dbms_output.put_line('sutdent name'||v_stuname||', student age:'||v_stuage); exception when NO_DATA_FOUND then dbms_output.put_line('There is no student with stu no 2'); end; 范例3: new -> program window ->procedure --根据学号显示对应学生信息 create or replace procedure uuu_show_student(x_stuno in int) is v_stuname varchar2(20); v_stuage int; begin select stuname, stuage into v_stuname,v_stuage from my_student where stuno=x_stuno; dbms_output.put_line('sutdent name'||v_stuname||', student age:'||v_stuage); exception when NO_DATA_FOUND then dbms_output.put_line('There is no student with stu no 2'); end uuu_show_student; 运行存储过程 执行execute uuu_show_student; 范例4.两数求和 create or replace procedure show_uuu_number(x_num1 in int,x_num2 in int) is x_big_number int; x_num_index int; begin if x_num1 > x_num2 then x_big_number := x_num1; x_num_index := 1; else x_big_number := x_num2; x_num_index := 2; end if; dbms_output.put_line('big number:'||x_big_number||' index:'||x_num_index); end show_uuu_number; 范例5.循环分支 create or replace procedure show_uuu_number(x_num1 in int,x_num2 in int) is x_big_number int; x_num_index int; begin if x_num1 > x_num2 then x_big_number := x_num1; x_num_index := 1; elsif x_num1 > x_num2 then x_big_number := x_num2; x_num_index := 2; else dbms_output.put_line('equal'); end if; if x_num1 <> x_num2 then dbms_output.put_line('big number:'||x_big_number||' index:'||x_num_index); end if; end show_uuu_number; 》case when.... when.... else ..... and case; 范例6 有返回值的用function 计算三角形面积 create or replace function uuu_area(width in number,height in number) return number is Result number; begin Result := 0.5*width*height; return(Result); end uuu_area; 运行函数 select uuu_area(3,5) from dual; uuu_AREA(3,5) ------------- 15 范例7 》case when.... when.... else ..... and case; 应用 create or replace function show_uuu_day(datestr in varchar2) return varchar2 is Result varchar2(200); checkdate date; v_day varchar2(1); begin checkdate := to_date(trim(datestr), 'YYYY-MM-DD'); v_day := to_char(checkdate, 'D'); case v_day when '1' then Result := datestr || '是星期天'; when '2' then Result := datestr || '是星期一'; when '3' then Result := datestr || '是星期二'; when '4' then Result := datestr || '是星期三'; when '5' then Result := datestr || '是星期四'; when '6' then Result := datestr || '是星期五'; when '7' then Result := datestr || '是星期六'; else dbms_output.put_line(datestr || '是星期六'); end case; return(Result); end show_uuu_day; 执行函数 SQL> select show_uuu_day('2010-11-4') from dual; SHOW_uuu_DAY('2010-11-4') -------------------------------------------------------------------------------- 2010-11-4是星期四 范例8 循环结构 create or replace function uuu_get_sum(num1 in number, num2 in number) return number is Result number := 0; temp number; begin temp := num1; loop Result:=Result+temp; temp:=temp+1; -- if temp > num2 then -- exit; --end if; exit when temp > num2; end loop; return(Result); end uuu_get_sum; 执行函数 SQL> select uuu_get_sum(1,100) from dual; uuu_GET_SUM(1,100) ------------------ 5050 while loop 例: create or replace function uuu_get_sum(num1 in number, num2 in number) return number is Result number := 0; temp number;begin temp := num1; while num2 <= 200 loop Result:=Result+temp; temp:=temp+1; -- if temp > num2 then -- exit; --end if; exit when temp > num2; end loop; return(Result); end uuu_get_sum; 执行函数 SQL> select uuu_get_sum(1,1000) from dual; uuu_GET_SUM(1,1000) ------------------- 0 范例8 数字for loop循环 create or replace procedure sum(begin1 number,end2 number) as tosum number; begin tosum:=0; for i in begin1..end2 loop tosum:=tosum+i; end loop; dbms_output.put_line(tosum); end sum; 001.判断一个数是否为质数 create or replace function x_isprime(num in int) return int is Result int; begin for i in 2..num-1 loop if num mod i = 0 then Result := 0; return(Result); end if; end loop; if num >= 2 then Result := 1; end if; return(Result); end x_isprime; 002判断质数 create or replace procedure x_prime(begini in int, endi in int) is count2 int := 0; begin for i in begini..endi loop if x_isprime(i) = 1 then dbms_output.put(i||' '); count2:=count2+1; if count2 mod 8 = 0 then dbms_output.put_line(''); end if; end if; end loop; end x_prime; ====== debug 权限设置 grant debug connect session to test1; 003判断质数 ======= CREATE OR REPLACE PROCEDURE show_prime(bval IN INT, eval IN INT) IS flag INT; icount int:=0; BEGIN -- 取数循环 FOR i IN bval .. eval LOOP -- 质数判断循环 flag := 1; FOR j IN 2 .. i - 1 LOOP IF i MOD j = 0 THEN flag := 0; EXIT; END IF; END LOOP; IF flag = 1 THEN dbms_output.put(i||' '); icount:=icount+1; if icount mod 8 =0 then dbms_output.put_line(''); end if; END IF; END LOOP; END show_prime; ====================================================================== --游标 pl/sql cursor 操作 1. 什么是游标? oracle在执行一条SQL语句的时候,它将创建一个内存区域 (context area),该内存区域包含执行这条语句所需要的所有信息。 信息如下: 1. 该语句执行之后返回的记录集 2. 一个指针,指向了该语句在内存中的被解析后的结果。 cursor(游标) 是一个handle (pointer), 指向了这个上下文区域。 通过cursor, PL/SQL程序能够控制context area, 掌握在语句运行的时,将如何对该区域产生影响。 2. 游标的类型 1) implicit cursor 每条SQL语句执行的时候,将自动产生一个implicit游标。 该游标,用户不可控制。 一个cursor将自动和每条DML语句关联 (update,delete,insert), 我们可以通过cursor了解上头语句产生的结果。 所有update和delete语句相关联cursor,包含了该操作影响的行的集合。 最后打开的cursor, 名字叫SQL cursor. -------------------------------------------------------------------------------------------- update my_student set stuname='mary' where stuno=60; dbms_output.put_line(SQL%ROWCOUNT); 2) explicit cursor 用户自己定义的游标,针对的是返回超过一条记录的查询。 用户可以通过该cursor控制记录返回过程。 Record Type 记录是复杂的数据结构。记录往往表现成为表的一行。 create or replace procedure show_student2 is vr_student my_student%ROWTYPE; begin select * into vr_student from my_student where stuno=6; dbms_output.put_line(vr_student.stuno||','||vr_student.stuname); end show_student2; a. 声明游标 (declare a cursor) 该操作初始化这个游标,为其创建内存空间。 CURSOR c_cursor_name is select statement (该游标和select语句相关联) ------------------ declare v_name varchar2(20); CURSOR c_mycursor is select * from student where name like '%h%'; 游标打开后,不能继续再二次打开。 b. 打开游标 (open cursor) 创建context area, 执行语句, 获得rows. open c_mycursor c. 获取cursor中的行。 fetch cursorname into pl/sql variables fetch cursorname into pl/sql record d. 关闭cursor 一旦所有的行被处理结束,cursor应该被关闭。 关闭的操作通知pl/sql engine, 程序对该cursor的需求已经结束,可以释放context are所占用的内存资源。 cursor一旦关闭,则不可以继续fetch . 也不能重复关闭cursor. > 常用的cursor属性 cursorname%NOTFOUND cursorname%FOUND cursorname%ROWCOUNT cursorname%ISOPEN -------------------- create or replace procedure show_student2 is CURSOR c_student is select * from my_student order by stuno desc; vr_student my_student%ROWTYPE; TYPE simple_stu is record ( stuname my_student.stuname%TYPE, stuage my_student.stuage%TYPE, stuage2 int ); vr_simple_student simple_stu; cursor c_simple_student is select stuname,stuage,stuage+2 from my_student order by stuno desc; begin /* select * into vr_student from my_student where stuno=6; dbms_output.put_line(vr_student.stuno||','||vr_student.stuname); */ open c_student; loop fetch c_student into vr_student; exit when c_student%NOTFOUND; dbms_output.put_line(vr_student.stuno||','||vr_student.stuname); end loop; if c_student %ISOPEN then close c_student; end if; open c_simple_student; loop fetch c_simple_student into vr_simple_student; exit when c_simple_student%NOTFOUND; dbms_output.put_line(vr_simple_student.stuname||','||vr_simple_student.stuage ||','||vr_simple_student.stuage2); end loop; close c_simple_student; end show_student2; -------------------------------------------- cursor for loop nested cursor DECLARE v_sid student.student_id%TYPE; CURSOR c_student IS SELECT student_id, first_name, last_name FROM student WHERE student_id < 110; CURSOR c_course IS SELECT c.course_no, c.description FROM course c, section s, enrollment e WHERE c.course_no = s.course_no AND s.section_id = e.section_id AND e.student_id = v_sid; BEGIN /* 使用for loop cursor, 可以便捷的遍历游标,省去了open,fetch,close的书写。 连保存变量的定义也可以省略,可在for后直接书写变量名。 */ FOR r_student IN c_student LOOP v_sid := r_student.student_id; DBMS_OUTPUT.PUT_LINE(chr(10)); DBMS_OUTPUT.PUT_LINE(' The Student ' || r_student.student_id || ' ' || r_student.first_name || ' ' || r_student.last_name); DBMS_OUTPUT.PUT_LINE(' is enrolled in the ' || 'following courses: '); -- nested cursor FOR r_course IN c_course LOOP DBMS_OUTPUT.PUT_LINE(r_course.course_no || ' ' || r_course.description); END LOOP; END LOOP; END; --------------------------------------------------------------- 带参数的游标 CURSOR c_student(p_stuage in my_student.stuage%type) is select * from my_student where stuage=p_stuage order by stuno desc; for vr_student in c_student(20) loop dbms_output.put_line(vr_student.stuno||','||vr_student.stuname); end loop; CURSOR c_student2(p_stuage in my_student.stuage%type,p_stuname in my_student.stuname%TYPE) is select * from my_student where stuage=p_stuage order by stuno desc; cursor具备的参数: 1) cursor得到了复用。 2) 提高了性能,压缩了返回的行的数量。 ==================================================================== --异常 1.PL/SQL Exception 常用的预定义异常 a)NO_DATA_FOUND 根据查询条件,没有查询记录被找到 b)TOO_MANY_ROWS select into 结构只能返回一条记录,赋予存储过程变量。如果select ..into..结构返回记录为多条,将产生这个异常 c)ZERO_DIVIDE 除数是0(把ORA-01476 error映射成ZERO_DIVIDE错误) 例: SQL> select 6/0 from dual; select 6/0 from dual ORA-01476: 除数为 0 d)VALUE_ERROR 从运算或者数据库中取得值赋予变量的时候类型不匹配或者长度不足,导致的异常 e)DUP_VAL_ON_INDEX 主键不可重复,违反主键唯一约束 f)OTHERS (类似java Exception异常) 数据库表 create table my_student( stuno int primary key, stuname varchar2(20), stuage int ); insert into my_student values(1,'dadiv',20); insert into my_student values(2,'mary',20); insert into my_student values(3,'henry',20); 异常举例例子: create or replace procedure uuu_show_student(x_stuno in int) is v_stuname varchar2(20);--替换v_stuname varchar2(2)/int值不匹配或者长度不足异常 v_stuage int; begin insert into my_student values(2,'kate',49);--DUP_VAL_ON_INDEX异常 select stuname, stuage into v_stuname,v_stuage from my_student where stuno=x_stuno;--加上or stuname like '%y'产生值记录太多异常 dbms_output.put_line('sutdent name'||v_stuname||', student age:'||v_stuage); exception when NO_DATA_FOUND then dbms_output.put_line('There is no student with stu no '||x_stuno); when VALUE_ERROR then dbms_output.put_line('值不匹配'); when TOO_MANY_ROWS then dbms_output.put_line('记录太多'); when DUP_VAL_ON_INDEX then dbms_output.put_line('主键不可重复,插入失败'); when OTHERS then dbms_output.put_line('其它异常捕获');--一些预定义异常的父类 end uuu_show_student; 异常的作用域 DECLARE v_student_id NUMBER := &sv_student_id; v_name VARCHAR2(30); v_total NUMBER(1); -- outer block BEGIN SELECT RTRIM(first_name)||' '||RTRIM(last_name) INTO v_name FROM student WHERE student_id = v_student_id; DBMS_OUTPUT.PUT_LINE ('Student name is '||v_name); -- inner block BEGIN SELECT COUNT(*) INTO v_total FROM enrollment WHERE student_id = v_student_id; DBMS_OUTPUT.PUT_LINE ('Student is registered for '|| v_total||' course(s)'); EXCEPTION WHEN VALUE_ERROR OR INVALID_NUMBER THEN DBMS_OUTPUT.PUT_LINE ('An error has occurred'); END; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE ('There is no such student'); END; 自定义异常: 例: create or replace procedure uuu_show_student(x_stuno in int) is v_stuname varchar2(20);--替换v_stuname varchar2(2)/int值不匹配或者长度不足异常 v_stuage int; e_invalid_stuno EXCEPTION;--自定义异常 begin if x_stuno < 0 then raise e_invalid_stuno; else --insert into my_student values(2,'kate',49);--DUP_VAL_ON_INDEX异常 select stuname, stuage into v_stuname,v_stuage from my_student where stuno=x_stuno;--加上or stuname like '%y'产生值记录太多异常 dbms_output.put_line('sutdent name'||v_stuname||', student age:'||v_stuage); end if; exception when NO_DATA_FOUND then dbms_output.put_line('There is no student with stu no '||x_stuno); when VALUE_ERROR then dbms_output.put_line('值不匹配'); when TOO_MANY_ROWS then dbms_output.put_line('记录太多'); when e_invalid_stuno then --控制台输入execute uuu_show_student(-2); dbms_output.put_line('学生编号不合法'); when DUP_VAL_ON_INDEX then dbms_output.put_line('主键不可重复,插入失败'); when OTHERS then dbms_output.put_line('其它异常捕获');--一些预定义异常的父类 end uuu_show_student; ================================================================== --触发器的一个例子 create or replace trigger student_aud before insert on my_student for each row declare v_highage int; begin select stu_highage into v_highage from stu_stat; if :NEW.stuage >25 then v_highage:=v_highage+1; end if ; update stu_stat set stu_count=stu_count+1,stu_highage=v_highage; end student_aud;