PL/SQL 是对 SQL 语言存储过程语言的扩展
它现在已经成为 一种过程处理语言,简称 PL/SQL。
(有些类似普通编程语言与sql的结合)
DECLARE
/* 声明部分: 在此声明 PL/SQL 用到的变量,类型及游标,以及局部的存储过程和函数 */
BEGIN
/* 执行部分: 过程及 SQL 语句 , 即程序的主要部分 */
EXCEPTION
/* 执行异常部分: 错误处理 */
END;
PL/SQL 块可以分为三类:
例子:
declare
type test_rec is record(
l_name varchar2(30),
d_id number(4) ),
V_emp test_rec;
begin
vemp.l_name:=Tom:
vemp.d_id:=1234;
select last_name,department_id
into v_emp
from employees
where employee_id=200;
dbms_output.put_line(v emp.l_name ||','|| v emp.d_id):
end;
plsql内的赋值号为 :=
程序中 type test_rec is record(……)
是记录类型, 记录类型是把逻辑相关的数据作为一个单元存储起来,称作 PL/SQL RECORD
的域(FIELD
),其作用是存放互不相同但逻辑相关的信息。
定义记录类型语法如下:
TYPE record_type IS RECORD(
Field1 type1 [NOT NULL] [:= exp1 ],
Field2 type2 [NOT NULL] [:= exp2 ],
. . . . . .
Fieldn typen [NOT NULL] [:= expn ] ) ;
对于select …… into v_emp……
可以用 SELECT
语句对记录变量进行赋值,只要保证记录字段与查询结果列表中的字段相配即可。
DBMS_OUTPUT.PUT_LINE
过程的功能类似于 Java 中的 System.out.println()
直接将输出结果送到 标准输出中.
SQL * PLUS
的环境参数 SERVEROUTPUT
设置为 ON
, 否则将看不 到输出结果: set serveroutput on
对于上面例子中记录类型内,除了明确写清是哪个类型,还可以通过%type
来写
例如
declare
type test_rec is record (
l_name employees.last_name%type,
d_id employees.department_id%type );
V_emp test_rec;
begin
end;
使用%TYPE 特性的优点在于:
所引用的数据库列的数据类型可以不必知道;
所引用的数据库列的数据类型可以实时改变。
PL/SQL 提供%ROWTYPE
操作符, 返回一个记录类型, 其数据类型和数据库表的数据结构相一致。
例如:
declare
v_emp empoyees%rowtype;
使用%ROWTYPE
特性的优点在于:
PL/SQL 程序设计中的标识符定义与 SQL 的标识符定义的要求相同。
要求和限制有:
CREATE [OR REPLACE] FUNCTION function_name
[ (argment [ { IN | IN OUT }] Type,
argment [ { IN | OUT | IN OUT } ] Type ]
[ AUTHID DEFINER | CURRENT_USER ]
RETURN return_type
{ IS | AS }
<类型.变量的说明>
BEGIN
FUNCTION_body
EXCEPTION<可选的异常错误处理程序>
其它语句
END;
说明:
OR REPLACE 为可选. 有了它, 可以或者创建一个新函数或者替换相同名字的函数, 而不会出现冲突
函数名后面是一个可选的参数列表, 其中包含 IN, OUT 或 IN OUT 标记. 参数之间用逗号隔开. IN 参数
标记表示传递给函数的值在该函数执行中不改变; OUT 标记表示一个值在函数中进行计算并通过该参
数传递给调用语句; IN OUT 标记表示传递给函数的值可以变化并传递给调用语句. 若省略标记, 则参数
隐含为 IN。
因为函数需要返回一个值, 所以 RETURN 包含返回结果的数据类型.
DECLARE
x NUMBER :=1;
BEGIN
WHILE x<=10 LOOP
DBMS_OUTPUT.PUT_LINE('X 的当前值为:'||x);
x:= x+1;
END LOOP;
END;
FOR 循环计数器 IN [ REVERSE ] 下限 .. 上限 LOOP
要执行的语句;
END LOOP;
每循环一次,循环变量自动加 1;使用关键字 REVERSE,循环变量自动减 1。跟在 IN REVERSE 后面的数字必
须是从小到大的顺序,而且必须是整数,不能是变量或表达式。可以使用 EXIT 退出循环。
例 :
BEGIN
FOR int in 1..10 LOOP
DBMS_OUTPUT.PUT_LINE('int 的当前值为: '||int);
END LOOP;
END;
由此:
create or replace function factorial(
n number)--参数n
return number --返回值类型为number
is
v_sum number;
v_num number;
begin
v_sum:=0;
FOR i IN 1..n LOOP
v_num:=1;
For j IN 1..i LOOP
v_num:=v_num*j;
end loop;
v_sum:=v_sum+v_num;
end loop;
return v_sum;
end;
测试:
SQL> begin
2 dbms_output.put_line(factorial(5));
3 end;
4 /
153
PL/SQL procedure successfully completed
set serveroutput on;
设置环境变量serveroutput为打开状态,从而使得pl/sql程序能够在SQL*plus中输出结果
dbms_output.put_line()
--使用此函数可输出内容到窗口
建立存储过程
oracle函数和存储过程最大的区别就在于,函数必须带上一个return返回值,后面跟的是返回值的类型,而存储过程可以不带任何返回值。
在ORACLE SERVER
上建立存储过程,可以被多个应用程序调用,可以向存储过程传递参数,也可以向存储过程传回参数
CREATE [OR REPLACE] PROCEDURE Procedure_name
[ (argment [ { IN | IN OUT }] Type,
argment [ { IN | OUT | IN OUT } ] Type ]
[ AUTHID DEFINER | CURRENT_USER ]
{ IS | AS }
<类型.变量的说明>
BEGIN
<执行部分>
EXCEPTION
<可选的异常错误处理程序>
END;
这篇文章写得很详细
CREATE OR REPLACE PROCEDURE find_name(
v_name student_lzq.sname%type)
is
--创建游标
cursor c_cursor is select s.cno,c.cname,s.grade
from student_lzq t,sc_lzq s,course_lzq c
where t.sname=v_name and s.sno=t.sno and s.cno=c.cno;
cur c_cursor%rowtype;--定义游标变量,该变量的类型为基于游标c_cursor的记录
BEGIN
--For 循环 for 循环这样的遍历会自动给我们打开游标,并在结束后关闭游标
for cur in c_cursor loop --从游标c_cursor 中取出一个结果cur
exit when c_cursor%notfound; --退出条件exit when c_cursor%notfound;
--即当游标c_cursor 中没有数据了,就退出循环
DBMS_OUTPUT.PUT_LINE('数据是:'||cur.cno||'--'||cur.cname||'--'||cur.grade);
end loop;
END;
写法2:通过fetch
CREATE OR REPLACE PROCEDURE find_name(
v_name student_lzq.sname%type)
is
--创建游标
cursor c_cursor is select s.cno,c.cname,s.grade
from student_lzq t,sc_lzq s,course_lzq c
where t.sname=v_name and s.sno=t.sno and s.cno=c.cno;
cur c_cursor%rowtype;--定义游标变量,该变量的类型为基于游标c_cursor的记录
BEGIN
--Fetch 循环
open c_cursor; --必须要明确的打开和关闭游标
LOOP
FETCH c_cursor INTO cur;
EXIT WHEN c_cursor%NOTFOUND;
--循环体
DBMS_OUTPUT.PUT_LINE('数据是:'||cur.cno||'--'||cur.cname||'--'||cur.grade);
END LOOP;
CLOSE c_cursor;
END;
写法3:通过while循环
CREATE OR REPLACE PROCEDURE find_name(
v_name student_lzq.sname%type)
is
--创建游标
cursor c_cursor is select s.cno,c.cname,s.grade
from student_lzq t,sc_lzq s,course_lzq c
where t.sname=v_name and s.sno=t.sno and s.cno=c.cno;
cur c_cursor%rowtype;--定义游标变量,该变量的类型为基于游标c_cursor的记录
BEGIN
--while 循环
open c_cursor; --必须要明确的打开和关闭游标
FETCH c_cursor INTO cur;
while c_cursor%FOUND LOOP
--循环体
DBMS_OUTPUT.PUT_LINE('数据是:'||cur.cno||'--'||cur.cname||'--'||cur.grade);
FETCH c_cursor INTO cur;
END LOOP;
CLOSE c_cursor;
END;
CREATE OR REPLACE PROCEDURE test_procedure(
v_sno in student_lzq.sno%type,
nc out number,
x out number
)
as
begin
select count(*),avg(s.grade) into nc,x
from sc_lzq s where s.sno=v_sno group by s.sno;
exception
when No_data_found then
dbms_output.put_line('查无此人:'||v_sno);
when others then
dbms_output.put_line('其他错误');
end;
调用 测试:
DECLARE
nc number;
x number;
begin
test_procedure(2013460422,nc,x);
dbms_output.put_line('选课数:'||nc||' 平均分:'||x);
end;
结果
选课数:4 平均分:91.75
PL/SQL procedure successfully completed