首页 > 代码库 > PL/SQL和Oracle对象

PL/SQL和Oracle对象

技术分享

技术分享

PL/SQL

-一、变量

01)引用变量

-- 查询并打印7839的姓名和薪水
declare
   --定义变量保存姓名和薪水
   --pename varchar2(20);
   --psal   number;
   pename emp.ename%type;
   psal   emp.sal%type;
begin
  -- 得到1234的姓名和薪水
  select ename,sal into pename,psal from emp where empno=1234;
  
  --打印
  dbms_output.put_line(pename||‘的薪水是‘||psal);
end;
/


02)记录型变量

-- 查询并打印7839的姓名和薪水
declare
  -- 定义记录型变量:代表一行
  emp_rec emp%rowtype;
begin
  select * into emp_rec from emp where empno=7839;  
 
  dbms_output.put_line(emp_rec.ename||‘的薪水是‘||emp_rec.sal);  
end;
/


 

03)常量

Var1 constant varchar(13);

 

-二、语句

01If语句

-- 判断用户从键盘输入的数字
--接受键盘输入
--num: 地址值,在该地址上保存了输入的值
accept num prompt ‘请输入一个数字:‘;
 
declare
  -- 定义变量保存数字
  pnum number := #
begin
  if pnum = 0 then dbms_output.put_line(‘您输入的是0‘);
     elsif pnum = 1 then dbms_output.put_line(‘您输入的是1‘);
     elsif pnum = 2 then dbms_output.put_line(‘您输入的是2‘);
     else dbms_output.put_line(‘其他数字‘);
  end if;
end;
/


 

02)循环语句

-- 打印1~10
declare
  pnum number := 1;
begin
  loop
       --循环
       --退出条件
       exit when pnum > 10;
  
       dbms_output.put_line(pnum);
       --加一
       pnum := pnum + 1;
  end loop;
end;
/


-三光标(游标)

(作用类似Java中的数组)

光标的属性:

%isopen

%rowcount(影响的行数)

%found

%notfound

 

基本操作:

初始化:cursor c1 is select ename from emp;

打开:open c1;

取一行游标的值:fetch c1 into pjob;

关闭:close c1;

结束光标:exist when c1%notfound

-- 查询并打印员工的姓名和薪水
/*
光标的属性:
%isopen   %rowcount(影响的行数)  %found  %notfound
*/
declare
  -- 定义光标
  cursor cemp is select ename,sal from emp;
  pename emp.ename%type;
  psal   emp.sal%type;
begin
  -- 打开光标
  open cemp;
  
  loop
       --取当前记录
       fetch cemp into pename,psal;
       --exit when 没有取到记录;
       exit when cemp%notfound;
  
       dbms_output.put_line(pename||‘的薪水是‘||psal);
  
  end loop;
  
  --关闭光标
  close cemp;
end;
/
-- 涨工资,总裁1000 经理800 其他400
declare
  -- 定义光标
  cursor cemp is select empno,job from emp;
  pempno emp.empno%type;
  pjob   emp.job%type;
begin
  rollback;  
 
 --打开光标
 open cemp;
 
 loop
      --取一个员工
      fetch cemp into pempno,pjob;
      exit when cemp%notfound;
      
      --判断职位
      if pjob = ‘PRESIDENT‘ then update emp set sal=sal+1000 where empno=pempno;
         elsif pjob = ‘MANAGER‘ then update emp set sal=sal+800 where empno=pempno;
         else update emp set sal=sal+400 where empno=pempno;
      end if;
 end loop;
 --关闭光标
 close cemp;
 
 --提交  ---> ACID
 commit;
 
 dbms_output.put_line(‘完成‘);
end;
/

 带参数的光标

-- 查询某个部门的员工姓名
declare
  cursor cemp(dno number) is select ename from emp where deptno=dno;
  pename emp.ename%type;
begin
  open cemp(20);
  loop
       fetch cemp into pename;
       exit when cemp%notfound;
  
       dbms_output.put_line(pename);
  
  end loop;
  close cemp;
end;
/


 

-四、例外

01.实例一

/*
SQL语句
select to_char(hiredate,‘yyyy‘) from emp;
--->光标 --> 循环 --> 退出条件:notfound
 
变量:1. 初始值  2.最终得到
count80 number := 0;
count81 number := 0;
count82 number := 0;
count87 number := 0;
*/
declare
  --入职年份
  cursor cemp is select to_char(hiredate,‘yyyy‘) from emp;
  phiredate varchar2(4);
  
  --每年入职的人数
  count80 number := 0;
  count81 number := 0;
  count82 number := 0;
  count87 number := 0;  
begin
  -- 打开光标
  open cemp;
  loop
       --取一个员工的入职年份
       fetch cemp into phiredate;
       exit when cemp%notfound;
       
       --判断年份
       if phiredate = ‘1980‘ then count80:=count80+1;
          elsif phiredate = ‘1981‘ then count81:=count81+1;
          elsif phiredate = ‘1982‘ then count82:=count82+1;
          else count87:=count87+1;
       end if;
  
  end loop;
  
  --关闭
  close cemp;
  
  --输出
  dbms_output.put_line(‘Total:‘||(count80+count81+count82+count87));
  dbms_output.put_line(‘1980:‘||count80);
  dbms_output.put_line(‘1981:‘||count81);
  dbms_output.put_line(‘1982:‘||count82);
  dbms_output.put_line(‘1987:‘||count87);  
end;
/

02.实例二 

/*
SQL语句
select empno,sal from emp order by sal
---> 光标 --> 循环  --> 退出条件:1. 总额>5w   2. notfound
 
变量:1. 初始值  2.最终得到
涨工资的人数: countEmp number := 0;
涨后的工资总额:salTotal number;
1.select sum(sal) into salTotal from emp;
2.涨后=涨前+sal*0.1
 
练习:涨后的工资总额:50205.325   人数:8
*/
declare
  cursor cemp is select empno,sal from emp order by sal;
  pempno emp.empno%type;
  psal   emp.sal%type;
  --涨工资的人数:
  countEmp number := 0;
  --涨后的工资总额:
  salTotal number;
begin
  -- 得到初始的工资总额
  select sum(sal) into salTotal from emp;
  
  open cemp;
  loop
       --1. 总额>5w
       exit when salTotal > 50000;
       --取一个员工涨工资
       fetch cemp into pempno,psal;
       --2. notfound
       exit when cemp%notfound;
       
       --涨工资
       update emp set sal=sal*1.1 where empno=pempno;
       --人数+1
       countEmp:= countEmp+ 1;
       
       --2.涨后=涨前+sal*0.1
       salTotal := salTotal + psal*0.1;
  
  end loop;
  close cemp;
   commit;
  dbms_output.put_line(‘涨后的工资总额:‘||salTotal||‘   人数:‘||countEmp);
  
end;
/

 

03.实例三

/*
SQL语句
部门:select deptno from dept;
查部门中的员工薪水: select sal from emp where deptno=??
 
变量:1. 初始值  2.最终得到
每个段的人数
count1 number; count2 number; count3 number;
部门的工资总额
salTotal number;
1. select sum(sal) into salTotal  from emp where deptno=??
2. 累加
*/
declare
  --部门
  cursor cdept is select deptno from dept;
  pdeptno dept.deptno%type;
  
  --部门中员工的薪水
  cursor cemp(dno number) is select sal from emp where deptno=dno;
  psal emp.sal%type;
  
  --每个段的人数
  count1 number; count2 number; count3 number;
  --部门的工资总额
  salTotal number; 
  begin
  open cdept;
  loop
       --取一个部门
       fetch cdept into pdeptno;
       exit when cdept%notfound;
       
       --初始化
       count1:=0; count2:=0; count3:=0;
       --部门的工资总额
       select sum(sal) into salTotal  from emp where deptno=pdeptno;
       
       
       --取部门中员工的薪水
       open cemp(pdeptno);
       loop
            --取一个员工的薪水
            fetch cemp into psal;
            exit when cemp%notfound;
            
            if psal <3000 then count1:=count1+1;
               elsif psal>=3000 and psal<6000 then count2:=count2+1;
               else count3:=count3+1;
            end if;
             end loop;
       close cemp;
       
       --保存结果
       insert into msg values(pdeptno,count1,count2,count3,nvl(salTotal,0));
  
  end loop;
  close cdept;
  
  commit;
  dbms_output.put_line(‘完成‘);
  
end;
/

Oracle对象

-一、存储过程

Java中不能直接访问PL/SQL,只能访问存储过程和存储函数,存储过程和存储函数是由PL/SQL编写的。

存储过程是指存储在数据库中供所有用户程序调用的子程序。

存储函数有return返回值,而存储过程没有。

由于存储过程是一个供用户调用的子程序,所以一般不在存储过程中提交或者回滚数据库,由调用者执行。

 

01.不带参数的存储过程

/*
第一个存储过程:打印HelloWorld
 
调用存储过程:
1. exec sayHelloWorld();
2. begin
     sayHelloWorld();
     sayHelloWorld();
   end;
   /
*/
create or replace procedure sayHelloWorld
as
begin
  dbms_output.put_line(‘Hello World‘);  
end sayHelloWorld;
/


02.带参数的存储过程

/*
给指定的员工涨100,并且打印涨前和涨后的薪水
*/
create or replace procedure raiseSalary(eno in number)
as
   --定义变量保存涨前的薪水
   psal emp.sal%type;
begin
   --得到涨前的薪水
   select sal into psal from emp where empno=eno;
 
   --涨100
   update emp set sal=sal+100 where empno=eno;
   
   --要不要commit?
 
   dbms_output.put_line(‘涨前:‘||psal||‘  涨后:‘||(psal+100));
end;
/

存储函数是一个PL/SQL程序。函数与过程的结构类似,但必须有一个return子句。

但是过程和函数都可以通过out指定一个或多个输出参数。我们可以利用out参数,在过程和函数中实现返回多个值。

 

--查询某个员工的年收入
 create or replace function queryEmpIncome(eno in number)
 return number
 as
    --月薪和奖金
    psal emp.sal%type;
    pcomm emp.comm%type;
 begin
    --得到月薪和奖金
    select sal,comm into psal,pcomm from emp where empno=eno;
 
    --返回年收入
    return psal*12+nvl(pcomm,0);
 end;
 /

技术分享

  

右击函数-->test

技术分享

 

可以通过:dbms_output.put_line(:result);得到数据

SQL> select sal*12+nvl(comm,0) from emp where empno=7839;检查

 

补:Out参数

存储过程和存储函数不同在于一个有return返回一个没有。

但是存储过程和存储函数都可以通过out指定一个或者多个输出参数。我们可以利用out参数在存储过程和存储函数中返回多个值。

有了out返回之后,存储过程是可以替代存储函数的,但是由于要兼容老版本,所以存储函数还存在。

/*
查询某个员工的姓名 月薪 职位
 
思考:
1. 查询某个员工的所有信息 ---> out参数太多
2. 查询某个部门中的所有员工信息  ---> 返回集合
*/
create or replace procedure queryEmpInformation(eno in number,
                                                pename out varchar2,
                                                psal   out number,
                                                pjob   out varchar2)
as
begin
  
   select ename,sal,job into pename,psal,pjob from emp where empno=eno;                                               
end;
/

 

-三、包

CREATE OR REPLACE
PACKAGE MYPACKAGE AS
 
  type empcursor is ref cursor;
  procedure queryEmpList(dno in number,empList out empcursor);
 
END MYPACKAGE;



-四、包头

CREATE OR REPLACE
PACKAGE BODY MYPACKAGE AS
 
  procedure queryEmpList(dno in number,empList out empcursor) AS
  BEGIN
   
   open empList for select * from emp where deptno=dno;
   
    NULL;
  END queryEmpList;
 
END MYPACKAGE;


-Java调用Oracle

01.导包:


技术分享

技术分享

 

02.建立类

技术分享 

package com.my.utils;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
 
public class JDBCUtils {

private static String driver="oracle.jdbc.OracleDriver";
private static String url="jdbc:oracle:thin:@192.168.83.10:1521/orcl";
private static String user="scott";
private static String password="tiger";

static{
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}

public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
rs=null;
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
st=null;
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
conn=null;
}
}
}
}

package com.my.utils;
 
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
 
import org.junit.Test;
 
import oracle.jdbc.OracleTypes;
import oracle.jdbc.driver.OracleCallableStatement;
import oracle.jdbc.oracore.OracleType;
 
public class TestOracle {
 
/*
 * 存储过程(out参数)
 * 
查询某个员工的姓名 月薪 职位
思考:
1. 查询某个员工的所有信息 ---> out参数太多
2. 查询某个部门中的所有员工信息  ---> 返回集合
*
create or replace procedure queryEmpInformation(eno in number,
            pename out varchar2,
            psal   out number,
            pjob   out varchar2)
as
begin
   select ename,sal,job into pename,psal,pjob from emp where empno=eno;                                               
end;
/
 * */
@Test
public void testProcedure(){
String sql="{call queryEmpInformation(?,?,?,?)}";

Connection conn=null;
CallableStatement call=null;

try {
conn=JDBCUtils.getConnection();
call=conn.prepareCall(sql);

call.setInt(1, 7839);
call.registerOutParameter(2, OracleTypes.VARCHAR);
call.registerOutParameter(3, OracleTypes.NUMBER);
call.registerOutParameter(4, OracleTypes.VARCHAR);

call.execute();
String name=call.getString(2);
double sal=call.getDouble(3);
String job=call.getString(4);
System.out.println(name+"\t"+sal+"\t"+job);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.release(conn, call, null);
}
}

/*
 * 存储函数
 *
 * --查询某个员工的年收入
 create or replace function queryEmpIncome(eno in number)
 return number
 as
    --月薪和奖金
    psal emp.sal%type;
    pcomm emp.comm%type;
 begin
    --得到月薪和奖金
    select sal,comm into psal,pcomm from emp where empno=eno;
    --返回年收入
    return psal*12+nvl(pcomm,0);
 end;
 /
 * */
 
 @Test
public void testfunction(){
String sql="{?=call queryEmpIncome(?)}";

Connection conn=null;
CallableStatement call=null;

try {
conn=JDBCUtils.getConnection();
call=conn.prepareCall(sql);

call.registerOutParameter(1, OracleTypes.NUMBER);

call.setInt(2,7839);
call.execute();
double income=call.getDouble(1);
System.out.println(income);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.release(conn, call, null);
}
}

/*
在out中使用光标
2. 查询某个部门中的所有员工信息  ---> 返回集合
包头
CREATE OR REPLACE PACKAGE MYPACKAGE AS
  type empcursor is ref cursor;
  procedure queryEmpList(dno in number,empList out empcursor);
END MYPACKAGE;
包体
CREATE OR REPLACE PACKAGE BODY MYPACKAGE AS
  procedure queryEmpList(dno in number,empList out empcursor) AS
  BEGIN
    open empList for select * from emp where deptno=dno;
  END queryEmpList;
END MYPACKAGE;
*/

@Test
public void testCursor(){
String sql="{call MYPACKAGE.QUERYEMPLIST(?,?)}";

Connection conn=null;
CallableStatement call=null;
ResultSet rs=null;
try {
conn=JDBCUtils.getConnection();
call=conn.prepareCall(sql);

call.setInt(1, 10);

call.registerOutParameter(2,OracleTypes.CURSOR);

call.execute();

rs=((OracleCallableStatement)call).getCursor(2);

while(rs.next()){
String name=rs.getString("ename");
double sal=rs.getDouble("sal");
String job=rs.getString("job");
System.out.println(name+"\t"+sal+"\t"+job);;
}


} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.release(conn, call, null);
}
}
 
}

-五、触发器

触发器是一个与表相关联的,有存储的PL/SQL程序。每当特定的数据操作(insert,uodate,delete)在指定的表发出时,Oracle就会自动的执行触发器中定义的语句序列。

作用:数据确认

实施复杂的安全性检查;

做审计,跟踪表上所作的数据操作等;

数据的备份和同步。

 

01.语句级触发器

在指定的操作语句操作之前或者之后执行一次,不关这条语句影响了多少行。

/*
触发器应用一: 禁止在非工作时间插入新员工
 
1. 周末: to_char(sysdate,‘day‘) in (‘星期六‘,‘星期日‘)
2. 上班前 下班后:to_number(to_char(sysdate,‘hh24‘)) not between 9 and 17
*/
create or replace trigger securityemp
before insert
on emp
begin
  if to_char(sysdate,‘day‘) in (‘星期六‘,‘星期日‘) or
     to_number(to_char(sysdate,‘hh24‘)) not between 9 and 17  then
     --禁止insert操作
     raise_application_error(-20001,‘禁止在非工作时间插入新员工‘);
  end if;
 
end;
/

验证:SQL>insert into emp(empno,ename,sal,deptno) values(1001,Tom,1000,10);

02.行级触发器

触发语句作用的每条记录都被触发,在行级触发器中使用:old:new伪记录变量,识别值的状态。

/*
触发器应用二:数据的确认
涨后的薪水不能少于涨前的薪水
*/
create or replace trigger checksalary
before update
on emp
for each row
begin
  --if 涨后的薪水 < 涨前的薪水 then
  if :new.sal < :old.sal then
    raise_application_error(-20002,‘涨后的薪水不能少于涨前的薪水.涨前:‘||:old.sal||‘   涨后:‘||:new.sal);
  end if;
end;
/

验证:

SQL>update emp set sal=sal+1 where empno=7839;

SQL>update emp set sal=sal-1 where empno=7839;



本文出自 “qb的博客” 博客,谢绝转载!

PL/SQL和Oracle对象