一般的SQL语句已经能支撑大规模的增删改查了,但有时候我们不仅要查出数据,还要对查出来的数据做进一步的加工,就像编程语言对数据的加工一样,在编程语言中,通过一系列方法,数据从入到出,变成了我们想要的模样,SQL也提供了类似的功能,唯一的区别是,SQL编程没有返回值,你可以直接对查出来的数据进行编程,结果直接展现在数据库中,这种模式叫做 PL SQL,全称Procedure Language SQL
中文:过程化SQL
如果你已有一门编程语言基础,那么以下内容将不费吹灰之力,尽数拿下
PL/SQL(Procedure Language/SQL)(过程语言/SQL)是Oracle对sql语言过程化扩展,把SQL语言的数据操纵能力与过程语言的数据能力结合起来
update emp set sal=sal*1.1
–>使用pl/sql实现
declare
说明部分(变量说明、游标申明、例外说明)
begin
语句序列 (DML语句) ..
exception
例外处理语句
End ;
变量的基本类型就是oracle中建表时字段的变量,如char,varchar2,date,number,boolean,long)
定义语法:
varl char(15) ;
Psal number(9,2) ;
/*说明变量名、数据类型和长度,分号必给*/
###############################################
/*常量定义*/
marrieed boolean:=true
##############################################
/*引用变量*/
Myname emp.ename%type
/*引用型变量Myname的类型与emp表中ename列的类型一样,在sql中使用into来赋值*/
declare
emprec emp.name%type ;
begin
select t.ename into emprec from emp t where t.empno = 7369 ;
dbms_output.put_line(emprec) ;
/*输出emprec的值*/
end ;
#############################################
/*记录型变量*/
/*Emprec emp%rowtype*/
/*记录变量分量(可以对应Java中的对象类型变量)的引用*/
/*emp rec.ename:='ADAMS';*/
declare
p emp%rowtype;
/*rowtype表emp中的一行当作一个类型*/
begin
select * into p from emp t where t.empno = 7369 ;
dbms_output_put_line(p.ename||''||p.sal) ;
/* ||表示连接 */
end ;
if 条件 then 语句1;
语句2;
end if;
if 条件 then 语句序列1;
else 语句序列 2;
end if ;
if 条件 then 语句;
elseif 语句 then 语句;
else 语句;
end if;
declare
pnum number :=&num ;
/* :=& * 表示输入并赋值*/
begin
if pnum =1 then
dbms_output.put_line('我是1') ;
end if ;
end ;
declare
pnum number :=&num ;
/* :=& * 表示输入并赋值*/
begin
if pnum =1 then
dbms_output.put_line('我是1') ;
else
dbms_output.put_line('我不是1') ;
end if ;
end ;
declare
mynum number := #
begin
if mynum < 18 then
dbms_output.put_line('未成年人');
elsif mynum >= 18 and mynum < 40 then
dbms_output.put_line('中年人');
elsif mynum >= 40 then
dbms_output.put_line('老年人');
end if;
end;
语法1
WHILE total <= 25000 LOOP
...
total : = total + salary;
END LOOP;
语法2(最常用)
Loop
EXIT [when 条件];
……
End loop
语法3
FOR I IN 1 . . 3 LOOP
语句序列 ;
END LOOP ;
declare
step number :=1 ;
begin
while step<= 10 loop
dbms_output.put_line(step);
step := step + 1 ;
end loop ;
end ;
declare
step number :=1 ;
begin
loop
exit when step>10 ;
dbms_output.put_line(step);
step := step + 1 ;
end loop ;
end ;
declare
step number :=1 ;
begin
for step in 1..10 loop
dbms_output.put_line(step);
end loop ;
end ;
在写java程序中我们有集合的概念,那么在pl/sql中也会用到多条记录,这时我们就要用到游标,游标可以存储查询返回的多条数据
语法:
CURSOR 游标名 [ (参数名 数据类型,参数名 数据类型,...)] IS SELECT 语句;
例如:
curror cl is select ename from emp ;
/*无参游标*/
注意:上面的pjob必须与emp表中的job列类型一样
declare
/*无参游标,记录emp表所有*/
cursor pc is
select * from emp ;
/*定义记录型变量*/
pemp emp%rowtype ;
begin
open pc ;
loop
fetch pc
into pemp
exit when pc%notfound ;
dbms_output.put_line(pemp.empno || ''|| pemp.ename);
end loop ;
close pc ;
end ;
declare
cursor pc is
select * from myemp;
addsal myemp.sal%type;
pemp myemp%rowtype;
begin
open pc;
loop
fetch pc
into pemp;
exit when pc%notfound;
if pemp.job = 'PRESIDENT' then
addsal := 1000;
elsif pemp.job = 'MANAGER' then
addsal := 800;
else
addsal := 400;
end if;
update myemp t set t.sal = t.sal + addsal where t.empno = pemp.empno;
end loop;
close pc;
end;
declare
cursor pc(dno myemp.deptno%type) is
select empno from myemp where deptno = dno;
/*保存empno*/
pno myemp.empno%type;
begin
/*打开游标并传入参数10 */
open pc(10);
loop
fetch pc
into pno;
exit when pc%notfound;
update myemp t set t.sal = t.sal + 1000 where t.empno = pno;
end loop;
close pc;
end;
例外是程序设计语言提供的一种功能,用来增强程序的健壮性和容错性
系统定义的例外
系统例外-示例
declare
pnum number;
begin
pnum := 1 / 0;
exception
when zero_divide then
dbms_output.put_line('被0除');
when value_error then
dbms_output.put_line('数值转换错误');
when others then
dbms_output.put_line('其他错误');
end;
用户自定义例外,在声明中来定义例外
DECLARE
My_job char(10);
v_sal emp.sal%type;
No_data exception; /*定义异常于此*/
cursor c1 is select distinct job from emp order by job;
declare
/*异常定义*/
no_emp_found exception;
cursor pemp is
select t.ename from emp t where t.deptno = 50;
/*存储*/
pename emp.ename%type;
begin
open pemp;
fetch pemp
into pename;
if pemp%notfound then
/*抛出异常*/
raise no_emp_found;
end if;
close pemp;
exception /*例外处理语句*/
when no_emp_found then
dbms_output.put_line('没有找到员工');
when others then
dbms_output.put_line('其他错误');
end;
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。
create [or replace] PROCEDURE 过程名[(参数名 in/out 数据类型)]
AS
begin
PLSQL子程序体;
End;
或者
create [or replace] PROCEDURE 过程名[(参数名 in/out 数据类型)]
is
begin
PLSQL子程序体;
End 过程名;
create or replace procedure helleworld is
begin
dbms_output.put_line('helloworld');
end helloword;
/*调用存储过程*/
begin
helloworld
end;
create or replace procedure addSal(eno in number) is
pemp myemp%rowtype;
begin
select * into pemp from myemp where empno = eno;
update myemp set sal = sal + 100 where empno = eno;
dbms_output.put_line('涨工资前' || pemp.sal || '涨工资后' || (pemp.sal + 100));
end addSal ;
/*调用*/
begin
addsal(eno => 7902) ;
commit ;
end ;
create or replace function 函数名(Name in type, Name in type, ...) return 数据类型 is
结果变量 数据类型;
begin
return(结果变量);
end函数名;
一般来讲,过程和函数的区别在于函数可以有一个返回值;而过程没有返回值。 但过程和函数都可以通过out指定一个或多个输出参数。我们可以利用out参数,在过程和函数中实现返回多个值。
范例:使用存储函数查询指定员工的年薪
create or replace function empincome(eno in emp.empno%type)
return number is
psal emp.sal%type ;
pcomm emp.comm%type ;
begin
select t.sal into psal from emp t where t.empno = eno ;
return psal*12+nvl(pcomm,0) ;
end ;
存储过程版:
create or replace procedure empincomep(eno in emp.empno%type, income out number) is
/*income out -- > return*/
psal emp.sal%type;
pcomm emp.comm%type;
begin
select t.sal, t.comm into psal, pcomm from emp t where t.empno = eno;
income := psal * 12 + nvl(pcomm, 0);
/*can return */
end empincomep ;
/*调用*/
调用:
declare
income number;
begin
empincomep(7369, income);
dbms_output.put_line(income);
end;
使用JAVA代码调用存储过程和存储函数
/*存储过程*/
Class.forName("oracle.jdbc.OracleDriver");
Connection conn = null;
conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl",
"scott", "tiger");
/*获得CallableStatment对象,指定存储函数*/
CallableStatement call = conn.prepareCall("{call countyearsal(?,?)}");
/*1代表第一个参数(?)*/
call.setInt(1, 7369);
/*注册oracle的输出参数类型*/
call.registerOutParameter(2, OracleTypes.NUMBER);
/*执行存储过程*/
call.execute();
/*获得返回值*/
int sum = call.getInt(2);
System.out.println(sum);
/*存储函数*/ Class.forName("oracle.jdbc.OracleDriver");
Connection conn = null;
conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl",
"scott", "tiger");
/*获得CallableStatment对象,指定存储函数*/
CallableStatement call = conn.prepareCall("{?= call countyearsal1(?)}");
/*注册oracle的输出参数类型*/
call.registerOutParameter(1, OracleTypes.NUMBER);
call.setInt(2, 7369);
/*执行存储函数*/
call.execute();
/*获得返回值*/
int sum = call.getInt(1);
System.out.println(sum);
语法:
CREATE [or REPLACE] TRIGGER 触发器名
{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [OF 列名]}
ON 表名
[FOR EACH ROW [WHEN(条件) ] ]
begin
PLSQL 块
End 触发器名
create or replace trigger testTrigger
after insert on person
declare
begin
dbms_output.put_line('一个员工被插入');
end testTrigger ;
create or replace trigger validInsertPerson
before insert on person
declare
weekend varchar2(10);
begin
select to_char(sysdate, 'day') into weekend from dual;
if weekend in ('星期一') then
raise_application_error(-20001, '不能在非法时间插入员工');
end if;
end validInsertPerson;
触发语句\ | :old | :new |
---|---|---|
Insert | 所有字段都是空(null) | 将要插入的数据 |
Update | 更新以前该行的值 | 更新后的值 |
delete | 删除以前该行的值 | 所有字段都是空(null) |
create or replace trigger addsal4p
before update of sal on myemp
for each row
begin
if :old.sal >= :new.sal then
raise_application_error(-20002, '涨前的工资不能大于涨后的工资');
end if;
end;
调用
update myemp t set t.sal = t.sal - 100 where t.empno = 7369;