目录结构
注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:
1、参考书籍:《Oracle Database SQL Language Reference》
2、参考书籍:《PostgreSQL中文手册》
3、EDB Postgres Advanced Server User Guides,点击前往
4、PostgreSQL数据库仓库链接,点击前往
5、PostgreSQL中文社区,点击前往
6、Oracle数据库 GOTO Statement 官方文档说明,点击前往
7、EDB数据库 GOTO statement v14官方文档说明,点击前往
1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
5、本文仅适于从事于PostgreSQL数据库内核开发者和数据库爱好者,对普通读者而言难度较大 但对于希望从事于数据库内核开发的初学者来说,是一次机会十分难得的学习案例 💪
6、本文内容基于PostgreSQL15.0源码开发而成
Oracle数据库PL/SQL语言GOTO语句技术详解
- 本人博客严正声明
- 文章快速说明索引
- GOTO语句的说明
本人博客严正声明
是这样的,熟悉在下的小伙伴们都知道 我写博客主要目的就是分享和学习总结。至于CSDN的排名 排名什么的,我并不是很在意!
- 一来 很不喜欢标题党
- 二来 更反感灌些水文
- 三来 痛恨无下限抄袭
本人博客都是认认真真写的,结果在CSDN并没有什么的太大的名气 关注度什么的也不高!前些天 一位好心的粉丝私聊了在下,反而一名某平台的哥们儿 快把我的《PostgreSQL的学习心得和知识总结》都给照搬过去了,甚至一个字都不改(连同在下 都是只字不提 好歹稍微提一下呀)!!!
实在是太过分,后来经过(友好)协商,现已经全部删除了!
本人是做PostgreSQL内核开发的,深感当下学风不正 大家都很浮躁,一向踏踏实实深耕的并不是很多!因为写代码这件事情上,欺骗不了任何人!本本分分老老实实地写好代码做好学问十分不易,容不得掺沙子和造假!这里把我喜欢的一句话送给各位以共勉:
非淡泊无以明志,
非宁静无以致远!
文章快速说明索引
学习目标:
目的:接下来这段时间我想做一些兼容Oracle数据库PL/SQL语言上的一些功能开发,本专栏这里主要是学习以及介绍Oracle数据库功能的使用场景、原理说明和注意事项等,基于PostgreSQL数据库的功能开发等之后 由新博客进行介绍和分享!今天我们主要看一下 PL/SQL语言GOTO语句 的相关内容!
学习内容:(详见目录)
1、Oracle数据库PL/SQL语言GOTO语句
学习时间:
2022-11-29 10:25:10
学习产出:
1、Oracle数据库PL/SQL语言GOTO语句
2、CSDN 技术博客 1篇
注:下面我们所有的学习环境是Centos7+PostgreSQL15.0+Oracle12c+MySQL5.7
postgres=# select version();
version
-----------------------------------------------------------------------------
PostgreSQL 15.0 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 7.1.0, 64-bit
(1 row)
postgres=#
#-----------------------------------------------------------------------------#
SQL> select * from v$version;
BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
PL/SQL Release 12.2.0.1.0 - Production 0
CORE 12.2.0.1.0 Production 0
TNS for Linux: Version 12.2.0.1.0 - Production 0
NLSRTL Version 12.2.0.1.0 - Production 0
SQL>
#-----------------------------------------------------------------------------#
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.19 |
+-----------+
1 row in set (0.06 sec)
mysql>
GOTO语句的说明
GOTO 语句将控制转移到带标签的块或语句,即:该语句使执行点跳转到具有指定标签的语句。
GOTO 语句的限制如下:
如果 GOTO 语句过早地退出游标 FOR LOOP 语句,游标将关闭。
- GOTO 语句不能将控制转移到 IF 语句、CASE 语句、LOOP 语句或子块中
- GOTO 语句不能将控制从一个 IF 语句子句转移到另一个,或从一个 CASE 语句 WHEN 子句转移到另一个
- GOTO 语句不能将控制转移出子程序
- GOTO 语句无法将控制转移到异常处理程序中
- GOTO 语句无法将控制从异常处理程序转移回当前块(但它可以将控制从异常处理程序转移到封闭块 即:父块)
- GOTO 语句不能将控制转移到条件块或子块中,但可以从条件块或子块中转移控制
- GOTO 语句不能跳转到声明(declaration)
- GOTO 语句不能将控制转移到另一个函数或过程中
- 标签(label)不应放在块、函数或过程的末尾
其语法格式如下:
GOTO <label>
标签:标识块或语句,label
是分配给可执行语句或块的名称。在函数、过程或块的范围内必须是唯一的。要标记语句,请使用以下语法:
<<label>> <statement>
-- 其中 statement 是程序跳转到的执行点
如果 label 不在当前块中,则 GOTO 语句将控制转移到 label 出现的第一个封闭块。你可以标记(label
)赋值语句、任何 SQL 语句和选定的过程语言语句。可以标记的过程语言语句有:
-- EDB (V14)
IF
EXIT
RETURN
RAISE
EXECUTE
PERFORM
GET DIAGNOSTICS
OPEN
FETCH
MOVE
CLOSE
NULL
COMMIT
ROLLBACK
GOTO
CASE
LOOP
WHILE
FOR
exit
被视为关键字,不能用作标签的名称。对应成语法如下:
statement ::= <Assign_Statement>
| <If_Statement>
| <Loop_Statement>
| <While_Statement>
| <For_Statement>
| <Goto_Statement> // 这就是我们这次要开发的
| <Case_Statement>
| <Exit_Statement>
| <Return_Statement>
| <Raise_Statement>
| <Perform_Statement>
| <Execute_Statement>
| <Commit_Statement>
| <RollBack_Statement>
| <Close_Statement>
| <Open_Statement>
| <Fetch_Statement>
| <Sql_Statement>
| <Null_Statement>
| <Get_Diagnostics_Statement>
-- 注:在PostgreSQL15中只会更多,详细请见:
-- src/pl/plpgsql/src/pl_gram.y 的 proc_stmt
其使用案例如下:
示例一:标签可以出现在语句之前:
-- 判断一个数是否为素数
SQL> set serveroutput on;
SQL> DECLARE
p VARCHAR2(30);
n PLS_INTEGER := 37;
BEGIN
FOR j in 2..ROUND(SQRT(n)) LOOP
IF n MOD j = 0 THEN
p := ' is not a prime number';
GOTO print_now;
END IF;
END LOOP;
p := ' is a prime number';
<<print_now>> --here
DBMS_OUTPUT.PUT_LINE(TO_CHAR(n) || p);
END;
/
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 37 is a prime number
PL/SQL procedure successfully completed.
SQL>
SQL> DECLARE
p VARCHAR2(30);
n PLS_INTEGER := 64;
BEGIN
FOR j in 2..ROUND(SQRT(n)) LOOP
IF n MOD j = 0 THEN
p := ' is not a prime number';
GOTO print_now;
END IF;
END LOOP;
p := ' is a prime number';
<<print_now>> --here
DBMS_OUTPUT.PUT_LINE(TO_CHAR(n) || p);
END;
/
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 64 is not a prime number
PL/SQL procedure successfully completed.
SQL>
示例二:标签只能出现在块之前或语句之前:
SQL> DECLARE
done BOOLEAN;
BEGIN
FOR i IN 1..50 LOOP
IF done THEN
GOTO end_loop;
END IF;
<<end_loop>> --here
END LOOP;
END;
/
2 3 4 5 6 7 8 9 10 11 END LOOP;
*
ERROR at line 9:
ORA-06550: line 9, column 3:
PLS-00103: Encountered the symbol "END" when expecting one of the following:
( begin case declare exit for goto if loop mod null raise
return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge
json_exists json_value json_query json_object json_array
SQL>
示例三:标签可以出现在 NULL 语句之前:
SQL> DECLARE
done BOOLEAN;
BEGIN
FOR i IN 1..50 LOOP
IF done THEN
GOTO end_loop;
END IF;
<<end_loop>> --here
NULL;
END LOOP;
END;
/
2 3 4 5 6 7 8 9 10 11 12
PL/SQL procedure successfully completed.
SQL>
SQL> BEGIN
FOR i IN 1..50 LOOP
IF i = 30 THEN
GOTO end_loop;
END IF;
END LOOP;
<<end_loop>>
NULL;
END;
/
2 3 4 5 6 7 8 9 10
PL/SQL procedure successfully completed.
SQL>
示例四:GOTO 语句可以将控制权从当前块转移到封闭块。如下使用GOTO将分出一个环绕块:
create table employees (employee_id number(6), last_name VARCHAR2(25));
insert into employees values (120, 'Weiss');
insert into employees values (121, 'Weiss1');
insert into employees values (122, 'Weiss2');
insert into employees values (123, 'Weiss3');
insert into employees values (124, 'Weiss4');
SQL> select * from employees;
EMPLOYEE_ID LAST_NAME
----------- -------------------------
120 Weiss
121 Weiss1
122 Weiss2
123 Weiss3
124 Weiss4
SQL>
SQL> DECLARE
v_last_name VARCHAR2(25);
v_emp_id NUMBER(6) := 120;
BEGIN
<<get_name>>
SELECT last_name INTO v_last_name
FROM employees
WHERE employee_id = v_emp_id;
BEGIN
DBMS_OUTPUT.PUT_LINE (v_last_name);
v_emp_id := v_emp_id + 2;
IF v_emp_id < 125 THEN
GOTO get_name;
END IF;
END;
END;
/
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Weiss
Weiss2
Weiss4
PL/SQL procedure successfully completed.
SQL>
示例五:GOTO 语句将控制转移到 IF 语句,导致错误:
SQL> DECLARE
valid BOOLEAN := TRUE;
BEGIN
GOTO update_row;
IF valid THEN
<<update_row>>
NULL;
END IF;
END;
/
2 3 4 5 6 7 8 9 10 11 GOTO update_row;
*
ERROR at line 4:
ORA-06550: line 4, column 3:
PLS-00375: illegal GOTO statement; this GOTO cannot branch to label 'UPDATE_ROW'
ORA-06550: line 6, column 12:
PL/SQL: Statement ignored
SQL>
示例六:GOTO用在循环当中。作用类似于continue:
SQL> create or replace procedure test111 is
i integer;
begin
i := 2;
loop
<<next_step>>
i := i * 2;
if i > 100 then
exit;
end if;
if i > 50 then
goto next_step;
end if;
dbms_output.put_line(i);
end loop;
end;
/
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Procedure created.
SQL>
SQL> call test111();
4
8
16
32
Call completed.
SQL>
如上:
<<next_step>>
:这是循环标签,next_step
是名字,可以自己定义goto next_step
:表示i > 50
后继续走到标签所在的位置执行代码- 特别注意:
<<next_step>>
后面不能直接跟EXCEPTION、END LOOP这种关键字类的语句,可以用NULL把标签跟关键字隔开
示例七:下面这个示例验证员工记录是否包含姓名、职位描述和员工雇用日期;如果缺少任何信息,则语句将执行点转移到打印一条消息的语句,该消息表明该员工无效:
-- EDB数据库提供
CREATE OR REPLACE PROCEDURE verify_emp (
p_empno NUMBER
)
IS
v_ename emp.ename%TYPE;
v_job emp.job%TYPE;
v_hiredate emp.hiredate%TYPE;
BEGIN
SELECT ename, job, hiredate
INTO v_ename, v_job, v_hiredate FROM emp
WHERE empno = p_empno;
IF v_ename IS NULL THEN
GOTO invalid_emp;
END IF;
IF v_job IS NULL THEN
GOTO invalid_emp;
END IF;
IF v_hiredate IS NULL THEN
GOTO invalid_emp;
END IF;
DBMS_OUTPUT.PUT_LINE('Employee ' || p_empno ||
' validated without errors.');
RETURN;
<<invalid_emp>> DBMS_OUTPUT.PUT_LINE('Employee ' || p_empno ||
' is not a valid employee.');
END;
示例八:GOTO不能跳转到 exception 中:
SQL> BEGIN
GOTO LB3; --expected error
EXCEPTION
WHEN OTHERS THEN
<<LB3>>
NULL;
END;
/
2 3 4 5 6 7 8 GOTO LB3; --expected error
*
ERROR at line 2:
ORA-06550: line 2, column 2:
PLS-00375: illegal GOTO statement; this GOTO cannot branch to label 'LB3'
ORA-06550: line 4, column 14:
PL/SQL: Statement ignored
SQL>
示例九:
SQL> DECLARE
I INT := 0;
BEGIN
<<LB1>>
I := I + 1;
dbms_output.put_line(I);
BEGIN
<<LB2>>
iF I = 1 THEN
RAISE INVALID_NUMBER;
ELSE
RAISE ZERO_DIVIDE;
END IF;
EXCEPTION
WHEN INVALID_NUMBER THEN
GOTO LB1; -- 如果跳转到 LB2 则报错,即:goto 语句不能从 EXCEPTION 中跳转到当前的语句块中
WHEN ZERO_DIVIDE THEN
dbms_output.put_line('ZERO_DIVIDE');
END;
END;
/
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1
2
ZERO_DIVIDE
PL/SQL procedure successfully completed.
SQL>
SQL> DECLARE
I INT := 0;
BEGIN
<<LB1>>
I := I + 1;
dbms_output.put_line(I);
BEGIN
<<LB2>>
iF I = 1 THEN
RAISE INVALID_NUMBER;
ELSE
RAISE ZERO_DIVIDE;
END IF;
EXCEPTION
WHEN INVALID_NUMBER THEN
GOTO LB2; -- 如果跳转到 LB2 则报错,即:goto 语句不能从 EXCEPTION 中跳转到当前的语句块中
WHEN ZERO_DIVIDE THEN
dbms_output.put_line('ZERO_DIVIDE');
END;
END;
/
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 GOTO LB2; -- 如果跳转到 LB2 则报错,即:goto 语句不能从 EXCEPTION 中跳转到当前的语句块中
*
ERROR at line 16:
ORA-06550: line 16, column 3:
PLS-00375: illegal GOTO statement; this GOTO cannot branch to label 'LB2'
ORA-06550: line 16, column 3:
PL/SQL: Statement ignored
SQL>