Oracle-PL/SQL语句

 

1 PL/SQL

1.1什么是PL/SQL?

n  PL:Process Language

n  PL/SQL是oracle对sql语言的过程化扩展

n  在SQL语言中添加了过程处理语句(如分支,循环),使SQL语言具有过程处理能力

1.2 PL/SQL程序结构

Declare

  说明部分 (变量、光标、例外声明)

Begin

  语句序列(DML)...

Exception

  例外处理语句

End;

1.3变量声明

n  可以声明的变量类型 char,varchar2,boolean,date,number,long

--查找7839的员工信息

--打开输出

set serveroutput on

declare

  pename emp.ename%type;--名字变量

  psal emp.sal%type;--薪水变量

begin

  select ename,sal into pename,psal from emp where empno=7839;

  --输出

  DBMS_OUTPUT.PUT_LINE(pename||'的薪水是'||psal);

end;

1.4 if语句

--判断用户从键盘输入的数字

--打开输出

set serveroutput on

--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;

1.5循环

--打开输出

set serveroutput on

declare

  pnum number :=1;

begin

  loop

  exit when pnum > 10;

  dbms_output.put_line(pnum);

  pnum :=pnum + 1;

  end loop;

end;

1.6光标

n  光标(cursor)也被称为游标。Oracle 使用两种光标:显示光标和隐式光标。

n  不管语句返回多少条纪录, PL/SQL 为使用的每一条 UPDATE 、 DELETE 和 INSERT 等 SQL 命令隐式的声明一个光标。

光标的作用

n  当 PL/SQL 光标查询返回多行数据时,这些记录组被称为活动集。 Oracle 将这种活动集存储在您创建的显示定义的已命名的光标中。Oracle 光标是一种用于轻松的处理多行数据的机制,没有光标, Oracle 开发人员必须单独地、显示地取回并管理光标查询选择的每一条记录。

n  光标的另一项功能事,它包含一个跟踪当前访问的记录的指针,这使您的程序能够一次处理多条记录。

练习

遍历员工薪水

set serveroutput on

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 cemp%notfound;

    dbms_output.put_line(pename||'的薪水是'||psal);

  end loop;

  --关闭光标

  close cemp;

end; 

涨工资,总裁1000 经理800 其他400

set serveroutput on

declare

  --job在plsql中是一个关键字,重命名一下

  --alter table "SCOTT"."EMP" rename column "JOB" to empjob

  cursor cemp is select empno,empjob from emp;

  pempno emp.empno%type;

  pjob emp.empjob%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;

  --需要提交

  commit;

end;  

带参数的光标

set serveroutput on

declare

  cursor cemp(dNo number) is select ename,sal from emp where deptno = dNo;

  pename emp.ename%type;

  psal emp.sal%type;

begin

  --打开光标

  open cemp(20);

  loop

    fetch cemp into pename,psal;

    exit when cemp%notfound;

    dbms_output.put_line(pename||'的薪水是'||psal);

  end loop;

  --关闭光标

  close cemp;

end; 

n  默认,一个会话中只能打开300个光标

SQL> show parameter cursor【只能系统管理员可查看】

SQL>alter system set open_cursors=400; 【修改】

1.7例外(异常)

n  例外是程序设计语言提供的一种功能,用来增强程序的键壮性和容错性

n  oracle的异常处理

n  系统定义的例外【异常】

n  No_data_found【没有找到数据】

n  Too_many_row【select ...into语句匹配多个行】

n  Zero_Divide【被零除】

n  Value_error 【算术或转换错误】

n  Timeout_on_resource【在等待资源时发生超时】

系统异常

set serveroutput on

declare

  pnum number;

begin

  pnum:=1/0;

  --pnum:='b';

exception

  when zero_divide then DBMS_OUTPUT.PUT_LINE('0不能做分母');

                        DBMS_OUTPUT.PUT_LINE('0不能做分母');--可写多行

  when value_error then  DBMS_OUTPUT.PUT_LINE('算术转换异常');

  when others then  DBMS_OUTPUT.PUT_LINE('其它导常');

end; 

自定义异常

--查询50号部门的员工姓名

set serveroutput on

declare

  cursor cemp is select ename from emp where deptno=50;

  pename emp.ename%type;

  --自定义异常

  no_emp_found exception;

begin

  open cemp;

  --取第一条记录

  fetch cemp into pename;

  if cemp%notfound then

    --抛出异常

    raise no_emp_found;

  end if;

  close cemp;

exception

  when no_emp_found then dbms_output.put_line('没有找到员工');

  when others then dbms_output.put_line('其他例外');

end;

1.8案例

n  统计每年入职的员工数

set serveroutput on

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;

n   

1.9案例

n  为员工涨工资。从最低工资起,每人涨10%,但总额不能超过5W,请计算涨工资的人数和长工资后的工资总额

/**

1.为员工涨工资。从最低工资起,每人涨10%,

2.但总额不能超过5W,请计算涨工资的人数和长工资后的工资总额

1.声明变量

涨工资的人数 countEmp number:0;

涨工资的总额 salTotal number;

[select sum(sal) into salTotal from emp];

2.得到初始的工资总和

3.光标遍历

4.如果总额大于5000就不加工资

*/

set serveroutput on

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

    --大于5W不在加工资

    exit when salTotal > 50000;

    fetch cemp into pempno,psal;

    --没有光标,无下条记录

    exit when cemp%notfound;

    --涨工资

    update emp set sal = sal*1.1 where empno=pempno;

   

    --涨工资人数

    countEmp :=countEmp+1;

    --涨后金额

    salTotal :=salTotal + psal*0.1;

  end loop;

  close cemp;

  commit;--提交事务

  DBMS_OUTPUT.PUT_LINE('涨工资人数'||countEmp);

  DBMS_OUTPUT.PUT_LINE('涨后工资总额'||salTotal);

end;

2.0案例

n  实现按部门分段(6000以上),(6000,3000),3000元以下统计各工资段的职工人数,以及各部门的工资总和

 

/*

1.部门 遍历打印部门编号

2.部门员工遍历,薪水

3.声明三个区间的变量

4.声明部门总工资变量

*/

set serveroutput on

declare

  --部门光标

  cursor cdept is select deptno from dept;

  --部门ID变量

  pdeptno dept.deptno%type;

  --员工光标

  cursor cemp(dNo number) is select sal from emp where deptno=dNo;

  psal emp.sal%type;

  --每个区间的员工个数

  count1 number;-->6000

  count2 number;--<3000

  count3 number;--3000~600

  totalSal number;--部门上总工资

begin

  --打开光标

  open cdept;

  --1.遍历部门

  loop

    count1:=0;count2:=0;count3:=0;

   

    fetch cdept into pdeptno;

    exit when cdept%notfound;

   

    select sum(sal) into totalSal from emp where deptno = pdeptno;

    --打印部门编号

    DBMS_OUTPUT.PUT_LINE ('部门编号'||pdeptno ||'总工资'||nvl(totalSal,0));

   

    --2.根据部门编号遍历员工

    open cemp(pdeptno);

    loop

      fetch cemp into psal;

      exit when cemp%notfound;

      --打印工资

      --DBMS_OUTPUT.PUT_LINE ('--'||psal);

      --3.遍历员工工资区间

      if psal>6000 then count1 :=count1+1;

      elsif psal<3000 then count2 :=count2+1;

      else count3 :=count3+1;

      end if;

    end loop;

    close cemp;

  

    --打印工资区间人数

    DBMS_OUTPUT.PUT_LINE ('工资>6000:'||count1||' 工资<3000:'||count2||' 工资3000~6000:'||count3);

    DBMS_OUTPUT.PUT_LINE ('=========================================');

  --结束循环 

  end loop;

  --关闭光标

  close cdept;

end;

2 存储过程,函数

2.1存储过程和存储函数

n  指存储在数据库中供所有用户程序调用的子程序叫存储过程和存储函数

2.2创建存储过程语法

Create [or replace] procedure 过程名(参数)

as

    PLSQL子程序

无参数的存储过程

--创建一个存储过程

create or replace procedure sayHelloWord

as

  --说明部份

begin

  DBMS_OUTPUT.PUT_LINE('Hello Word');

end;

--调用存储过程 第一方式

EXECUTE sayHelloWord();

--调用存储过程 第二方式

begin

  sayHelloWord();

  sayHelloWord();

end;

带参数的存储过程

--给指定的员工涨工资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;

  --打印

  DBMS_OUTPUT.put_line('涨前:'||psal||' 涨后:'||(psal + 100));

end;

--调用存储过程

set serveroutput on

begin

 raisesalary(7369);

 raisesalary(7369);

 --需要在这里提交事务,不要在内部提交

 commit;

end;

2.3存储过程的调试

n  如图,存储过程可调试,但在调试前需要给于权限

n  然后加断点调试,先选择调用,用法跟java的调试差不多

 

授权

 

2.4存储函数

n  函数(Function)为一命名的存储函数,可以带参数,并返回一计算值。函数和过程的结构相似,但必需要有一个return语句,用于返回函数值。函数说明要指定函数名、结果值的类型,以及参数类型。

2.5创建存储函数语法

Create [or replace] function 函数名(参数)return 返回值类型

as

    PLSQL子程序

函数声明 【计算年薪】

create or replace function queryincome(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;

函数调用

set serveroutput on

--函数的调用

declare

  y_sal number;

begin

  y_sal:=queryincome(7369);

  DBMS_OUTPUT.put_line('年薪:'||y_sal);

end;

2.6 什么时候用存储函数和存储过程

n  如果有一个返回值就用存储函数,否则就用存储过程

3 java中调用存储过程和函数

创建一个存储过程【根据员工编号返回员工名字和工资】

create or replace procedure queryempinfo(eno in number,empname out varchar,empsal out number)

as

  psal emp.sal%type;

begin

  select ename,sal into empname,empsal from emp where empno = eno;

end;

创建一个java项目,导入ojdbc和junit

写一个JDBC工具包

package demo;

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.56.102:1521:orcl";

         private static String user = "scott";

         private static String password = "scott";

        

         static{

                   try {

                            Class.forName(driver);

                   } catch (ClassNotFoundException e) {

                            // TODO: handle exception

                            e.printStackTrace();

                   }

         }

        

         /**

          * 返回一个数据连接

          * */

         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 rlease(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;//释放

                            }

                   }

         }

}

存储过程的调用

@Test

    public void testProcedure(){

       //存储过程的java写法{call <procedure-name>[(<arg1>,<arg2>, ...)]}

       String p = "{call queryempinfo(?,?,?)}";

      

       Connection con =null;

       CallableStatement call = null;

      

       try {

           con = JDBCUtils.getConnection();

           call = con.prepareCall(p);

           //对于in参数,赋值

           call.setInt(1, 7369);

          

           //设置out参数类型

           call.registerOutParameter(2, OracleTypes.VARCHAR);

           call.registerOutParameter(3, OracleTypes.NUMBER);

          

           //执行

           call.execute();

           //取结果

           String ename = call.getString(2);

           double sal = call.getDouble(3);

           System.out.println(ename);

           System.out.println(sal);

          

       } catch (SQLException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }finally {

         JDBCUtils.rlease(con, call, null);

      }

    }

存储函数的调用{?=call <funtion-name>[(<arg1>,<arg2>, ...)]}

@Test

    public void testFunction(){

       //存储方法的java写法{?=call <funtion-name>[(<arg1>,<arg2>, ...)]}

       String p = "{?=call queryincome(?)}";

      

       Connection con =null;

       CallableStatement call = null;

      

       try {

           con = JDBCUtils.getConnection();

           call = con.prepareCall(p);

           //对于in参数,赋值

           call.setInt(2, 7369);

          

           //设置out参数类型

           call.registerOutParameter(1, OracleTypes.NUMBER);

          

           //执行

           call.execute();

           //取结果

           double y_sal = call.getDouble(1);

           System.out.println(y_sal);

          

       } catch (SQLException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }finally {

           JDBCUtils.rlease(con, call, null);

       }

    }

4 触发器

4.1触发器定义

n  数据库触发器是一个与表相关联、存储的PL/SQL程序。每当一个特定的数据操作语句(Insert,Update,delete)在指定的表上发出时,Oracle自动地执行触发器中定义的语句序列。

n  触发器的类型

n  语句级触发器

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

n  行级触发器(FOR EACH ROW)

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

4.2触发器语法格式

CREATE [or REPLACE] TRIGGER 触发器名

{BEFORE | ALTER}

{DELETE | INSERT | UPDATE [OF 列名]}

ON 表名

[FOR EACH ROW [WHEN(条件)]]

PLSQL 块

4.3案例

第一个触发器,插入员工后打印一句输出

create trigger abcd after insert on emp

declare

begin

  DBMS_OUTPUT.PUT_LINE('成功插入新员工');

end;

set serveroutput on

insert into emp (empno,ename,sal,deptno) values(1122,'zhangsan',1920,10);

2.实施复杂的安全性检查 禁止在非工作时间插入新员工

create 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 RAISE_APPLICATION_ERROR(-20001, '禁止非工作时间插入新员工');

  end if;

end;

set serveroutput on

insert into emp (empno,ename,sal,deptno) values(1432,'zhangsan3',1920,10);

commit;

3涨后的工资不能少于涨前的工资

create or replace trigger checksalary

before update

on emp

for each row

begin

  if :new.sal < :old.sal  then

  raise_application_error(-20002,'涨后的工资不能少于涨前的工资new:'||:new.sal||' old:'||:old.sal);

  end if;

end;

set serveroutput on;

update emp set sal = sal -10 where empno=1122;

猜你喜欢

转载自www.cnblogs.com/royal6/p/12111575.html