PLSQL笔记

语法

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

游标

操作

  1. 声明游标
  2. 打开游标
  3. 循环提前游标
  4. 关闭游标

属性

  1. found属性用于标识当前游标从结果中获取记录时,是否捕获了记录.
  2. not found 属性是found对立面
  3. rowcount属性用于返回当前时刻已经获得了多少条记录
  4. 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;

存储过程与函数使用上的区别

  1. 函数注重数字运算
  2. 过程注重复杂的业务处理

触发器

--命令:
    --禁用表的所有触发器
    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;

你可能感兴趣的:(PLSQL笔记)