PLSQL新手新手向入门修炼(1)

PLSQL新手新手向入门修炼(1)

由于本人对plSQL理解有限,如果文章中出现什么问题,麻烦大家帮我指出来,攻城狮之路,互勉以修远。
本篇文章主要就以下几点来进行展开
(1)plSQL基础写法
(2)plSQL中的控制语句
(3)plSQL中的游标
(4)plSQL意外处理
(5)plSQL存储过程及函数

1.plSQL基础写法

基本格式-匿名块

DECLARE
自定义变量名 类型
BEGIN
SELECT 库中目标变量
INTO 自定义变量
FROM 目标表
WHERE 条件
[EXCEPTION]
异常处理
END;

在编写匿名块的时候有以下几点需要注意的:
1)‘库中目标变量名’与‘自定义变量名’不要相同
2)在声明自定义变量的时候,最好赋予一个初始值,进行初始化,同时在给自定义变量取名的时候要注意场景要求
3)在给自定义变量定义类型的时候

  • 可以使用 %TYPE 来声明与 XX 相同的类型+赋值,如下所示

    v_number employees.id%TYPE := &employee_id;

  • 可以使用 %ROWTYPE 来声明某个变量,使得其类型与与某张表的记录或者自定义记录类型保持一致 , 如下所示

    根据表的记录
    c_record employees%ROWTYPE;

4)在plSQL中 := 是赋值符号, 如果在赋值的同时想要手动输入变量 , 可以使用 &(变量名称) 这样在程序运行的同时就可以先手动输入变量了
5)在plSQL中有些SQL的函数是不能够使用的,如decode函数,分组函数(avg,sum…)
6)plSQL 中insert , update , delete ,merge 等语句与在 SQL 中操作基本相同,但指定条件的时候,可以通知declare进行定义赋值

2.plSQL中的控制语句

plSQL 中存在除了case 语句的另外一种条件语句 , 即 if .. else …

if conditiion then
statements
else
statements

1.在使用控制语句的时候我们对于null的判断需要加以注意

AND 判断

ANDTRUEFALSENULL
TRUETRUEFALSENULL
FALSEFALSEFALSEFALSE
NULLNULLFALSENULL

OR 判断

ORTRUEFALSENULL
TRUETRUETRUETRUE
FALSETRUEFALSENULL
NULLTRUENULLNULL

NOT 判断

TRUEFALSENULL
NOTFALSETRUENULL

一般而言只要记住:

  • 当 AND 的时候 true + null –> null
  • 当 OR 的时候 FALSE+ null –> null

其他情况则按正常情况去判断.

2.循环控制语句

主要分为以下三种:

1)基本循环

LOOP
statement;

EXIT WHEN condition;
END LOOP;

2)Wihle循环

WHILE i < upper_bound LOOP
statement;

END LOOP;

3)For循环

FOR i IN lower_bound..upper_bound LOOP
statement;

END LOOP;

注意:
结合内存表使用,可以实现类似于数组的遍历
首先内存表的定义为:

TYPE table_name IS TABLE OF
目标类型(可以是自定义,某列%type,某表%ROWTYPE)
INDEX BY BINARY_INTEGER;

通过 index by binary_integer 来当做数组的脚标,以便于遍历

我们来举个栗子:

DECLARE
 TYPE STUDENT_TYPE IS TABLE OF STUDENT%ROWTYPE 
 INDEX BY BINARY_INTEGER;
  C_STUDENT STUDENT_TYPE;
  V_COUNT   NUMBER(7, 2) := 17;
BEGIN
  FOR i IN 1 .. V_COUNT LOOP
    SELECT *
      INTO C_STUDENT(i)
      FROM STUDENT
     WHERE TO_NUMBER(SUBSTR(STUDENT.STUDENT_NO, 2, 3)) = i;
  END LOOP;
  FOR i IN C_STUDENT.FIRST .. C_STUDENT.LAST LOOP
    DBMS_OUTPUT.PUT_LINE(C_STUDENT(i).STUDENT_NAME);
  END LOOP;
END;

这个例子中还存在一个问题,就是说第一个循环的最终变量是手动输入的,我们需要通过表达式来使这个变量呈现动态变化,这个问题的解决方案我会贴在我日后的文章中

3.plSQL中的游标

游标是plSQL中很重要的一部分,所以务必通过实际验证以校验自己的了解情况如何,游标分为两种类型

  • 显式
    由程序员声明的游标,对于返回多行结果的SQL语句返回结果,可以使用显式游标独立的处理其中每一行的数据
  • 隐式
    oracle 通过使用隐式游标来解析和执行我们提交的SQL语句

1)关于隐式存在以下几条有用的属性:(重要)

*  SQL%ROWCOUNT  受最近的SQL语句影响的行数
*  SQL%FOUND     最近的SQL语句是否影响了一行以上的数据
*  SQL%NOTFOUND  最近的SQL语句是否未影响任何数据
*  SQL%ISOPEN    对于隐式游标而言永远为false

2)关于显式游标的使用(重要)

有以下几个步骤:

  • 声明游标
  • 打开游标
  • 提取当前行到变量
  • 测试行的存在
  • 关闭游标

我们来举个栗子:

DECLARE 
V_ST_NO STUDENT.STUDENT_NO%TYPE;
V_ST_NAME STUDENT.STUDENT_NAME%TYPE;
CURSOR ST_CURSOR IS
  SELECT STUDENT_NO,STUDENT_NAME
  FROM STUDENT;
BEGIN
  OPEN  ST_CURSOR;
  LOOP
    FETCH ST_CURSOR INTO V_ST_NO , V_ST_NAME;
    EXIT WHEN ST_CURSOR%ROWCOUNT > 10 OR ST_CURSOR%NOTFOUND;
    DBMS_OUTPUT.put_line(V_ST_NO  ||' ' || V_ST_NAME );
    END LOOP;
    CLOSE ST_CURSOR;
END; 

在这段代码中我们将student表中的id,name通过游标将前十条进行了输出,在这之中主要要注意的这段:

FETCH ST_CURSOR INTO V_ST_NO , V_ST_NAME;
–将游标中的值插入到指定自定义变量中
EXIT WHEN ST_CURSOR%ROWCOUNT > 10 OR ST_CURSOR%NOTFOUND;
–通过对最近执行语句条数的判断,从而决定何时跳出循环

我们还可以通过 FOR 循环语句来使用游标,相对之前的这种使用方式写法较为简单,但可读性没有之前这条来的强,但是使用 FOR 循环语句来使用游标在效率上比前一种块上许多

我们来举个栗子:

DECLARE 
V_ST_NO STUDENT.STUDENT_NO%TYPE;
V_ST_NAME STUDENT.STUDENT_NAME%TYPE;
BEGIN 
  FOR ST_CURSOR IN(SELECT STUDENT_NO,STUDENT_NAME
  FROM STUDENT) LOOP
   V_ST_NO := ST_CURSOR.STUDENT_NO;
   V_ST_NAME := ST_CURSOR.STUDENT_NAME;
   DBMS_OUTPUT.put_line(V_ST_NO  ||' ' || V_ST_NAME );
  END LOOP ;
END;

在使用游标的时候我们还可以使用带参数的游标,来缩小范围以提高效率。

在某些情况下我们使用游标是为了更新或者删除一些记录,在这种情况下我们需要对这部分数据进行锁定,此时我们需要在声明的最后面加上一条语句: for update of 目标条件 nowait

还有一种情况就是由于我们经常要逐条处理游标中的每一条记录,在循环体中做update 或者 delete 是需要有where 指向游标的当前记录,我们可以有更简单的 where 条件写法,我们可以将where条件语句写在游标中,如下:

DECLARE  
CURSOR ST_CURSOR IS  
 SELECT S.STUDENT_NO, S.STUDENT_NAME FROM STUDENT S WHERE TO_NUMBER(SUBSTR(S.STUDENT_NO, 2, 3))< 11 FOR UPDATE OF STUDENT_NO NOWAIT;  
 BEGIN FOR ST_record IN ST_CURSOR LOOP IF TO_NUMBER(SUBSTR(ST_record.STUDENT_NO, 2, 3)) < 11 THEN UPDATE STUDENT SET STUDENT_NO = ST_record.STUDENT_NO || 'M' WHERE CURRENT OF ST_CURSOR;  
  END IF;  
END LOOP;  
END;

4.plSQL意外处理

1)处理预定义例外
在 oracle 中存在一些常见例外,它已经帮我们预定义好了,使用时无需实现声明如:

  • ——NO_DATA_FOUND
  • ——TOO_MANY_ROWS
  • ——INVALID_CURSOR
  • ——ZERO_DIVIDE等

栗子如下:

BEGIN ... EXCEPTION WHEN NO_DATA_FOUND THEN statement; WHEN TOO_MANY_ROWS THEN statement; WHEN OTHERS THEN STATEMENT;
END;

我们可以通过使用 SQLCODE , SQLERRM 来分别返回Oracle 的错误号和错误描述,栗子如下:

DECLARE
  V_ERROR_CODE    NUMBER;
  V_ERROR_MESSAGE VARCHAR2(255);
BEGIN .. . EXCEPTION ... WHEN OTHERS THEN ROLLBACK;
  V_ERROR_CODE    := SQLCODE;
  V_ERROR_MESSAGE := SQLERRM;
  INSERT INTO ERRORS VALUES (V_ERROR_CODE, V_ERROR_MESSAGE);
END;

一般而言很多情况下例外都是数据库之前没有预声明的,此时就需要我们对这些例外进行声明处理,一般而言我们通过raise语句显示的抛出例外

如果觉得先声明再抛出例外很麻烦,也可以使用 RAISE_APPLICATION_ERROR()函数进行简化处理
如下所示

RAISE_APPLICATION_ERROR(-22202 , ‘This is not a valid manager ‘);

同时,还请注意,当我们在使用匿名块的嵌套的时候,当内层例外没有被处理,会一层一层向外传递,直到外层处理位置,或直接报错

5.plSQL存储过程及函数

这个存储过程及函数的调用是很重要的一块,通过这一块的调用,可以大幅提高代码的复用性,下文会通过我的一些代码以及个人看法来进行介绍:

1)存储过程的定义,我们举个栗子来看看怎么写:

CREATE OR REPLACE PROCEDURE ST_PROCEDURE (ST_NO IN STUDENT.STUDENT_NO%TYPE, ST_NAME OUT STUDENT.STUDENT_NAME%TYPE, ST_GEN IN OUT STUDENT.STUDENT_GENDER%TYPE) IS BEGIN SELECT S.STUDENT_NAME, S.STUDENT_GENDER INTO ST_NAME, ST_GEN FROM STUDENT S WHERE S.STUDENT_NO = ST_NO;
END ST_PROCEDURE;

一般而言大致模板如此,我们需要注意的主要有3点:

  • 对于()中的参数有三种关系可以进行书写,IN ,OUT , IN OUT 。
    *IN 指的是由运行环境输入至存储过程中
    *OUT 指的是由存储过程返回至运行环境
    *IN OUT 则是既可以输入,也可以输出
  • 存储过程不存在返回值,即RETURN ,但可以输出值
  • 对于在存储过程中出现的例外,如果在存储过程中没有解决,则会直接跳出存储过程,至调用存储过程的外部例外处理程序中

2)函数的定义,我们举个栗子来看看怎么写:

CREATE OR REPLACE FUNCTION ST_FUNCTION (ST_NO IN STUDENT.STUDENT_NO%TYPE) RETURN VARCHAR2 IS V_NAME VARCHAR2(200);
BEGIN SELECT S.STUDENT_NAME INTO ST_NAME FROM STUDENT S WHERE S.STUDENT_NO = ST_NO;
   V_NAME := ST_NAME;
  RETURN(V_NAME);
END ST_FUNCTION;

函数的使用范围很广,因为其拥有返回值,其返回值可以是单独的值,也可以是一个表对象,所以在使用的时候存在以下几点需要注意的:
(1)函数只能被调用,与存储关系不同的,存储关系可以单独进行运行,但是函数只能在语句中被调用;
(2)函数中只能存在 IN 模式的参数;
(3)只能接收或者返回SQL类型的数据,不能接收plSQL特有的参数比如recored ,plSQL内存表等;
(4)在SQL语句中使用函数,函数中不能包含DML语句,事务结束语句等;
(5)在UPDATE / DETELE 语句中调用函数,函数中不能存在针对该表的select 语句。

    原文作者:落幕米欧
    原文地址: https://blog.csdn.net/qq_29495699/article/details/76038060
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞