语法
--plsql语句块:
set serveroutput on; --打开控制台输出的命令
语法:
declare
--声明部分
begin
--执行语句
--异常处理
exception
when others then
--执行语句
end;
自定义变量类型
-
定义和列的类型保持一致
- vsal emp%type; —>表示和emp表的sal列标尺一致
-
定义和表的类型保持一致
- vsal emp%rowtype; —>表示和emp表的结构一致
-
定义自己的封装类
声明的是类型
type type_emp_name_sal is record(v_empname emp.ename%type,v_empsal emp.sal%type);
变量名是v_name_sal 类型是type_emp_name_sal
v_name_sal type_emp_name_sal;
-
数组
- 声明数组类型
- type int_array is table of integer index by BINARY_integer;
- int类型数组的变量
- v_numbers int_array;
流程控制语句
- if语句
if v_id = 1 then
dbms_output.put_line(v_id);
elsif v_id = 2 then
dbms_output.put_line('elsif');
else
dbms_output.put_line(v_id);
end if;
- switch语句 case语句
case
when v_id = 1 then
dbms_output.put_line(v_id);
when v_id = 2 then
dbms_output.put_line('elsif');
else
dbms_output.put_line(v_id);
end case;
- 循环语句
--for循环
for v_i in reverse 1..10 loop
sys_dbms_output.put_line(v_i);
end loop;
--while 循环
while v_id<10 loop
sys.dbms_output.put_line(v_id);
v_id := v_id+1;
end loop;
--loop循环
loop
sys.dbms_output.put_line(v_id);
v_id := v_id +1;
exit when v_id = 10;
end loop;
--通过goto语句完成循环
<>
sys.dbms_output.put_line(v_id);
v_id := v_id +1;
if v_id < 10 then
goto a;
end if;
案例之输出菱形星号
*
***
*****
***
*
- 代码
declare
kong integer := 0;
xing integer := 0;
begin
for i in 1..5 loop
--每行由空格和星号组成
if i<4 then
-- 1. 上半
kong := 3-i;
xing := 2 * i -1;
else
-- 2.下半
kong := i -3;
xing := -2 * i +11;
end if;
--输出空格
for k in 1..kong loop
dbms_output.put(' ');
end loop;
--输出星号
for k in 1..xing loop
dbms_output.put('*');
end loop;
--换行
dbms_output.new_line();
end loop;
end;
游标
操作
- 声明游标
- 打开游标
- 循环提前游标
- 关闭游标
属性
- found属性用于标识当前游标从结果中获取记录时,是否捕获了记录.
- not found 属性是found对立面
- rowcount属性用于返回当前时刻已经获得了多少条记录
- isopen属性用于判断游标是否处于打开状态
前三个属性,均被游标的fetch动作更新
DECLARE CURSOR emps
IS SELECT *
FROM emp;
c_emp emp%ROWTYPE;
BEGIN
IF emps%ISOPEN
THEN
dbms_output.put_line('emps游标打开了');
ELSE
dbms_output.put_line('emps游标关闭了');
END IF;
OPEN emps;
IF emps%ISOPEN
THEN
dbms_output.put_line('emps游标打开了');
ELSE
dbms_output.put_line('emps游标关闭了');
END IF;
FETCH emps into c_emp;
dbms_output.put_line('第一次执行fetch 命令之后,游标的rowcount属性为:'||
emps%ROWCOUNT );
if emps%FOUND THEN
dbms_output.put_line('第一次执行fetch 命令之后,游标found属性值为:true');
END IF;
dbms_output.put_line('-----------');
LOOP
if emps%found THEN
dbms_output.put_line('循环执行....rowcount:'|| emps%ROWCOUNT );
fetch emps into c_emp;
ELSE
dbms_output.put_line('循环完毕....rowcount:'|| emps%ROWCOUNT );
EXIT ;
END IF;
END LOOP;
dbms_output.put_line('-----------');
close emps;
END;
游标实现降薪处理
declare
--声明游标
cursor v_emp_cur is select empno,sal from emp;
--信息封装
type type_empno_sal is record (v_empno emp.empno%type, v_sal emp.sal%type);
v_temp_empno_sal type_empno_sal;
begin
--打开游标
open v_emp_cur;
--循环提取游标
loop
--去当前游标所在行的数据
fetch v_emp_cur into v_temp_empno_sal;
--判断提取成功或失败
if v_emp_cur%found then
--有数据
sys_dbms_output.put_line(v_temp_empno_sal.vempno||'<>'||v_temp_empno_sal.v_sal);
case
when v_temp_empno_sal.v_sal > 3000 then
update emp set sal = sal*0.6 where empno=v_temp_empno_sal.v_empno;
when v_temp_empno_sal.v_sal > 2000 and v_temp_empno_sal.v_sal<=30000 then
update emp set sal = sal*0.7 where empno = v_temp_empno_sal.v_empno;
when v_temp_empno_sal.v_sal >1500 and v_temp_empno_sal.v_sal <=2000 then
update emp set sal = sal*0.8 where empno = v_temp_empno_sal.v_empno;
else
update emp set sal = sal * 0.99 where empno = v_temp_empno_sal.v_empno;
end case;
else
--游标提取完毕
exit;
end if;
commit;
end loop;
--关闭游标
close v_emp_cur;
end;
动态游标
定义在游标声明时没有设定, 打开时,可以进行动态修改,动态游标分为,强类型动态游标和弱类型动态游标
强类型动态游标
指游标声明时,虽未设定其查询定义,但是已经指定了游标的返回类型
CREATE OR REPLACE PROCEDURE printemp(in_empno IN NUMBER) AS
BEGIN
DECLARE
TYPE EMP_TYPE IS RECORD (
tno emp.empno%TYPE,
tname emp.ename%TYPE,
tsal emp.sal%TYPE
);
--ref cursor表明该自定义类型是一个动态游标
TYPE EMPS_TYPE IS REF CURSOR
--由返回值可以看出该游标是强类型的动态游标
RETURN EMP_TYPE;
p_emp EMP_TYPE;
p_emps EMPS_TYPE;
BEGIN
IF in_empno <= 0
THEN
OPEN p_emps FOR SELECT
EMPNO,
ENAME,
SAL
FROM emp;
ELSE
OPEN p_emps FOR
SELECT
EMPNO,
ENAME,
SAL
FROM emp
WHERE empno = in_empno;
END IF;
FETCH p_emps INTO p_emp;
WHILE p_emps%FOUND LOOP
dbms_output.put_line(p_emp.tno || ':' || p_emp.tname || ':' || p_emp.tsal);
FETCH p_emps INTO p_emp;
END LOOP;
CLOSE p_emps;
END;
END;
--测试该存储过程
BEGIN
printemp(7876);
END;
弱类型动态游标
在声明游标的时候不使用关键字指定游标的返回值类型
CREATE OR REPLACE PROCEDURE printEmpByFlag(in_flag IN VARCHAR2) AS
BEGIN
DECLARE
TYPE NAME_TYPE IS RECORD (
empno emp.empno%TYPE,
ename emp.ename%TYPE
);
TYPE SAL_TYPE IS RECORD (
empno emp.empno%TYPE,
sal emp.sal%TYPE
);
TYPE EMPS_TYPE IS REF CURSOR;
name NAME_TYPE;
sal SAL_TYPE;
emps EMPS_TYPE;
BEGIN
IF upper(in_flag) = 'NAME'
THEN
OPEN emps FOR SELECT
empno,
ename
FROM emp;
FETCH emps INTO name;
WHILE emps%FOUND LOOP
dbms_output.put_line(name.empno || ':' || name.ename);
FETCH emps INTO name;
END LOOP;
ELSIF upper(in_flag) = 'SAL'
THEN
OPEN emps FOR SELECT
empno,
sal
FROM emp;
FETCH emps INTO sal;
WHILE emps%FOUND LOOP
dbms_output.put_line(sal.empno || ':' || sal.sal);
FETCH emps INTO sal;
END LOOP;
END IF;
IF emps%ISOPEN
THEN
CLOSE emps;
END IF;
END;
END;
BEGIN
printEmpByFlag('name');
END;
for循环与游标
declare
--声明游标
cursor cur_emp_no_sal(v_deptno emp.deptno%type) is select empno, sal from emp where deptno = v_deptno;
begin
--for循环提取游标
for v_empno_sal in cur_emp_no_sal(10) loop
sys.dbms_output.put_line(v_empno_sal.empno||'<>'||v_empno_sal.sal);
end loop;
end;
异常处理
--1. 根据异常名字
exception
when too_many_rows then
sys_dbms_output.put_line('too_many_rows');
when others then
--注意: others只能写在最后面
--2. 根据错误代号
exception
when others then
case
when sqlcode = -1476 then
dbms_output.put_line('被0整除异常');
when sqlcode = -1422 then
dbms_output.put_line('返回多条记录赋值');
else
dbms_output.put_line('其他异常');
end case;
sys.dbms_output.put_line('出现异常了'||sqlcode||'<>'||sqlerrm);
rollback;
--3. 自定义异常
declare
my_exec exception;
my_exec2 exception;
--把自定义异常和错误代号绑定
pragma EXCEPTION_INIT(my_exec2,-9527);
begin
if 3>2 then
--抛出异常 throw new 对象
raise my_exec2;
end if;
exception
when my_exec then
SYS.DBMS_OUTPUT.PUT_LINE('自定义异常'||sqlcode);
when my_exec2 then
SYS.DBMS_OUTPUT.PUT_LINE('自定义异常2<>'||sqlcode);
when others then
SYS.DBMS_OUTPUT.PUT_LINE('其他异常');
end;
--4.自定义异常2
declare
my_exec exception;
my_exec2 exception;
pragma EXCEPTION_INIT(my_exec2,-9527);
begin
if 3 > 2 then
--抛出异常 throw new 对象 -20000 到 -20999
RAISE_APPLICATION_ERROR(-20000, '我的异常');
end if;
exception
when my_exec then
SYS.DBMS_OUTPUT.PUT_LINE('自定义异常'||sqlcode);
when my_exec2 then
SYS.DBMS_OUTPUT.PUT_LINE('自定义异常2<>'||sqlcode);
when others then
SYS.DBMS_OUTPUT.PUT_LINE('其他异常'||sqlcode);
end;
存储过程
就是对plsql语句块的封装,命名的语句块,通过名字直接调用
create or replace procedure 过程名(参数名1 in 类型,参数名2 in out 类型,,,,)
is/as
声明部分;
begin
执行部分;
end;
--完整的过程语法
create or replace PROCEDURE get_sal(v_empno in emp.empno%type,v_sal in out emp.sal%type)
is
--声明部分
--v_empno emp.empno%type :=7782; 放到了get_sal中声明
begin
if v_sal is not null then
sys.dbms_output.put_line('有值');
end if;
-- 执行部分
select sal into v_sal from emp where empno = v_empno;
end;
函数
语法上满足过程的所有语法,就比过程多了返回类型
create or replace FUNCTION get_sal_from_empno(v_empno in emp.empno%type)
return emp.sal%type
is
--声明部分
v_sal emp.sal%type;
begin
select sal into v_sal from emp where empno=v_empno;
return v_sal;
end;
存储过程与函数使用上的区别
- 函数注重数字运算
- 过程注重复杂的业务处理
触发器
--命令:
--禁用表的所有触发器
alter table emp disable all triggers;
--要点:
-- 触发器中不能做事务的操作
--事件编程
--1.行级触发器(事后触发器)
create or replace TRIGGER tri_update_dept
after
update on dept
for each row
begin
--SYS.DBMS_OUTPUT.PUT_LINE('更新了');
--同时更新子表中的记录
--update emp set deptno = :new.deptno where deptno = :old.deptno;
end;
--2.指定更新列的触发器
create or replace TRIGGER tri_update_dept
after
update of deptno on dept
for each row
begin
sys.dbms_output.put_line('更新了.......');
--同时更新子表中的记录
udpate emp set deptno = :new.deptno where deptno = :old.deptno;
end;
--3. 同步更新数据
create database link linkhm2 connect to scott identified by tiger using '192.168.34.188/orcl';
create or replace trigger tri_emp_sal
after
update of sal on emp
for each row
begin
--同步更新远程数据库的记录
update emp@linkhm2 set sal = :new.sal where empno= :old.empno;
end;
--4. 语句级触发器(事前触发器)
create or replace trigger tri_op_emp
before
insert or update or delete on emp
begin
--星期日不能对emp做任何操作
if to_char(sysdate,'day') = '星期日' then
raise_application_error(-20008,'星期日不能操作该表');
end if;
end;
注意触发器描述和代码块中不能出现分号
错误:
create or replace trigger tr_before_insert_myemp
before insert
on myemp;
for each row;
begin
:new.sal :=1000;
end;
正确:
create or replace trigger tr_before_insert_myemp
before insert
on myemp;
for each row
begin
:new.sal :=1000;
end;
在多个事件上定义触发器
create or replace trigger tr_insert_update
before insert or update
on myemp
for each row
begin
:new.ename := upper(:new.ename);
end;
语句触发器
创建语句触发器
演示如果利用触发器记录表的修改日志
CREATE TABLE emp_log (
update_by VARCHAR2(10),
update_at DATE
);
--创建触发器
CREATE OR REPLACE TRIGGER tr_emp_log
BEFORE INSERT OR UPDATE
ON MYEMP
BEGIN
INSERT INTO emp_log VALUES (user, sysdate);
END;
触发器的谓词
常用的谓词包括inserting,updating ,deleting,这三个词会返回一个boolean,以表示激活动作是否为insert,update,delete
--更改上面的日子表的结构
ALTER TABLE emp_log
ADD (action VARCHAR2(10));
--创建触发器
CREATE OR REPLACE TRIGGER tr_emp_log
BEFORE INSERT OR UPDATE OR DELETE
ON myemp
BEGIN
IF INSERTING
THEN
INSERT INTO emp_log VALUES (user, sysdate, '插入数据');
END IF;
IF UPDATING
THEN
INSERT INTO emp_log VALUES (user, sysdate, '更新数据');
END IF;
IF DELETING
THEN
INSERT INTO emp_log VALUES (user, sysdate, '删除数据');
END IF;
END;
--插入
INSERT INTO myemp (empno, ename) VALUES (998, 'luoluo');
--更新
UPDATE myemp
SET ename = 'LUOLUO'
WHERE empno = 998;
--删除
DELETE myemp
WHERE empno = 998;
--查询emp_log表
select * from emp_log;
序列
由于oracle中并没有像mysql一样提供自增长的字段类型,所以就产生了序列
创建和使用序列
create sequence student_seq;
--查看序列
select object_name,object_type,status from user_objects
where lower(object_name) = 'student_seq';
select * from user_sequences where lower(sequence_name)='student_seq';
序列的调用方法为seq.currval seq.nextval ,但是需要注意的是,在序列创建之后,应该首先使用seq.nextval,然后才能使用seq.currval
--更改表的结构
alter table students modify(student_id NUMBER PRIMARY KEY );
--插入数据
INSERT INTO students VALUES (student_seq.nextval, 'aaa', 11);
序列初始值start with
drop SEQUENCE student_seq;
create sequence student_seq
START WITH 12;
修改序列属性
--设置序列minvalue,但是不能大于目前的值
alter SEQUENCE student_seq MINVALUE 20;
--设置序列maxvalue
alter SEQUENCE student_seq MAXVALUE 9999;
--设置序列最大值为无限
alter SEQUENCE student_seq NOMAXVALUE ;
修改increment by
increment by就是步长的意思
--创建序列
create sequence test_seq;
--1
select test_seq.nextval from dual;
--2
select test_seq.nextval from dual;
alter sequence test_seq increment by 5;
--7
SELECT test_seq.nextval from dual;