一.pl/sql(Procedure Language/SQL)编程语言
1.概念
PL/SQL是Oracle数据库对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点,所以PL/SQL把数据操作和查询语句组织在PL/SQL代码的过程性单元中,通过逻辑判断、循环等操作实现复杂的功能或者计算。PL/SQL 只有 Oracle 数据库有。 MySQL 目前不支持 PL/SQL 的。

2.变量和常量
声明普通变量:
     v_name varchar2(30) := 'tom';  (:=为赋值符号;=为比较符号,相当于java中的==);
声明引用型变量:
     v_sal emp.sal%type := 100;    声明的v_sal变量与emp表中sal字段的类型一致;
声明记录型变量:
     v_row emp%rowtype; 记录型变量相当于java中的resultset,用来存储整张表中的数据;
声明常量:
     v_gender constant number(1) number(1) := 1;   

3.分支语句
语法一:if ---then---end if;
语法二:if ---then---else---end if;
语法三:if ---then---elsif---then----else----end if;
举例:
--年龄小于18,显示未成年人,18-60,显示成年人,60以上显示老年人

declare
     v_age number(8) :=#

begin 
     if v_age < 18 then 
         dbms_output.put_line('未成年人');
     elsif v_age >= 18 and v_age <= 60 then 
         dbms_output.put_line('成年人');
     else 
         dbms_output.put_line('老年人');
     end if;

end;

4.循环语句
语法一:loop---exit when----end loop;
举例:
--输出1--100的数

declare
  v_num number(8) := 1;

begin
  loop
   exit when v_num > 100;
   dbms_output.put_line(v_num);
   v_num := v_num + 1;
  end loop;

end;

语法二:while---loop----end loop;

declare
  v_num number(8) := 1;

begin
  while v_num <= 100 loop
   dbms_output.put_line(v_num);
   v_num := v_num + 1;
  end loop;

end;

语法三:for---in 起始值..终止值---loop---end loop;

declare
  v_num number(8) := 1;

begin
  for v_num in 1 .. 100 loop
   dbms_output.put_line(v_num);
  end loop;

end;

5.游标(cursor)
作用:用来接收多条数据结果,相当于java中的ResultSet
语法:cursor 游标名称 is sql 查询语句;
使用:
     open 游标名称
     loop
         fetch 游标名称 into 记录型变理
         exit when 游标名称%notfound;
             逻辑处理
     end loop;
     close 游标名称;
举例:
--打印emp表的所有信息

DECLARE
  CURSOR c_emp IS SELECT * FROM emp;
  v_row emp%ROWTYPE;

BEGIN
  OPEN c_emp;
  LOOP
    FETCH c_emp INTO v_row;
          EXIT WHEN c_emp%NOTFOUND;
          dbms_output.put_line(v_row.ename||'--'||v_row.job);
   END LOOP;
   CLOSE c_emp;

END;

--带参数的游标

DECLARE
   CURSOR c_emp(v_no1 NUMBER, v_no2 NUMBER) IS SELECT * FROM emp WHERE deptno = v_no1 OR deptno = v_no2;
   v_row emp%ROWTYPE;

BEGIN
  OPEN c_emp(10,20);--传入部门编号deptno
  LOOP 
   FETCH c_emp INTO v_row;
   EXIT WHEN c_emp%NOTFOUND;
   dbms_output.put_line(v_row.ename||'=='||v_row.job);
  END LOOP;
  CLOSE c_emp;

END;

6.异常
exception---when----then
--预定义异常

DECLARE
  v_num NUMBER(3);
  BEGIN
   v_num := 10000;
   EXCEPTION
    WHEN value_error THEN
     v_num := 999;
     dbms_output.put_line(v_num);
  END;

--自定义异常
 

DECLARE
   V_AGE NUMBER(8) := &NUM;
   EXC_AGE EXCEPTION; --声明异常
  BEGIN
   IF V_AGE > 150 THEN
    RAISE EXC_AGE;
   END IF;
  EXCEPTION
   WHEN EXC_AGE THEN
    RAISE_APPLICATION_ERROR(-20001, 'illegal age');
  END;

二.存储过程
概念:一段被命名的plsql,预编译到了数据库中
语法:
     create or replace procedure 存储过程名字(参数1 [in]/out 数据类型)
         as | is
         begin

        end;
例子1:

--存储过程,打印指定员工的年薪
  create or replace procedure pro_emp_sal(v_no number) is
  v_sal number(8, 2);

begin
  select sal * 12 + nvl(comm, 0) into v_sal from emp where empno = v_no;
  dbms_output.put_line(v_sal);

end;

--方法一调用存储过程
  call pro_emp_sal(7788);

--方法二调用存储过程

begin
  pro_emp_sal(7788);

end;

例子2:带out参数的存储过程

CREATE OR REPLACE PROCEDURE pro_emp_sal2(v_no NUMBER, v_yearsal OUT NUMBER) IS

BEGIN
  SELECT sal*12 + NVL(comm,0) INTO v_yearsal FROM emp WHERE empno = v_no;

END;

--只能使用方式二调用

DECLARE
  v_sal NUMBER(8,2);
  BEGIN
   pro_emp_sal2(7788,v_sal);
   dbms_output.put_line(v_sal);
  END;

三.存储函数
--存储函数

CREATE OR REPLACE FUNCTION fun_emp_sal(v_no NUMBER)

RETURN NUMBER

IS

v_sal NUMBER(8,2);

BEGIN
  SELECT sal*12+NVL(comm,0) INTO v_sal FROM emp WHERE empno = v_no;
  RETURN v_sal;
  END;
  --使用存储函数
  BEGIN
   dbms_output.put_line(fun_emp_sal(7788));
   END;

注:存储过程和存储函数的区别
1、语法不同
2、使用场景:一般存储函数多被存储过程使用,存储过程一般使用在项目和项目之间的数据交互
3、存储函数可以直接在sql中使用,而存储过程不能
select ename,sal,func_emp_sal(empno)  from emp;

四.使用jdbc调用存储过程和存储函数
1.BaseDao用于加载驱动和获取连接
2.ProcedureDao用于调用存储过程
3.TestDao用于测试

举例:
1.BaseDao用于加载驱动和获取连接

public class BaseDao {
      //加载驱动
     static{
         try {
             Class.forName("oracle.jdbc.OracleDriver");
         } catch (ClassNotFoundException e) {
             e.printStackTrace();
         }
     }
     
     //获取连接
     public static Connection getConn() throws SQLException{
         String url="jdbc:oracle:thin:@192.168.92.8:1521:orcl";
         String user="qin";
         String password="qin";
         return DriverManager.getConnection(url, user, password);
     }
     
     public static void closeAll(ResultSet rs,Statement stmt,Connection conn){
         if(rs!=null){
             try {
                 rs.close();
             } catch (SQLException e) {
                 e.printStackTrace();
             }
         }
         if(stmt!=null){
             try {
                 stmt.close();
             } catch (SQLException e) {
                 e.printStackTrace();
             }
         }
         if(conn!=null){
             try {
                 conn.close();
             } catch (SQLException e) {
                 e.printStackTrace();
             }
         }
     }

}

2.ProcedureDao用于调用存储过程

public class ProcedureDao {
     public static Long  getSal(Long v_no){
         Connection conn = null;
         CallableStatement stmt=null;
         Long yearsal=0l;        try {
             conn=BaseDao.getConn();
             stmt = conn.prepareCall("call pro_emp_sal2(?,?)");//调用存储过程
             stmt.setLong(1, v_no);
             stmt.registerOutParameter(2, OracleTypes.NUMBER);    //指定参数的数据类型
             stmt.execute();
             yearsal = stmt.getLong(2);
         } catch (SQLException e) {
             e.printStackTrace();
         }
         return yearsal;
     }

}

3.TestDao用于测试

public class TestDao {
     public static void main(String[] args) {
         CursorDao.getEmp(10l);
     }

}

五.触发器
1.--创建添加数据引发操作的触发器

CREATE OR REPLACE TRIGGER tri_add_emp

AFTER

INSERT ON empBEGIN
  dbms_output.put_line('增加了一条数据');

END;

--增加一条数据,看是否会触发
INSERT INTO emp(empno,ename,deptno) VALUES(1,'tom',10);

2.--系统时间引发的触发器

CREATE OR REPLACE TRIGGER tri_emp

BEFORE 

DELETE OR UPDATE OR INSERT

ON emp

FOR EACH ROWDECLARE
  v_dateStr VARCHAR2(20);

BEGIN
  SELECT to_char(SYSDATE,'yyyy-mm-dd') INTO v_dateStr FROM dual;
  IF v_dateStr = '2017-09-20' THEN
   raise_application_error(-20002,'今天系统维护');
   END IF;
  END;


  --测试是否能引发触发器
  INSERT INTO emp(empno,ename,deptno) VALUES (3,'jerry',10);

六.误删除数据恢复语句

create table tableName_bak

as

select * from tableName as of TIMESTAMP to_timestamp('20081126 103435','yyyymmdd hh24miss');