PL/SQL

PL/SQL

1.1PL/SQL简介

PL/SQL是一种块结构的语言,它将一组语句放在一个块中,一次性发送给服务器,PL/SQL引擎分析收到PL/SQL语句块中的内容,把其中的过程控制语句由PL/SQL引擎自身去执行,把PL/SQL块中的SQL语句交给服务器的SQL语句执行器执行。

简单来说就是对于sql语言的过程化扩展。使得sql具有过程处理的能力。(减少数据库与代码的交互,提高执行效率)

在这里插入图片描述

① 声明部分:声明部分包含了变量和常量的定义。这个部分由关键字DECLARE开始,如果不声明变量或者常量,可以省略这部分。

② 执行部分:执行部分是 PL/SQL块的指令部分,由关键字BEGIN开始,关键字END结尾。所有的可执行PL/SQL语句都放在这一部分,该部分执行命令并操作变量。其他的PL/SQL块可以作为子块嵌套在该部分。PL/SQL块的执行部分是必选的。注意END关键字后面用分号结尾。

③ 异常处理部分:该部分是可选的,该部分用EXCEPTION关键字把可执行部分分成两个小部分,之前的程序是正常运行的程序,一旦出现异常就跳转到异常部分执行。


declare 
  -- Local variables here(声明变量,游标的地方,没有变量游标的话,是可以省略的)
  i integer;
begin
  -- Test statements here
  -- 执行分析业务逻辑
  -- 异常处理
  
end;
类型符号说明
赋值运算符:=Java和C#中都是等号,PL/SQL的赋值是:=
特殊字符||字符串连接操作符。
PL/SQL中的单行注释。
/,/PL/SQL中的多行注释,多行注释不能嵌套。
<<,>>标签分隔符。只为了标识程序特殊位置。
范围操作符,比如:1…5 标识从1到5
算术运算符+,-,*,/基本算术运算符。
**求幂操作,比如:3**2=9
关系运算符>,<,>=,<=,=基本关系运算符,=表示相等关系,不是赋值。
<>,!=不等关系。
逻辑运算符AND,OR,NOT逻辑运算符。

1.2最简单的输出hello world

最简单的输出hello world

begin
  -- dbms_output相当于java中的system.out,put相当于print,put_line相当于println
  dbms_output.put_line('hello world');
end;

在sqlplus中执行输出语句,需要先运行

 set serveroutput on;

保证输出功能被打开

之后就是输入

begin
  -- dbms_output相当于java中的system.out,put相当于print,put_line相当于println
  dbms_output.put_line('hello world');
end;
/

在plsql中的命令行窗口中也需要加 /

1.3变量

  • 普通数据类型(char,varchar2,date,number,boolean,long)

  • 特殊变量类型(引用型变量,记录型变量)

  • 声明变量的方式

    –1.定义变量时,建议用v_作为前缀:v_ename
    –2.定义常量时,建议用c_作为前缀:c_rate
    –3.定义异常时,建议用e_作为前缀:e_error

变量名 变量类型(变量长度) 例如:  v_name varchar2(20) ;
  • 变量赋值方式

    1.直接赋值 :=

    ​ 例子: v_name :=‘zhangsan’ ;

    2.语句赋值 select 值 into 变量

    ​ 例子: select ‘zhangsan’ into v_name

-- 打印人员个人信息,包括姓名,薪水,地址
declare
  v_name varchar2(20) := '张三'; --直接赋值
  v_scl  number(20);
  v_addr varchar2(200);

begin
  --直接赋值
  v_scl := 1580;
  --语句赋值 select 值 into 变量
  select '标准地址' into v_addr from dual;
  --打印
  dbms_output.put_line('姓名:' || v_name || ' 薪水:' || v_scl || ' 地址:' || v_addr);
end;

① 变量初始化时,可以使用DEFAULT关键字对变量进行初始化。

② 使用select…into语句对变量赋值,要求查询的结果必须是一行,不能是多行或者没有记录。

常量在声明时赋予初值,并且在运行时不允许重新赋值。使用CONSTANT关键字声明常量。

-- 常量
declare 
PI constant number :=3.14;
  i integer;
begin
 /* PI :=10;*/
dbms_output.put_line(PI);

end;
类型说明
VARCHAR2(长度)可变长度字符串,Oracle SQL定义的数据类型,在PL/SQL中使用时最常32767字节。在PL/SQL中使用没有默认长度,因此必须指定。
NUMBER(精度,小数)Oracle SQL定义的数据类型,
DATEOracle SQL定义的日期类型,
TIMESTAMPOracle SQL定义的日期类型,
CHAR(长度)Oracle SQL定义的日期类型,固定长度字符,最长32767字节,默认长度是1,如果内容不够用空格代替。
LONGOracle SQL定义的数据类型,变长字符串基本类型,最长32760字节。在Oracle SQL中最长2147483647字节。
BOOLEANPL/SQL附加的数据类型,逻辑值为TRUE、FALSE、NULL
BINARY_INTEGERPL/SQL附加的数据类型,介于-231和231之间的整数。
PLS_INTEGERPL/SQL附加的数据类型,介于-231和231之间的整数。类似于BINARY_INTEGER,只是PLS_INTEGER值上的运行速度更快。
NATURALPL/SQL附加的数据类型,BINARY_INTEGER子类型,表示从0开始的自然数。
NATURALN与NATURAL一样,只是要求NATURALN类型变量值不能为NULL。
POSITIVEPL/SQL附加的数据类型,BINARY_INTEGER子类型,正整数。
POSITIVEN与POSITIVE一样,只是要求POSITIVE的变量值不能为NULL。
REALOracle SQL定义的数据类型,18位精度的浮点数
INT,INTEGER,SMALLINTOracle SQL定义的数据类型,NUMBERDE的子类型,38位精度整数。
SIGNTYPEPL/SQL附加的数据类型,BINARY_INTEGER子类型。值有:1、-1、0。
STRING与VARCHAR2相同。

​ PL/SQL中标量数据类型。

  • 引用型变量

    变量的类型和长度取决于表中字段的类型和长度。

    通过表名.列名%TYPE 指定变量的类型和长度。

    使用这个引用型变量的原因是保证与原表中的类型相同,防止由于类型原因导致报错,比如varchar2(20)与varchar2(1),这种情况就可能会报错,因为存不下数据,并且之后可能会对于原表改字段类型,导致出现问题(这种情况很少,毕竟是企业级开发,对于数据库定义更改情况真的少,还是建议使用这种方法)。

    例子:

    v_name   yanxy_test1.a_name%TYPE  := '张三';
    
    -- 打印 yanxy_test1表中的a_no=1数据的信息
    declare
      v_name   yanxy_test1.a_name%TYPE ;
      v_date   yanxy_test1.a_date%TYPE ;
    
    begin
      --查询
      select a_name,a_date into v_name,v_date from  yanxy_test1 where a_no=1;
      --打印
      dbms_output.put_line('姓名:' || v_name || ' 时间'|| v_date);
    end;
    
    

    这个例子中,最重要的是查询语句

    需要把查询字段赋值给对应的declare中的字段,并且要一一对应。

    下面是简单的建立了一个表,对应增加数据有三种方法。

     create table yanxy_test1(
       a_no number,
       a_name varchar2(20),
       a_date date);
    

    1.insert 插入数据

    2.既然在plsql中,就可以使用rowid

    3.使用for update

  • 记录型变量

    接收表中的一条记录

    语法:

    变量名称 表名%ROWTYPE

    虽然看起来使用简单,不需要建立那么多的变量,但*会增加数据库运行的效率,很不建议使用。

-- 打印 yanxy_test1表中的a_no=1数据的信息
declare
  v_person  yanxy_test1%ROWTYPE;

begin
  --查询
  select * into v_person from  yanxy_test1 where a_no=1;
  --打印
  dbms_output.put_line('姓名:' || v_person.a_name || ' 时间'|| v_person.a_date);
end;

使用时会产生的错误:

1.以一个数据接收一条数据

select a_no into v_person from  yanxy_test1 where a_no=1;

2.以一条数据的量接收多行数据

select * into v_person from  yanxy_test1 where a_no in(1,2);

1.4条件判断

1.4.1 if
java中if语法PL/SQL中IF语法
if (条件){ //条件结构体 }IF 条件 THEN --条件结构体 END IF;

**案例1:**查询1号员工的工资,如果大600元,则发奖金8%。

declare 
 salary1 emp_test.salary%type;
begin
select a.salary into salary1 from s_emp a where a.id=1; 
if salary1>600 then 
   update emp_test 
   set commission_pct=8
   where id=1;
  end if;
  commit;
end;

代码演示:IF-THEN应用

-- Created on 2019/5/29 by 墨 
declare 
 salary1 s_emp.salary%type;
begin
select a.salary into salary1 from s_emp a where a.id=1; 
if salary1>900 then 
  update 
  set
  end if;
      dbms_output.put_line(salary1);
end;

代码解析:

① 先判断条件,如果条件为TRUE,则执行条件结构体内部的内容。

② 在PL/SQL块中可以使用事务控制语句,该COMMIT同时也能把PL/SQL块外没有提交的数据一并提交,使用时需要注意。

java中if语法PL/SQL中IF语法
if (条件){ //条件成立结构体 } else{ //条件不成立结构体 }IF 条件 THEN --条件成立结构体 ELSE --条件不成立结构体 END IF;
  • 对于值进行判断,并将不同的情况进行不同的分析。

    注意:在条件判断中有 elsif ,这与java中有一定的不同,对于这个来说,要注意。

    实际操作时候不要忘记if then end if;

-- 打印s_emp表中的记录是否超过某个值
declare
  v_count number;

begin
  --查询
  select count(1) into v_count from s_emp;
  if v_count > 26 then
    dbms_output.put_line('数==量' || v_count);
  elsif v_count >= 10 then
    dbms_output.put_line('数..量' || v_count);
  else
    dbms_output.put_line('数//量' || v_count);
  
  end if;

end;

1.4.2 case

case有两种使用方法

1.根据情况输出

declare
  v_gender char(3) := upper('&gender');

begin
  case v_gender
    when 'A' then
      dbms_output.put_line('this is A good');
    
    when 'B' then
      dbms_output.put_line('this is B good');
    else
      dbms_output.put_line('this is C good');
  end case;
end;

2.根据情况赋值

DECLARE
  v_grade CHAR(1) := UPPER('&grade');
  p_grade VARCHAR(20);
BEGIN
  p_grade := 
             CASE v_grade
               WHEN 'A' THEN
                'Excellent'
               WHEN 'B' THEN              
                'Very Good'             
               WHEN 'C' THEN               
                'Good'             
               ELSE               
                'No such grade'      
             END;
  dbms_output.put_line('Grade:' || v_grade || ',the result is ' || p_grade);
END;

当然也可以换种方法赋值

这种赋值方式只是将case 后面的值换到后面

DECLARE
  v_grade CHAR(1) := UPPER('&grade');
  p_grade VARCHAR(20);
BEGIN
  p_grade := CASE          
               WHEN v_grade = 'A' THEN            
                'Excellent'         
               WHEN v_grade = 'B' THEN              
                'Very Good'            
               WHEN v_grade = 'C' THEN               
                'Good'            
               ELSE              
                'No such grade'            
             END;
  dbms_output.put_line('Grade:' || v_grade || ',the result is ' || p_grade);
END;

1.5循环

oracle有三种循环方式,打印99乘法表就能看出一定的区别.所有的循环不要忘记加结束end loop;

1.5.1 loop循环

–LOOP循环 条件成立时退出
LOOP
EXIT [when 条件];
语句序列
END LOOP;

在判断条件处,可以以if做判断,exit跳出循环loop,或者以exit when 跳出循环

--1-100求和

declare
  i   number(10) := 0;
  sums number(10) := 0;

begin
  loop
    i:= i + 1;
    sums := sums + i;
    if i >= 100 then
      exit;
    end if;
    --exit when i>=100;
  end loop;
  dbms_output.put_line('sum'||to_char(sums));
end;

--打印数字1-10
declare
  v_id number := 1;
begin
  loop
    exit when v_id > 10; --退出循环条件
    dbms_output.put_line(v_id);
    v_id := v_id + 1;
  end loop;
end;

-- 打印99乘法表
declare
  i number := 1;
  j number := 1;
  m number := 1;
begin
  loop
    loop
      m := i * j;
      dbms_output.put(j || '*' || i || '=' || m || ' ');
      j := j + 1;
      dbms_output.put('');
      exit when j > i;
    end loop;
    dbms_output.new_line;
    i := i + 1;
    j := 1;
    exit when i > 9;
  end loop;
end;

1.5.2WHILE循环

–WHILE 条件成立时循环
WHILE total<=25000 LOOP
语句序列
total:=tatal+salary;
END LOOP;

-- 99乘法表
declare
  i number := 1;
  j number ;
  m number ;
begin
  while i <= 9 loop
    j:=1;
    while j <= i loop
      m := i * j;
      dbms_output.put(j || '*' || i || '=' || m || ' ');
            j := j + 1;
    end loop;
    dbms_output.new_line;--换行
    i := i + 1;
  end loop;
end;

1.5.3FOR循环

–FOR循环 1…3表示区间
FOR I IN 1…3 LOOP
语句序列;

​ END LOOP;

--for循环99乘法表
declare
  i number;
  j number;
  m number;
begin
  for i in 1 .. 9 loop
    for j in 1 .. i loop
      m := i * j;
      dbms_output.put(j || '*' || i || '=' || m || ' ');
    end loop;
    dbms_output.new_line;--换行
  end loop;
end;

使用REVERSE递减

DECLARE
v_i NUMBER;
BEGIN
  FOR v_i IN REVERSE 1..18 LOOP
    dbms_output.put_line(v_i);
  END LOOP;
END;

1.6顺序结构

在程序顺序结构中有两个特殊的语句。GOTO和NULL

GOTO语句将无条件的跳转到标签指定的语句去执行。标签是用双尖括号括起来的标示符,在PL/SQL块中必须具有唯一的名称,标签后必须紧跟可执行语句或者PL/SQL块。GOTO不能跳转到IF语句、CASE语句、LOOP语句、或者子块中。

NULL语句什么都不做,只是将控制权转到下一行语句。NULL语句是可执行语句。NULL语句在IF或者其他语句语法要求至少需要一条可执行语句,但又不需要具体操作的地方。比如GOTO的目标地方不需要执行任何语句时。

DECLARE
  sumsal emp_test.salary%TYPE;
BEGIN
  SELECT SUM(salary) INTO sumsal FROM EMP_test;
  IF sumsal > 20000 THEN
    GOTO first_label;
  ELSE
    GOTO second_label;
  END IF;
  <<first_label>>
  dbms_output.put_line('ABOVE 20000:' || sumsal);
  <<second_label>>
  NULL;
END;

1.7动态sql

declare
  sql_stmt    VARCHAR2(200);
  v_id        dept_test.id%type := 10;
  v_name      dept_test.name%type := 'ASDWE';
  v_region_id dept_test.region_id%type := 2;
  emp_rec     dept_test%ROWTYPE;
begin
  --创建表写法,动态执行一个完整的SQL语句。
  EXECUTE IMMEDIATE 'CREATE TABLE bonus1 (id NUMBER, amt NUMBER)';
  --插入sql语句写法,SQL语句中存在3个参数分别标识为:[:1、:2、:3],因此需要用USING关键字对三个参数分别赋值。
  sql_stmt := 'INSERT INTO dept_test VALUES (:1, :2, :3)';
  EXECUTE IMMEDIATE sql_stmt
    using v_id, v_name, v_region_id;
  --查询表写法,对动态查询语句可以使用INTO子句把查询的结果保存到一个变量中,要求该结果只能是单行。
  sql_stmt := 'SELECT * FROM dept_test WHERE id = :id';
  EXECUTE IMMEDIATE sql_stmt
    INTO emp_rec
    USING v_id;
  --在Oracle的insert,update,delete语句都可以使用RETURNING子句把操作影响的行中的数据返回,对SQL语句中存在RETURNING子句时,在动态执行时可以使用RETURNING INTO来接收。
  sql_stmt := 'UPDATE emp_test SET salary = 2000 WHERE id = :1
      RETURNING salary INTO :2';
  EXECUTE IMMEDIATE sql_stmt
    USING emp_id
    RETURNING INTO salary;
  --动态执行参数中可以是:[:数字]也可以是[:字符串]。
  EXECUTE IMMEDIATE 'DELETE FROM dept_test WHERE id = :num'
    USING dept_id;
end;

2.1 游标

2.1.1什么是游标

用于临时存储一个查询返回的多行数据,通过遍历游标,可以逐行处理该结果集的数据

游标使用的方法:声明–>打开–>读取–>关闭

语法:

声明游标

cursor 游标名[()] is 查询语句;

打开游标:

open 游标名;

读取游标:

fetch 游标名 into 变量列表;

关闭游标;

close 游标名;

–显式游标属性:
%FOUND 找到是否找到数据,有数据TRUE,没有则FALSE
%ISOPEN 判断游标是否打开,打开则返回TRUE,没有打开则返回FALSE
%NOTFOUND 返回FETCH …INTO…是否有数据如果没有返回TRUN,有则为FALSE
%ROWCOUNT 返回执行FETCH 语句所返回的行数,初始为0,每执行一行则%ROWCOUNT增加1

2.2显式游标

--取emp_test表中的两列数据,并循环输出
DECLARE
  CURSOR emp_cur IS
    SELECT id, last_name FROM emp_test; --定义游标
  v_id   emp_test.id%TYPE; --定义变量ID
  v_name emp_test.last_name%TYPE;
BEGIN
  OPEN emp_cur; --打开游标

  LOOP
    FETCH emp_cur
      INTO v_id, v_name; ---提示取游标
    EXIT WHEN emp_cur%notFOUND; --判断是否还有数据
    dbms_output.put_line('员工编号' || v_id || ',员工姓名:' || v_name);
  END LOOP;
  CLOSE emp_cur; --关闭游标
END;

--ROWCOUNT实例
DECLARE
  V_NAME  VARCHAR2(50); --定义变量姓名
  V_DNAME VARCHAR2(50); --定义变量部门名称
  CURSOR CUR_E IS --定义游标
    SELECT ENAME, DNAME FROM EMP, DEPT WHERE EMP.DEPTNO = DEPT.DEPTNO;
BEGIN
  OPEN CUR_E; --打开游标
  LOOP
    --使用循环来读取游标
    FETCH CUR_E
      INTO V_NAME, V_DNAME; --提取游标
    EXIT WHEN CUR_E%NOTFOUND; --判断游标是否还有内容
    DBMS_OUTPUT.PUT_LINE(CUR_E%ROWCOUNT || '  员工姓名:' || V_NAME || ' 部门名称:' ||
                         V_DNAME);                            --输出内容
  
  END LOOP;
  CLOSE CUR_E; --关闭游标
END;
DECLARE
  V_EMPNAME emp_test.last_name%TYPE;
  V_DNAME   DEPT_TEST.NAME%TYPE;
  CURSOR EMP_CUR IS
    SELECT E.last_NAME, D.NAME FROM emp_test E, DEPT_TEST D WHERE E.DEPT_ID = D.ID;
BEGIN
  OPEN EMP_CUR;
  LOOP
    FETCH EMP_CUR
      INTO V_EMPNAME, V_DNAME;
    EXIT WHEN EMP_CUR%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('员工姓名 :' || V_EMPNAME || ',部门名称 :' || V_DNAME);
  END LOOP;
  CLOSE EMP_CUR;
END;
--
DECLARE
  CURSOR CUR_EMP IS
    SELECT * FROM EMP_test;
  V_EMPROW Emp_Test%ROWTYPE;
BEGIN
  IF CUR_EMP%ISOPEN THEN
    NULL;--按照保证不重复开启的原则,可以判断一下
  ELSE
    OPEN CUR_EMP;
  END IF;
  FETCH CUR_EMP
    INTO V_EMPROW;
  WHILE CUR_EMP%FOUND LOOP
    DBMS_OUTPUT.PUT_LINE('员工姓名: ' || V_EMPROW.LAST_NAME || ',职位: ' ||
                         V_EMPROW.TITLE || ' ,工资' || V_EMPROW.SALARY);
    FETCH CUR_EMP
      INTO V_EMPROW;
  END LOOP;
  CLOSE CUR_EMP;
END;
--使用for循环,使用FOR循环操作游标不仅代码简单,而且可以将游标的状态交给系统去完成,尽量使用FOR循环为主
DECLARE 
CURSOR cur_emp IS SELECT * FROM emp_test;
BEGIN
  FOR emp_row1 IN cur_emp LOOP
    DBMS_OUTPUT.PUT_LINE('员工姓名: ' || emp_row1.last_name || ',职位: ' ||
                         emp_row1.manager_id || ' ,工资' || emp_row1.salary);
     END LOOP;
END;

2.3REF动态游标

TYPE 类型名 IS REF CURSOR [RETURN]数据类型
游标名 类型名
OPEN 游标名 FOR 查询语句
–强类型:带RETURN

DECLARE
  TYPE REF_EMP IS REF CURSOR RETURN EMP%ROWTYPE; --定义一个REF动态游标,并返回类型
  CUR_EMP REF_EMP; --定义一个变量类型是上面的REF动态游标也称游标变量
  V_EMP   EMP%ROWTYPE; --定义一个变量,类型和REF游标返回类型相同,行类型
BEGIN
  OPEN CUR_EMP FOR
    SELECT * FROM EMP; --打开游标,并关联查询语句
  LOOP
    FETCH CUR_EMP
      INTO V_EMP; --提取游标数据
    EXIT WHEN CUR_EMP%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(CUR_EMP%ROWCOUNT||'  员工编号:' || V_EMP.EMPNO || ' 员工姓名:' || V_EMP.ENAME);
  END LOOP;
  CLOSE CUR_EMP;
END;

–弱类型:不带RETURN

DECLARE
  TYPE REF_EMP IS REF CURSOR; --定义一个REF动态游标,并返回类型
  CUR_EMP REF_EMP; --定义一个变量类型是上面的REF动态游标也称游标变量
  V_EMP   EMP%ROWTYPE; --定义一个变量,类型和REF游标返回类型相同,行类型
  V_DEPT  DEPT%ROWTYPE; --定义一个变量,类型和REF游标返回类型相同,行类型
BEGIN
  --员工表
  OPEN CUR_EMP FOR
    SELECT * FROM EMP; --打开游标,并关联查询语句
  LOOP
    FETCH CUR_EMP
      INTO V_EMP; --提取游标数据
    EXIT WHEN CUR_EMP%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(CUR_EMP%ROWCOUNT || '  员工编号:' || V_EMP.EMPNO ||
                         ' 员工姓名:' || V_EMP.ENAME);
  END LOOP;
  CLOSE CUR_EMP;
  ------------下面是部门表  
  OPEN CUR_EMP FOR
    SELECT * FROM DEPT; --打开游标,并关联查询语句
  LOOP
    FETCH CUR_EMP
      INTO V_DEPT; --提取游标数据
    EXIT WHEN CUR_EMP%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(CUR_EMP%ROWCOUNT || '  部门编号:' || V_DEPT.DEPTNO ||
                         ' 部门名称:' || V_DEPT.DNAME);
  END LOOP;
  CLOSE CUR_EMP;
END;
在Oracle9i之后为了方便用户使用弱类型游标变量,可以使用  
SYS_REFCURSOR 来替代  TYPE REF_EMP IS REF CURSOR
上面的声明可以换为:
  CUR_EMP  SYS_REFCURSOR; --定义一个变量类型是上面的REF动态游标也称游标变量
  V_EMP   EMP%ROWTYPE; --定义一个变量,类型和REF游标返回类型相同,行类型
  V_DEPT  DEPT%ROWTYPE; --定义一个变量,类型和REF游标返回类型相同,行类型
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值