KingbaseES 处理 PL/SQL 运行时错误全解析:从异常捕获到异常处理的实践指南

引言

在国产化数据库替代浪潮中,KingbaseES凭借其与 Oracle PL/SQL 高兼容度,成为金融、政务等关键行业数据库迁移的首选。然而在实际迁移过程中,开发者常遇到"相同的PL/SQL代码在Oracle运行正常,在KingbaseES却报错"的困惑。

本文通过实践,梳理KingbaseES异常处理机制,从语法差异到运行时错误的完整解决方案,共同构建高可靠的国产数据库应用。

No. 文章地址(点击进入)
1 电科金仓KingbaseES数据库解析:国产数据库的崛起与技术创新
2 KingBase数据库迁移利器:KDTS工具深度解析与实战指南
3 KingBase数据库迁移利器:KDTS工具 MySQL数据迁移到KingbaseES实战
4 电科金仓KingbaseES V9数据库:国产数据库的自主创新与行业实践深度解析
5 KingbaseES客户端工具Ksql使用全指南:从安装到高级操作
6 Spring JDBC与KingbaseES深度集成:构建高性能国产数据库应用实战
7 深度解析:基于 ODBC连接 KingbaseES 数据库的完整操作与实践
8 Oracle与Kingbase深度兼容体验:从连接配置到性能优化全解析
9 Python驱动Ksycopg2连接和使用Kingbase:国产数据库实战指南
10 Go语言×Kingbase数据库极速打通:Gokb驱动三步实操,让国产数据库连接效率嘎嘎提升!
11 金仓数据库KingbaseES实现MongoDB平滑迁移全攻略:从架构适配到性能调优的完整实践
12 深度解析:通过ADO.NET驱动Kdbndp高效连接与操作Kingbase数据库
13 PHP驱动Pdo_kdb连接Kingbase数据库全攻略:从零到实战的深度指南
14 KingbaseES数据库操作指南(1):SQL语法从入门到精通
15 KingbaseES数据库操作指南(2):SQL语法从入门到精通
16 KingbaseES通过KDTS实现SQLServer至KingbaseES迁移深度实战指南
17 KingbaseES权限隔离深度解析:从MySQL的遗憾到安全增强的革新之路
18 深度解析KingbaseES数据库备份利器sys_dump:从参数到实战的全流程指南
19 KingbaseES数据库------医疗领域的应用实践与深度探索
20 KingbaseES赋能新能源电力交易------电力现货交易辅助决策系统国产化实践深度解析
21 KingbaseES在政务领域的应用实践------武汉人社大数据平台"数字化服务新模式"
22 KingbaseES在国家电网领域的深度应用与实践------国家电网新一代集控系统
23 KingbaseES 面向应用程序的SQL开发深度实践指南
24 KingbaseES 面向应用程序的SQL开发:从原理到实战的深度探索
25 破局困境:Oracle迁移金仓KingbaseES数据库的深度实践
26 破译JSON密码:KingbaseES全场景JSON数据处理实战指南
27 国产数据库替代指南:SQL Server向KingbaseES平滑迁移

一、异常处理机制

异常(PL/SQL 运行时错误)可能来自设计错误、编码错误、硬件故障或其他来源。无法预先处理所有可能触发的异常,但可以编写异常处理程序,让程序在触发异常的情况下继续运行。

1.1 异常处理原理

KingbaseES三级异常处理架构

  • 语法解析:通过扩展的SQL解析器识别Oracle PL/SQL特有语法
  • 语义逻辑:将Oracle语义规则映射为KingbaseES执行逻辑
  • 函数映射:内置同名函数实现无缝替换

当执行到错误语句时,控制流会跳转到对应的EXCEPTION块。系统在异常条件列表中匹配当前触发的异常,若匹配成功则执行对应的HandlerStatements,否则异常会向外层传播直至被捕获或终止程序。

1.2 封闭块

异常处理程序运行后,控制权转移到封闭块的下一条语句。如果没有封闭块会出现下面的情况

如果异常处理程序在子程序中,则将控制权返回给调用者调用之后的语句处

如果异常处理程序位于匿名块中,则控制权转移到主机环境

如果在没有异常处理程序的 PL/SQL 块中触发异常,则异常会传播。也就是说,异常会在连续的封闭块中向上抛出,直到一个 PL/SQL 块有一个异常处理程序或没有封闭块为止,如果没有异常处理程序,那么 PL/SQL 会向调用者或主机环境返回一个未处理的异常,这将决定最终的返回结果

1.3 异常种类

KingbaseES异常的种类分为:

系统预定义异常

系统预定义异常是 PL/SQL 已命名的异常,这些异常都有一个错误代码,且会在系统运行出错时隐式(自动)触发。

用户自定义异常

可以在任何 PL/SQL 匿名块、子程序或包的声明部分中声明自己的异常。例如,可以声明一个名为 invalid_number 的异常标记一个无效数字。 用户自定义异常必须显式的触发。

异常差异

下面将详细讲解两种异常

1.4 异常处理程序的优点

使用异常处理程序进行异常处理使程序更易于编写和理解,并降低了未处理异常的可能性。

如果没有异常处理程序,则必须检查所有可能触发的异常,并处理它。但这样很容易忽略可能出现的异常,尤其是在无法立即检测到异常的情况下(例如,在计算运行之前使用了错误数据可能无法检测到)。异常处理代码可以分散在整个程序中。

使用异常处理程序,不需要预先知道每个可能触发的异常或它可能发生的位置。只需在可能发生错误的每个块中包含一个异常处理模块。在异常处理模块中,用户可以编写处理特定错误或未知错误的异常处理程序。如果块中的任何地方(包括子块内)发生错误,则异常处理程序都会捕获并处理它。错误处理代码则会被隔离在块的异常处理部分。

一个存储过程使用单个异常处理程序来处理预定义异常 NO_DATA_FOUND,该异常可以出现在两个 SELECT INTO 语句中的任何一个中。

如果多个语句使用相同的异常处理程序,并且用户想知道哪个语句触发了异常,则可以使用变量辅助定位。

如果用户可以确定需要处理哪个异常,就可以为特定异常设置一个异常处理程序。用户还可以通过将语句放入具有自己的异常处理程序的块中来检查单个语句中的异常。

二、系统预定义异常

针对许多比较常见的异常以及系统运行时会触发的异常, PL/SQL 内部为其预定义了一个名称。例如:除零错误,对应的预定义异常名称为 DIVISION_BY_ZERO。当错误发生时,系统隐式(自动)抛出该异常。

PL/SQL 预定义异常'

预定义异常信息可通过数据库系统函数 SYS_GET_PREDEFINED_EXCEPTION_DETAIL 进行查询

2.1 匿名块中处理 DIVISION_BY_ZERO

sql 复制代码
set serverout on

\set SQLTERM /
DECLARE
  stock_price   NUMBER := 9.73;
  net_earnings  NUMBER := 0;
  pe_ratio      NUMBER;
BEGIN
  pe_ratio := stock_price / net_earnings;  -- raises ZERO_DIVIDE exception
  DBMS_OUTPUT.PUT_LINE('Price/earnings ratio = ' || pe_ratio);
EXCEPTION
  WHEN ZERO_DIVIDE THEN
    DBMS_OUTPUT.PUT_LINE('Company had zero earnings.');
    pe_ratio := NULL;
END;
/
\set SQLTERM ;

结果:

sql 复制代码
Company had zero earnings.

2.2 匿名块中避免 DIVISION_BY_ZERO

sql 复制代码
\set SQLTERM /
DECLARE
  stock_price   NUMBER := 9.73;
  net_earnings  NUMBER := 0;
  pe_ratio      NUMBER;
BEGIN
  pe_ratio :=
    CASE net_earnings
      WHEN 0 THEN NULL
      ELSE stock_price / net_earnings
    END;
END;
/
\set SQLTERM ;

三、用户自定义异常

用户可以在任何 PL/SQL 块,函数、存储过程或者包中声明一个异常。

语法格式:

sql 复制代码
exception_name EXCEPTION ;

用户自定义异常必须被显式触发,有关异常触发的详细信息

用户自定义异常可以与一个错误码进行绑定,具体语法为:

sql 复制代码
PRAGMA EXCEPTION_INIT (exception, error_code) ;

其中 exception 是用户自定义的异常, error_code 是大于 -1000000 且小于 0 的整数,error_code 可以是系统预定义异常的错误码。

四、重新声明预定义的异常

建议不要重新声明系统预定义的异常,即声明用户定义的异常名称与系统预定义的异常名称同名

如果重新声明了系统预定义的异常,那么本地声明将覆盖STANDARD 包的全局声明。此时只能使用 STANDARD包名来限定全局异常名称,否则异常处理程序无法处理全局异常。

重新声明预定义异常:

sql 复制代码
DROP TABLE IF EXISTS t CASCADE;
CREATE TABLE t (c NUMBER(2,1));

在下面的块中,INSERT 语句隐式引发了 VALUE_ERROR 异常,异常处理程序捕获并处理了这个异常。

sql 复制代码
set serverout on

\set SQLTERM /
DECLARE
  default_number NUMBER := 0;
BEGIN
  INSERT INTO t VALUES(TO_NUMBER('100.001', '9G999'));
EXCEPTION
  WHEN VALUE_ERROR THEN
    DBMS_OUTPUT.PUT_LINE('Substituting default value for invalid number.');
    INSERT INTO t VALUES(default_number);
END;
/
\set SQLTERM ;

结果:

sql 复制代码
Substituting default value for invalid number.

以下块重新声明了预定义的异常VALUE_ERROR。当 INSERT 语句隐式触发预定义异常 VALUE_ERROR 时,异常处理程序不会处理它。

sql 复制代码
set serverout on

\set SQLTERM /
DECLARE
  default_number NUMBER := 0;
  value_error EXCEPTION;    -- redeclare predefined exception
BEGIN
  INSERT INTO t VALUES(TO_NUMBER('100.001', '9G999'));
EXCEPTION
  WHEN VALUE_ERROR THEN
    DBMS_OUTPUT.PUT_LINE('Substituting default value for invalid number.');
    INSERT INTO t VALUES(default_number);
END;
/
\set SQLTERM ;

结果:

sql 复制代码
ERROR:  numeric field overflow
DETAIL:  A field with precision 2, scale 1 must round to an absolute
value less than 10^1.
CONTEXT:  SQL statement "INSERT INTO t VALUES(TO_NUMBER('100.001', '9G999'))"
PL/SQL function inline_code_block line 5 at SQL statement

如果在异常处理程序中使用包名 STANDARD 限定异常名称,则上述块中的异常处理程序将处理预定义的异常 VALUE_ERROR:

sql 复制代码
set serverout on

\set SQLTERM /
DECLARE
  default_number NUMBER := 0;
  value_error EXCEPTION;    -- redeclare predefined exception
BEGIN
  INSERT INTO t VALUES(TO_NUMBER('100.001', '9G999'));
EXCEPTION
  WHEN STANDARD.VALUE_ERROR THEN
    DBMS_OUTPUT.PUT_LINE('Substituting default value for invalid number.');
    INSERT INTO t VALUES(default_number);
END;
/
\set SQLTERM ;

结果:

sql 复制代码
Substituting default value for invalid number.

五、显式触发异常

要显式触发常,使用 RAISE 语句或 存储过程RAISE_APPLICATION_ERROR。

5.1 RAISE 语句

RAISE 语句可以显式的触发一个异常。在异常处理程序之外,RAISE 语句必须指定异常名称。如果在异常处理程序内部,且省略了异常名称,那么该 RAISE 语句将重新引发当前正在处理的异常。

语法格式:

sql 复制代码
RAISE [ exception ];

其中 exception 可以是已定义的用户自定义异常,也可以是系统预定义异常。

省略 exception 的 RAISE 子句仅可在异常处理模块中使用。

5.2 使用 RAISE 语句触发用户自定义的异常

存储过程声明了一个名为 past_due 的异常,使用 RAISE 语句显式触发它,并使用异常处理程序处理它。

声明、触发和处理用户定义的异常

sql 复制代码
\set SQLTERM /
CREATE PROCEDURE account_status (
  due_date DATE,
  today    DATE)
AUTHID DEFINER IS
  past_due  EXCEPTION;  -- declare exception
BEGIN
  IF due_date < today THEN
    RAISE past_due;  -- explicitly raise exception
  END IF;
EXCEPTION
  WHEN past_due THEN  -- handle exception
    RAISE NOTICE 'Account past due.';
END;
/
\set SQLTERM ;

调用存储过程:

sql 复制代码
\set SQLTERM /
BEGIN
  account_status (TO_DATE('01-JUL-2010', 'DD-MON-YYYY'),
                  TO_DATE('09-JUL-2010', 'DD-MON-YYYY'));
END;
/
\set SQLTERM ;

输出结果:

sql 复制代码
NOTICE: Account past due.

5.3 使用 RAISE 语句触发系统预定义的异常

系统预定义异常通常由系统运行时隐式触发,但也可以使用 RAISE 语句显式地触发它们。当一个预定义异常拥有对应的异常处理程序时,无论是显式触发还是隐式触发,都会触发异常处理程序对相应的异常进行处理。

存储过程不论显式或是隐式触发预定义异常 VALUE_ERROR,异常处理程序始终都会处理它。

触发系统预定义异常

sql 复制代码
DROP TABLE IF EXISTS t CASCADE;
CREATE TABLE t (c NUMBER(2,1));

\set SQLTERM /
CREATE PROCEDURE p (n NUMBER) AUTHID DEFINER IS
  default_number NUMBER := 0;
BEGIN
  IF n < 0 THEN
    RAISE VALUE_ERROR;  -- 显式触发
  ELSE
    INSERT INTO t VALUES(TO_NUMBER('100.001', '9G999'));  -- 隐式触发
  END IF;
EXCEPTION
  WHEN VALUE_ERROR THEN
    RAISE NOTICE 'Substituting default value for invalid number.';
    INSERT INTO t VALUES(default_number);
END;
/
\set SQLTERM ;

调用存储过程:

sql 复制代码
\set SQLTERM /
BEGIN
  p(-1);
END;
/
\set SQLTERM ;

输出结果:

sql 复制代码
NOTICE: Substituting default value for invalid number.

再次调用存储过程:

sql 复制代码
\set SQLTERM /
BEGIN
  p(1);
END;
/
\set SQLTERM ;

输出结果:

sql 复制代码
NOTICE:  Substituting default value for invalid number.

六、总结与展望

KingbaseES通过完善的异常处理机制,实现了从语法兼容到运行时行为的全面Oracle兼容。其三级异常处理架构、丰富的预定义异常、灵活的自定义异常能力,配合智能诊断工具,构建了完整的异常处理生态体系。

相关推荐
leo_2322 小时前
表&表结构--SMP(软件制作平台)语言基础知识之三十三
数据库·开发工具·表结构·smp(软件制作平台)·应用系统
C***11502 小时前
Spring TransactionTemplate 深入解析与高级用法
java·数据库·spring
+VX:Fegn08953 小时前
计算机毕业设计|基于springboot + vue建筑材料管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
2301_800256113 小时前
B+树:数据库的基石 R树:空间数据的索引专家 四叉树:空间划分的网格大师
数据结构·数据库·b树·机器学习·postgresql·r-tree
大厂技术总监下海3 小时前
用户行为分析怎么做?ClickHouse + 嵌套数据结构,轻松处理复杂事件
大数据·数据结构·数据库
alonewolf_993 小时前
深入理解MySQL事务与锁机制:从原理到实践
android·数据库·mysql
朝依飞3 小时前
fastapi+SQLModel + SQLAlchemy2.x+mysql
数据库·mysql·fastapi
3***g2054 小时前
redis连接服务
数据库·redis·bootstrap
m0_598177234 小时前
SQL 方法函数(1)
数据库