oracle.PL/SQL高级

 一、匿名块
 1、使用returning ... INTO 保存增删改表数据时的一些列的值
 (01)增加数据时保存数据
DECLARE
  v_ename emp.ename%TYPE;
  v_sal emp.sal%TYPE;
BEGIN
  INSERT INTO emp(empno,ename,JOB,sal,comm,deptno) VALUES(&no,'zhaoliu','deve',3000,500,10) RETURNING ename,sal INTO v_ename,v_sal;
  dbms_output.put_line(v_ename || ','||v_sal);
end;

---打开控制台输出开关
SET serveroutput ON;

(02)修改数据时保存数据
DECLARE
  v_ename emp.ename%TYPE;
  v_sal emp.sal%type;
BEGIN
  UPDATE emp SET sal=sal+100 WHERE empno=&no RETURNING ename,sal INTO v_ename,v_sal;
  dbms_output.put_line(v_ename || ','||v_sal);
END;

(03)删除数据时保存数据
DECLARE
  v_ename emp.ename%TYPE;
  v_sal emp.sal%TYPE;
BEGIN
  DELETE FROM emp WHERE empno=&no RETURNING ename,sal INTO v_ename,v_sal;
  dbms_output.put_line(v_ename || ','||v_sal);
END;

二、分支结构
1、if
IF 条件 THEN 结果;
END IF;

IF 条件 THEN 结果;
ELSE 结果;
END IF;

IF 条件 THEN 结果;
elsif 条件 THEN 结果;
....
ELSE 结果;
END IF;

---查询一个员工薪水,判断员工薪水如果大于3000,输出他是一个白领;如果工资小于等于3000大于1500,输出他是一个蓝领;其它情况输出是一个灰领;
DECLARE
  v_sal emp.sal%TYPE;
BEGIN
  SELECT sal INTO v_sal FROM emp WHERE empno=&no;
  IF v_sal>3000 THEN
    dbms_output.put_line('白领');
  elsif v_sal>1500 THEN
    dbms_output.put_line('蓝领');
  ELSE dbms_output.put_line('灰领');
  END IF;
END;

2、case:
格式一:
  CASE WHEN 条件 THEN 结果;
       WHEN 条件 THEN 结果;
       ...
       ELSE 结果;
  END CASE;

格式二:
  CASE 条件表达式 WHEN 值1 THEN 结果1;
                WHEN 值2 THEN 结果2;
                ....
                ELSE 结果m;
  END CASE;
 
---输入一个成绩,判断该成绩等级,如果在99~90之间,返回为A,如果在89~80之间,返回为B,如果在79~70之间,返回C,其它返回为D
DECLARE
  v_score NUMBER(3);
  v_flag VARCHAR2(1);
BEGIN
  v_score:=&score;
  CASE trunc(v_score/10) WHEN 9 THEN v_flag:='A';
                         WHEN 8 THEN v_flag:='B';
                         WHEN 7 THEN v_flag:='C';
                         ELSE v_flag:='D';
  END CASE;
  dbms_output.put_line(v_score || ',该 成绩的等级为:' || v_flag);
END;

三、循环结构
1、直到型循环
loop
  执行循环体;
  exit WHEN 条件;
END loop;

---在控制台输出1~5
DECLARE
  v_i NUMBER(1):=1;
BEGIN
  loop
    dbms_output.put_line(v_i);
    v_i:=v_i+1;
    exit WHEN v_i>5;
  END loop;
end;

2、while:
while 条件
  loop
    执行循环体;
  END loop;
 
---在控制台输出1~5
DECLARE
  v_i NUMBER(1):=1;
BEGIN
  while v_i<=5
    loop
      dbms_output.put_line(v_i);
      v_i:=v_i+1;
    END loop;
end;

3、fOR:
FOR 循环变量 IN 最小值..最大值
  loop
    执行循环体;
  END loop;

---在控制台输出1~5
BEGIN
  for v_i in 1..5
    loop
      dbms_output.put_line(v_i);
    END loop;
end;

练习:
创建一张表,只包含一个字段id
先在表中插入一条记录5
然后将10—20之间的数插入两遍

四、存储过程
语法:
  CREATE [OR REPLACE] PROCEDURE 存储过程名(形参1 IN|out 数据类型,形参2 IN|out 数据类型...)
  IS | AS
    定义部分;
  begin
    执行部分;
  exception
    异常部分;
  END;
 
调用过程:
  exec 存储过程名(实参...);
  call 存储过程名(实参...);   ---如果调用的是无参存储过程,过程名后面必须()

1、创建一个无参存储过程
CREATE PROCEDURE pro_demo1
IS
BEGIN
  INSERT INTO dept VALUES(50,'deve','nanjing');
end;
 
exec pro_demo1;
call pro_demo1();

2、创建一个输入参数存储过程
---根据输入的员工工号和新的月薪修改数据库中该 员工的薪水
CREATE OR REPLACE PROCEDURE pro_demo1(NO emp.empno%TYPE,newsal emp.sal%TYPE)
is
BEGIN
  update emp set sal=newsal where empno=no;
end;

一个为雇员加薪的过程
CREATE OR REPLACE PROCEDURE pro_demo1(NO emp.empno%TYPE,addsal emp.sal%TYPE)
is
BEGIN
  UPDATE emp SET sal=sal+addsal WHERE empno=NO;
end;


CALL pro_demo1(7788,10000);

SELECT * FROM emp;

3、创建一个即有输入参数也有输出参数存储过程
---输出指定员工的姓名和工资
CREATE OR REPLACE PROCEDURE pro_demo2(NO emp.empno%TYPE,NAME out emp.ename%TYPE,salary out emp.sal%TYPE)
IS
BEGIN
  SELECT ename,sal INTO NAME,salary FROM emp WHERE empno=NO;
END;


--使用匿名块调用存储过程
DECLARE
  v_name VARCHAR2(30);
  v_sal NUMBER(7);
BEGIN
  pro_demo2(&no,v_name,v_sal);
  dbms_output.put_line(v_name || ',' || v_sal);
end;
 
五、函数
CREATE OR REPLACE FUNCTION 函数名(形参 数据类型,...) RETURN 数据类型
IS
  定义部分;
BEGIN
  执行部分;
exception
  异常部分;
END;

---返回指定员工的年薪
CREATE OR REPLACE FUNCTION fun_demo1(NO emp.empno%TYPE) RETURN NUMBER
IS
  v_salary NUMBER(10,2);
BEGIN
  SELECT sal*12 INTO v_salary FROM emp WHERE empno=NO;
  RETURN v_salary;
END;

SELECT fun_demo1(7788) FROM dual;

---写一个函数,返回用户的姓名,入职日期,工资
CREATE OR REPLACE FUNCTION fun_demo2(NO emp.empno%TYPE) RETURN VARCHAR2
IS
  v_ename emp.ename%TYPE;
  v_hiredate varchar2(50);
  v_sal emp.sal%TYPE;
  v_info varchar2(200);
BEGIN
  SELECT ename,to_char(hiredate,'yyyy-mm-dd'),sal INTO v_ename,v_hiredate,v_sal FROM emp WHERE empno=NO;
  v_info:=v_ename ||','|| v_hiredate ||','|| v_sal;
  RETURN v_info;
end;

SELECT fun_demo2(7788) FROM dual;

存储过程和函数的区别:
1.返回值的区别,函数返回值只有一个,而存储过程是通过输出参数来返回,可以有多个
2.调用的区别,函数可以在查询语句中直接调用,而存储过程必须单独调用
3.应用场合的区别,函数一般用来计算并返回计算的结果,而存储过程一般用来执行特定的操作,如创建表、创建用户等DDL语句

五、包,里面包含的内容:存储过程,函数
语法:包括两个部分
声明部分:
  CREATE OR REPLACE PACKAGE 包名
  IS
    声明内容;
  END;

包体的实体:
  CREATE OR REPLACE PACKAGE BODY 包名
  is
    实现部分;
  end;

CREATE OR REPLACE PACKAGE pac_demo1
IS
  PROCEDURE pro_demo1(NO emp.empno%TYPE,salary emp.sal%TYPE);
  PROCEDURE pro_demo2(NO emp.empno%TYPE);
  FUNCTION fun_demo1(NO emp.empno%TYPE) RETURN NUMBER;
end;

create or replace package body pac_demo1
IS
  PROCEDURE pro_demo1(NO emp.empno%TYPE,salary emp.sal%TYPE)
  is
  BEGIN
    UPDATE emp SET sal=salary WHERE empno=NO;
  END;
  PROCEDURE pro_demo2(NO emp.empno%TYPE)
  IS
  BEGIN
    DELETE FROM emp WHERE empno=NO;
  END;
  FUNCTION fun_demo1(NO emp.empno%TYPE) RETURN NUMBER
  IS
    v_salary number(10,2);
  BEGIN
    SELECT sal*12 INTO v_salary FROM emp WHERE empno=NO;
    RETURN v_salary;
  END;
end;

CALL pac_demo1.pro_demo1(7788,20000);
SELECT * FROM emp;
CALL pac_demo1.pro_demo2(7788);
select pac_demo1.fun_demo1(7369) from dual;

--1.创建一个包,包含一个为雇员加薪的过程,一个为雇员减薪的过程
CREATE OR REPLACE PACKAGE pac_test1
IS
  PROCEDURE addsal(NO emp.empno%TYPE,addsal emp.sal%TYPE);
  procedure minussal(no emp.empno%type,minussal emp.sal%type);
END;

CREATE OR REPLACE PACKAGE body pac_test1
IS
  PROCEDURE addsal(NO emp.empno%TYPE,addsal emp.sal%TYPE)
  is
  BEGIN
    UPDATE emp SET sal=sal+addsal WHERE empno=NO;
  end;
  PROCEDURE minussal(NO emp.empno%TYPE,minussal emp.sal%TYPE)
  IS
  BEGIN
    UPDATE emp SET sal=sal-minussal WHERE empno=NO;
  end;
END;

--2.编写一个过程,验证用户登陆。如果用户名、密码匹配,输出Y,否则输出N    emp表中的ename为用户名 empno为密码
CREATE OR REPLACE PROCEDURE pro_test2(username VARCHAR2,PASSWORD NUMBER,flag out VARCHAR2)
IS
  v_count number(2);
BEGIN
  SELECT count(empno) INTO v_count FROM emp WHERE ename=username AND empno=PASSWORD;
  IF v_count>0 THEN flag:='Y';
  ELSE flag:='N';
  END IF;
END;

--3.编写一个函数,根据雇员编号,计算调整后的工资
如果该雇员从事经理工作,且工资大于3000,则工资上涨20%,否则上涨35%
其他情况均上涨10%,输出雇员编号、姓名、上涨后的工资

CREATE OR REPLACE FUNCTION fun_test3(NO emp.empno%TYPE) RETURN VARCHAR2
IS
  v_ename emp.ename%TYPE;
  v_sal emp.sal%TYPE;
  v_newsal emp.sal%TYPE;
  v_job emp.job%type;
  v_info varchar2(100);
BEGIN
  SELECT ename,sal,JOB INTO v_ename,v_sal,v_job FROM emp WHERE empno=NO;
  IF upper(v_job)='MANAGER' THEN
    IF v_sal>3000 THEN
      v_newsal:=v_sal*1.2;
    ELSE v_newsal:=v_sal*1.35;
    END IF;
  ELSE v_newsal:=v_sal*1.1;
  END IF;
   v_info:= NO || ',' || v_ename || ',' || v_newsal;
  RETURN v_info;
end;

--4.创建一个包
包中含有一个存储过程,实现涨工资的功能,输入雇员姓名,如果其工作年限超过20年,工资增加2000
工作年限在10-20年,工资增加1000,工作年限在5-10年,工资增加500,输出雇员姓名和增涨后的工资
包中还含有一个函数,实现输入工资,判断工资如果大于4500,返回1,否则返回0

CREATE OR REPLACE PACKAGE pac_test4
IS
  PROCEDURE pro_addsal(NAME emp.ename%TYPE,info out VARCHAR2);
  FUNCTION fun_sal(salary emp.sal%TYPE) RETURN NUMBER;
end;

CREATE OR REPLACE PACKAGE body pac_test4
IS
  PROCEDURE pro_addsal(NAME emp.ename%TYPE,info out VARCHAR2)
  IS
    v_year NUMBER(5);
    v_sal emp.sal%TYPE;
  BEGIN
    SELECT trunc(months_between(SYSDATE,hiredate)/12),sal INTO v_year,v_sal FROM emp WHERE upper(ename)=upper(NAME);
    IF v_year>20 THEN v_sal:=v_sal+2000;
    elsif v_year>=10 THEN v_sal:=v_sal+1000;
    elsif v_year>=5 THEN v_sal:=v_sal+500;
    END IF;
    info:=NAME || ','||v_sal;
  end;
  FUNCTION fun_sal(salary emp.sal%TYPE) RETURN NUMBER
  IS
    v_flag NUMBER(1);
  BEGIN
    IF salary>4500 THEN
      v_flag:=1;
    ELSE v_flag:=0;
    END IF;
    RETURN v_flag;
  end;
END;

六、游标:
1、显示游标(查)、隐式游标(增删改)
使用显示游标有四个步骤:
  (01):声明游标:cursor 游标名 IS 查询语句;
  (02):打开游标:open 游标名;
  (03):获取数据:fetch 游标名 INTO 变量名;
  (04):关闭游标:close 游标名;
 
游标属性:
  %found:是否发现数据,如果发现,返回true,否则返回false
  %notfound:是否没有数据,如果没有,返回true,否则返回false
  %isopen:是否打开,如果打开,,返回true,否则返回false
  %rowcount:返回涉及的行数
 
 
DECLARE
  CURSOR cur IS SELECT * FROM dept;
  v_dept dept%rowtype;
BEGIN
  OPEN cur;
  fetch cur INTO v_dept;
  loop
    dbms_output.put_line(v_dept.deptno || ','|| v_dept.dname || ','||v_dept.loc);
    fetch cur INTO v_dept;
  exit WHEN cur%notfound;
  END loop;
  CLOSE cur;
end;

---使用for循环读取游标指向的数据,打开游标、获取游标数据、关闭游标都自动
DECLARE
  CURSOR cur IS SELECT * FROM dept;
BEGIN
  FOR v_dept IN cur
    loop
      dbms_output.put_line(v_dept.deptno || ','|| v_dept.dname || ','||v_dept.loc);
    END loop;
end;

2、隐式游标:游标名为SQL,增删改

---修改指定员工的工资,如果员工不存在,提示员工不存在,如果存在,输出更新数据的行数
BEGIN
  UPDATE emp SET sal=sal*1.2 WHERE empno=&no;
  IF SQL%found THEN
    dbms_output.put_line('更新数据的行数为:' || SQL%rowcount);
  ELSE
    dbms_output.put_line('对不起,该用户不存在');
  END IF;
end;

六、异常:
1、预定义异常:有异常号也有异常名
DECLARE
  v_emp emp%rowtype;
BEGIN
  SELECT * INTO v_emp FROM emp WHERE empno=&no;
exception
  WHEN no_data_found THEN
    dbms_output.put_line('用户不存在');
  WHEN too_many_rows THEN
    dbms_output.put_line('返回了多行数据');
  WHEN others THEN
    dbms_output.put_line('程序出现了错误');
END;

2、非预定义异常:有异常号没有异常名
3、自定义异常:没有异常号也没有异常名

七、触发器
1、DML触发器:

CREATE OR REPLACE TRIGGER 触发器名
  AFTER|BEFORE INSERT|UPDATE|DELETE ON 表名
  [FOR EACH ROW]  ---行触 发器
BEGIN
  执行体;
end;

---如果有人删除emp表中的数据,给出警告信息
CREATE OR REPLACE trigger tri_demo1
  AFTER DELETE ON emp
BEGIN
  dbms_output.put_line('有人删除了emp表中的数据,请注意');
end;

DELETE FROM emp WHERE empno=7566;

CREATE OR REPLACE TRIGGER tri_demo1
  AFTER DELETE or update or insert ON emp
BEGIN
  IF deleting THEN
    dbms_output.put_line('有人在删除emp表的数据');
  elsif inserting THEN
    dbms_output.put_line('有人新增了数据');
  elsif updating THEN
    dbms_output.put_line('有人修改了数据');
  end if;
END;

        DELETE    INSERT    UPDATE
:NEW    无          有效      有效
:old    有效        无        有效

CREATE OR REPLACE TRIGGER tri_demo3
  before DELETE OR UPDATE OR INSERT ON emp
  for each row
BEGIN
  IF deleting THEN
    dbms_output.put_line('有人在删除emp表的数据');
    dbms_output.put_line(:old.ename ||','||:old.sal);
  elsif inserting THEN
    dbms_output.put_line('有人新增了数据');
    dbms_output.put_line(:new.ename ||','||:new.sal);
  elsif updating THEN
    dbms_output.put_line('有人修改了数据');
    dbms_output.put_line('原数据为:'||:OLD.ename ||','||:OLD.sal);
    dbms_output.put_line('修改后的数据为:'||:new.ename ||','||:new.sal);
  END IF;
END;


语句触发器:无论增加、删除、修改多少行数据,触发器只触发一次
行触发器:增加、删除、修改对于每一行涉及的数据,触发器都会执行

INSERT INTO emp(empno,ename,sal) VALUES(1000,'bbb',6000);
commit;

DELETE FROM emp WHERE deptno=10;
commit;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值