Bo's Oracle Station

【博客文章2026】MySQL 9.x数据库管理技巧10:存储过程、函数和异常处理---与Oracle数据库作对比

2026-4-3 23:33| 发布者: admin| 查看: 451| 评论: 0|原作者: Bo Tang

摘要: 本博客系统对比了MySQL 9.6与Oracle 19c在存储过程、函数及异常处理方面的核心差异,并通过完整的实战示例展示了两种数据库的具体实现方式。包括:一、语言与架构对比;二、存储过程对比;三、函数对比和四、异常处理对比。
【博客文章2026】MySQL 9.x数据库管理技巧10:存储过程、函数和异常处理---与Oracle数据库作对比


Author: Bo Tang

    MySQL数据库的编程语言是SQL/PSM (Persistent Stored Modules)。它轻量级、简洁易用,无包概念,每个过程/函数独立存储。它适用于Web应用、中小型系统、OLTP。函数不支持递归,过程默认禁用递归。
    Oracle数据库的编程语言是PL/SQL。它企业级、功能全面,支持包(Package)组织多个子程序复合体。它适用复杂业务逻辑、数据仓库、大型系统。函数和过程都支持递归。

1. MySQL数据库的存储过程与Oracle数据库的存储过程作对比

1.1 在MySQL 9.6数据库中创建一个存储过程,用于计算world_y数据库中的各个表的行数:

[root@station95 ~]# mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 9.6.0 MySQL Community Server - GPL

Copyright (c) 2000, 2026, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use world_y;
mysql> delimiter //
mysql> create procedure row_count()
    -> begin
    ->  select 'How many rows does the table city have : ', count(*) from city;
    ->  select 'How many rows does the table country have : ', count(*) from country;
    ->  select 'How many rows does the table countrylanguage have : ', count(*) from countrylanguage;
    -> end//
Query OK, 0 rows affected (0.003 sec)

mysql> delimiter ;

    使用“show create procedure”命令来查看MySQL数据库里的该存储过程的DDL信息:

mysql>  show create procedure row_count\G
*************************** 1. row ***************************
           Procedure: row_count
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
    Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `row_count`()
begin
 select 'How many rows does the table city have : ', count(*) from city;
select 'How many rows does the table country have : ', count(*) from country;
select 'How many rows does the table countrylanguage have : ', count(*) from countrylanguage;
 end
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
  Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.001 sec)

    使用“show procedure status like”命令来查看MySQL数据库里的该存储过程的状态信息:

mysql> show procedure status like 'row_count%'\G
*************************** 1. row ***************************
                  Db: world_y
                Name: row_count
                Type: PROCEDURE
            Language: SQL
             Definer: root@localhost
            Modified: 2026-04-06 19:49:35
             Created: 2026-04-06 19:49:35
       Security_type: DEFINER
             Comment: 
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
  Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.004 sec)

    在information_schema方案下,查看MySQL数据库里的该存储过程的数据字典信息

mysql> use information_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from routines where specific_name='row_count'\G
*************************** 1. row ***************************
           SPECIFIC_NAME: row_count
         ROUTINE_CATALOG: def
          ROUTINE_SCHEMA: world_y
            ROUTINE_NAME: row_count
            ROUTINE_TYPE: PROCEDURE
               DATA_TYPE: 
CHARACTER_MAXIMUM_LENGTH: NULL
  CHARACTER_OCTET_LENGTH: NULL
       NUMERIC_PRECISION: NULL
           NUMERIC_SCALE: NULL
      DATETIME_PRECISION: NULL
      CHARACTER_SET_NAME: NULL
          COLLATION_NAME: NULL
          DTD_IDENTIFIER: NULL
            ROUTINE_BODY: SQL
      ROUTINE_DEFINITION: begin
 select 'How many rows does the table city have : ', count(*) from city;
select 'How many rows does the table country have : ', count(*) from country;
select 'How many rows does the table countrylanguage have : ', count(*) from countrylanguage;
 end
           EXTERNAL_NAME: NULL
       EXTERNAL_LANGUAGE: SQL
         PARAMETER_STYLE: SQL
        IS_DETERMINISTIC: NO
         SQL_DATA_ACCESS: CONTAINS SQL
                SQL_PATH: NULL
           SECURITY_TYPE: DEFINER
                 CREATED: 2026-04-06 19:49:35
            LAST_ALTERED: 2026-04-06 19:49:35
                SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
         ROUTINE_COMMENT: 
                 DEFINER: root@localhost
    CHARACTER_SET_CLIENT: utf8mb4
    COLLATION_CONNECTION: utf8mb4_0900_ai_ci
      DATABASE_COLLATION: utf8mb4_0900_ai_ci
1 row in set (0.003 sec)

    在MySQL数据库里执行该存储过程

mysql> use world_y;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql>  call row_count();
+-------------------------------------------+----------+
| How many rows does the table city have :  | count(*) |
+-------------------------------------------+----------+
| How many rows does the table city have :  |     4074 |
+-------------------------------------------+----------+
1 row in set (0.002 sec)

+----------------------------------------------+----------+
| How many rows does the table country have :  | count(*) |
+----------------------------------------------+----------+
| How many rows does the table country have :  |      239 |
+----------------------------------------------+----------+
1 row in set (0.003 sec)

+------------------------------------------------------+----------+
| How many rows does the table countrylanguage have :  | count(*) |
+------------------------------------------------------+----------+
| How many rows does the table countrylanguage have :  |      984 |
+------------------------------------------------------+----------+
1 row in set (0.004 sec)

Query OK, 0 rows affected (0.004 sec)

1.2 在Oracle数据库(19.3.0)中创建一个存储过程,用于计算各个表的行数:
    由于在上一篇博客中已数据从MySQL数据库导出并导入Oracle数据库,所以我们可以在Oracle数据库作如下对比实验,创建功能相同的存储过程,用于计算并显示各个表的行数

[oracle@classroom ~]$ sqlplus /nolog

SQL*Plus: Release 19.0.0.0.0 - Production on Mon Apr 6 20:05:29 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

SQL> conn hr/oracle_4U
Connected.
SQL> create or replace procedure row_count
  2  is
  3   v_city_count number;
  4   v_country_count number;
  5   v_countrylan_count number;
  6  begin
  7   select count(*) into v_city_count from city;
  8   select count(*) into v_country_count from country;
  9   select count(*) into v_countrylan_count from countrylanguage;
 10   dbms_output.put_line('How many rows does the table city have: '||v_city_count||'.');
 11   dbms_output.put_line('How many rows does the table country have: '||v_country_count||'.');
 12   dbms_output.put_line('How many rows does the table countrylanguage have: '||v_countrylan_count||'.');
 13  end;
 14  /

Procedure created.

    使用“dbms_metadata.get_ddl”函数来查看Oracle数据库里的该存储过程的DDL信息:

SQL> conn / as sysdba         
Connected.
SQL> set long 1000
SQL> set pagesize 1000
SQL> select dbms_metadata.get_ddl(OBJECT_TYPE=>'PROCEDURE',NAME=>'ROW_COUNT',SCHEMA=>'HR') from dual;

DBMS_METADATA.GET_DDL(OBJECT_TYPE=>'PROCEDURE',NAME=>'ROW_COUNT',SCHEMA=>'HR')
--------------------------------------------------------------------------------

  CREATE OR REPLACE EDITIONABLE PROCEDURE "HR"."ROW_COUNT"
is
 v_city_count number;
 v_country_count number;
 v_countrylan_count number;
begin
 select count(*) into v_city_count from city;
 select count(*) into v_country_count from country;
 select count(*) into v_countrylan_count from countrylanguage;
 dbms_output.put_line('How many rows does the table city have: '||v_city_count||
'.');
 dbms_output.put_line('How many rows does the table country have: '||v_country_c
ount||'.');
 dbms_output.put_line('How many rows does the table countrylanguage have: '||v_c
ountrylan_count||'.');
end;

    使用dba_objects来查看Oracle数据库里的该存储过程的状态信息:

SQL> conn / as sysdba   
Connected.
SQL> desc dba_objects;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                              VARCHAR2(128)
 OBJECT_NAME                                        VARCHAR2(128)
 SUBOBJECT_NAME                                     VARCHAR2(128)
 OBJECT_ID                                          NUMBER
 DATA_OBJECT_ID                                     NUMBER
 OBJECT_TYPE                                        VARCHAR2(23)
 CREATED                                            DATE
 LAST_DDL_TIME                                      DATE
 TIMESTAMP                                          VARCHAR2(19)
 STATUS                                             VARCHAR2(7)
 TEMPORARY                                          VARCHAR2(1)
 GENERATED                                          VARCHAR2(1)
 SECONDARY                                          VARCHAR2(1)
 NAMESPACE                                          NUMBER
 EDITION_NAME                                       VARCHAR2(128)
 SHARING                                            VARCHAR2(18)
 EDITIONABLE                                        VARCHAR2(1)
 ORACLE_MAINTAINED                                  VARCHAR2(1)
 APPLICATION                                        VARCHAR2(1)
 DEFAULT_COLLATION                                  VARCHAR2(100)
 DUPLICATED                                         VARCHAR2(1)
 SHARDED                                            VARCHAR2(1)
 CREATED_APPID                                      NUMBER
 CREATED_VSNID                                      NUMBER
 MODIFIED_APPID                                     NUMBER
 MODIFIED_VSNID                                     NUMBER
         
SQL> col owner format a5 trunc
SQL> col object_name format a20 trunc
SQL> select OWNER,OBJECT_NAME,OBJECT_TYPE,CREATED,LAST_DDL_TIME,STATUS from dba_objects where owner='HR' and object_name='ROW_COUNT';

OWNER OBJECT_NAME          OBJECT_TYPE             CREATED   LAST_DDL_ STATUS
----- -------------------- ----------------------- --------- --------- -------
HR    ROW_COUNT            PROCEDURE               07-APR-26 07-APR-26 VALID

    使用dba_source来查看Oracle数据库里的该存储过程的数据字典信息:

SQL> conn / as sysdba
Connected.
SQL> desc dba_source;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                              VARCHAR2(128)
 NAME                                               VARCHAR2(128)
 TYPE                                               VARCHAR2(12)
 LINE                                               NUMBER
 TEXT                                               VARCHAR2(4000)
 ORIGIN_CON_ID                                      NUMBER

SQL> col text format a120 trunc
SQL> col name format a20 trunc
SQL> set linesize 200 
SQL> set pagesize 1000
SQL> select * from dba_source where owner='HR' and name='ROW_COUNT';

OWNER NAME                 TYPE               LINE TEXT                                                                                                                     ORIGIN_CON_ID
----- -------------------- ------------ ---------- ------------------------------------------------------------------------------------------------------------------------ -------------
HR    ROW_COUNT            PROCEDURE             1 procedure row_count                                                                                                  0
HR    ROW_COUNT            PROCEDURE             2 is                                                                                                                   0
HR    ROW_COUNT            PROCEDURE             3  v_city_count number;                                                                                                0
HR    ROW_COUNT            PROCEDURE             4  v_country_count number;                                                                                             0
HR    ROW_COUNT            PROCEDURE             5  v_countrylan_count number;                                                                                          0
HR    ROW_COUNT            PROCEDURE             6 begin                                                                                                                0
HR    ROW_COUNT            PROCEDURE             7  select count(*) into v_city_count from city;                                                                        0
HR    ROW_COUNT            PROCEDURE             8  select count(*) into v_country_count from country;                                                                  0
HR    ROW_COUNT            PROCEDURE             9  select count(*) into v_countrylan_count from countrylanguage;                                                       0
HR    ROW_COUNT            PROCEDURE            10  dbms_output.put_line('How many rows does the table city have: '||v_city_count||'.');                                0
HR    ROW_COUNT            PROCEDURE            11  dbms_output.put_line('How many rows does the table country have: '||v_country_count||'.');                          0
HR    ROW_COUNT            PROCEDURE            12  dbms_output.put_line('How many rows does the table countrylanguage have: '||v_countrylan_count||'.');               0
HR    ROW_COUNT            PROCEDURE            13 end;                                                                                                                 0

13 rows selected.

    在Oracle数据库里执行该存储过程:

SQL> conn hr/oracle_4U
Connected.
SQL> set serveroutput on
SQL> exec row_count;
How many rows does the table city have: 4074.
How many rows does the table country have: 239.
How many rows does the table countrylanguage have: 984.

PL/SQL procedure successfully completed.

2. MySQL数据库的函数与Oracle数据库的函数作对比

2.1 在MySQL 9.6数据库中创建一个函数,根据输入的:理财投资金额(单位为万元)、投资天数和收益金额(单位为元)这3个参数来计算理财投资的年化收益率:
    如果投资1万元,假设年化收益率为1%,投资天数为1年,那么收益金额就是100元,则每天的收益就是100/365=0.2739元。根据以上计算,可以得出任意理财投资金额(单位为万元)、任意投资天数的理财投资的年化收益率为:
理财投资的年化收益率=收益金额(单位为元)/(理财投资金额(单位为万元)*投资天数*0.2739)
    注意:与Oracle数据库的PL/SQL语法要求不同,向MySQL函数传递的参数必须指定精度。

[root@station95 ~]# mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 9.6.0 MySQL Community Server - GPL

Copyright (c) 2000, 2026, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use world_y;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> delimiter //
mysql> create function annualized_rate (p_amount float(10,2),p_days int,p_return float(8,2))
    -> returns float(4,2)
    -> no sql
    -> begin
    ->  declare v_annualized_rate float(4,2) default 0;
    ->  set v_annualized_rate=p_return/(p_amount*p_days*0.2739);
    ->  return v_annualized_rate;
    -> end//
Query OK, 0 rows affected, 4 warnings (0.003 sec)

mysql> delimiter ;

    使用“show create function”命令来查看MySQL数据库里的该函数的DDL信息:

mysql> show create function annualized_rate\G
*************************** 1. row ***************************
            Function: annualized_rate
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
     Create Function: CREATE DEFINER=`root`@`localhost` FUNCTION `annualized_rate`(p_amount float(10,2),p_days int,p_return float(8,2)) RETURNS float(4,2)
    NO SQL
begin
 declare v_annualized_rate float(4,2) default 0;
 set v_annualized_rate=p_return/(p_amount*p_days*0.2739);
 return v_annualized_rate;
end
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
  Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.001 sec)

    使用“show function status like”命令来查看MySQL数据库里的该函数的状态信息:

mysql> show function status like 'annualized_rate'\G
*************************** 1. row ***************************
                  Db: world_y
                Name: annualized_rate
                Type: FUNCTION
            Language: SQL
             Definer: root@localhost
            Modified: 2026-04-07 04:21:16
             Created: 2026-04-07 04:21:16
       Security_type: DEFINER
             Comment: 
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
  Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.004 sec)

    在information_schema方案下,查看MySQL数据库里的该函数的数据字典信息

mysql> use information_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from routines where specific_name='annualized_rate'\G
*************************** 1. row ***************************
           SPECIFIC_NAME: annualized_rate
         ROUTINE_CATALOG: def
          ROUTINE_SCHEMA: world_y
            ROUTINE_NAME: annualized_rate
            ROUTINE_TYPE: FUNCTION
               DATA_TYPE: float
CHARACTER_MAXIMUM_LENGTH: NULL
  CHARACTER_OCTET_LENGTH: NULL
       NUMERIC_PRECISION: 4
           NUMERIC_SCALE: 2
      DATETIME_PRECISION: NULL
      CHARACTER_SET_NAME: NULL
          COLLATION_NAME: NULL
          DTD_IDENTIFIER: float(4,2)
            ROUTINE_BODY: SQL
      ROUTINE_DEFINITION: begin
 declare v_annualized_rate float(4,2) default 0;
 set v_annualized_rate=p_return/(p_amount*p_days*0.2739);
 return v_annualized_rate;
end
           EXTERNAL_NAME: NULL
       EXTERNAL_LANGUAGE: SQL
         PARAMETER_STYLE: SQL
        IS_DETERMINISTIC: NO
         SQL_DATA_ACCESS: NO SQL
                SQL_PATH: NULL
           SECURITY_TYPE: DEFINER
                 CREATED: 2026-04-07 04:21:16
            LAST_ALTERED: 2026-04-07 04:21:16
                SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
         ROUTINE_COMMENT: 
                 DEFINER: root@localhost
    CHARACTER_SET_CLIENT: utf8mb4
    COLLATION_CONNECTION: utf8mb4_0900_ai_ci
      DATABASE_COLLATION: utf8mb4_0900_ai_ci
1 row in set (0.004 sec)

    在MySQL数据库里该执行函数
    投资150万5千元,投资天数为180天,收益金额为25703.56元,计算年化收益率:

mysql> use world_y;
Database changed
mysql> select annualized_rate(150.5,180,25703.56);
+-------------------------------------+
| annualized_rate(150.5,180,25703.56) |
+-------------------------------------+
|                                3.46 |
+-------------------------------------+
1 row in set (0.002 sec)

    在当前的投资市场中,年化收益率为3.46是一个不错的收益率。
2.2 在Oracle数据库(19.3.0)中创建一个函数,根据输入的:理财投资金额(单位为万元)、投资天数和收益金额(单位为元)这3个参数来计算理财投资的年化收益率:
    由于在上一篇博客中已数据从MySQL数据库导出并导入Oracle数据库,所以我们可以在Oracle数据库作如下对比实验,创建功能相同的函数
    注意:Oracle数据库的PL/SQL语法要求:向函数传递的任何参数都不能指定精度。
   
[oracle@classroom ~]$ sqlplus /nolog

SQL*Plus: Release 19.0.0.0.0 - Production on Tue Apr 7 18:46:13 2026
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

SQL> conn hr/oracle_4U
Connected.
SQL> create or replace function annualized_rate(p_amount float, p_days int, p_return float)
  2  return float
  3  is
  4   v_annualized_rate float default 0;
  5  begin
  6   v_annualized_rate := round(p_return/(p_amount*p_days*0.2739),2);
  7   return v_annualized_rate;
  8  end;
  9  /

Function created.

    使用“dbms_metadata.get_ddl”函数来查看Oracle数据库里的该函数的DDL信息:

SQL> conn / as sysdba 
Connected.
SQL> set long 1000
SQL> set pagesize 1000
SQL> select dbms_metadata.get_ddl(OBJECT_TYPE=>'FUNCTION',NAME=>'ANNUALIZED_RATE',SCHEMA=>'HR') from dual;

DBMS_METADATA.GET_DDL(OBJECT_TYPE=>'FUNCTION',NAME=>'ANNUALIZED_RATE',SCHEMA=>'H
--------------------------------------------------------------------------------

  CREATE OR REPLACE EDITIONABLE FUNCTION "HR"."ANNUALIZED_RATE" (p_amount float,
 p_days int, p_return float)
return float
is
 v_annualized_rate float default 0;
begin
 v_annualized_rate := round(p_return/(p_amount*p_days*0.2739),2);
 return v_annualized_rate;
end;

    使用dba_objects来查看Oracle数据库里的该函数的状态信息:

SQL> conn / as sysdba
Connected.
SQL> desc dba_objects; 
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                              VARCHAR2(128)
 OBJECT_NAME                                        VARCHAR2(128)
 SUBOBJECT_NAME                                     VARCHAR2(128)
 OBJECT_ID                                          NUMBER
 DATA_OBJECT_ID                                     NUMBER
 OBJECT_TYPE                                        VARCHAR2(23)
 CREATED                                            DATE
 LAST_DDL_TIME                                      DATE
 TIMESTAMP                                          VARCHAR2(19)
 STATUS                                             VARCHAR2(7)
 TEMPORARY                                          VARCHAR2(1)
 GENERATED                                          VARCHAR2(1)
 SECONDARY                                          VARCHAR2(1)
 NAMESPACE                                          NUMBER
 EDITION_NAME                                       VARCHAR2(128)
 SHARING                                            VARCHAR2(18)
 EDITIONABLE                                        VARCHAR2(1)
 ORACLE_MAINTAINED                                  VARCHAR2(1)
 APPLICATION                                        VARCHAR2(1)
 DEFAULT_COLLATION                                  VARCHAR2(100)
 DUPLICATED                                         VARCHAR2(1)
 SHARDED                                            VARCHAR2(1)
 CREATED_APPID                                      NUMBER
 CREATED_VSNID                                      NUMBER
 MODIFIED_APPID                                     NUMBER
 MODIFIED_VSNID                                     NUMBER

SQL> col owner format a5 trunc
SQL> col object_name format a20 trunc
SQL> select OWNER,OBJECT_NAME,OBJECT_TYPE,CREATED,LAST_DDL_TIME,STATUS from dba_objects where owner='HR' and object_name='ANNUALIZED_RATE';

OWNER OBJECT_NAME          OBJECT_TYPE             CREATED   LAST_DDL_ STATUS
----- -------------------- ----------------------- --------- --------- -------
HR    ANNUALIZED_RATE      FUNCTION                07-APR-26 07-APR-26 VALID

    使用dba_source来查看Oracle数据库里的该函数的数据字典信息:

SQL> conn / as sysdba
Connected.
SQL> desc dba_source;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                              VARCHAR2(128)
 NAME                                               VARCHAR2(128)
 TYPE                                               VARCHAR2(12)
 LINE                                               NUMBER
 TEXT                                               VARCHAR2(4000)
 ORIGIN_CON_ID                                      NUMBER

SQL> col text format a120 trunc
SQL> col name format a20 trunc
SQL> set linesize 200 
SQL> set pagesize 1000
SQL> select * from dba_source where owner='HR' and name='ANNUALIZED_RATE';
OWNER NAME                 TYPE               LINE TEXT                                                                                                             ORIGIN_CON_ID
----- -------------------- ------------ ---------- ------------------------------------------------------------------------------------------------------------------------ -------------
HR    ANNUALIZED_RATE      FUNCTION              1 function annualized_rate(p_amount float, p_days int, p_return float)                                         0
HR    ANNUALIZED_RATE      FUNCTION              2 return float                                                                                                 0
HR    ANNUALIZED_RATE      FUNCTION              3 is                                                                                                           0
HR    ANNUALIZED_RATE      FUNCTION              4  v_annualized_rate float default 0;                                                                          0
HR    ANNUALIZED_RATE      FUNCTION              5 begin                                                                                                        0
HR    ANNUALIZED_RATE      FUNCTION              6  v_annualized_rate := round(p_return/(p_amount*p_days*0.2739),2);                                                     0
HR    ANNUALIZED_RATE      FUNCTION              7  return v_annualized_rate;                                                                                   0
HR    ANNUALIZED_RATE      FUNCTION              8 end;                                                                                                         0

8 rows selected.

    在Oracle数据库里执行该函数:

SQL> conn hr/oracle_4U
Connected.
SQL>  select annualized_rate(150.5,180,25703.56) from dual;

ANNUALIZED_RATE(150.5,180,25703.56)
-----------------------------------
                               3.46

3. MySQL数据库编程语言的异常处理与Oracle数据库编程语言的异常处理作对比

    MySQL数据库编程语言所预定义异常较少(SQLEXCEPTION, SQLWARNING, NOT FOUND),比如:无NO_DATA_FOUND,需用FOUND_ROWS()或游标的NOT FOUND handler。异常处理器需用DECLARE HANDLER声明。不支持自定义异常。
    Oracle数据库编程语言所预定义异常丰富(NO_DATA_FOUND, TOO_MANY_ROWS等)。异常处理器无需声明,直接使用。支持自定义异常,需RAISE抛出。
3.1 MySQL数据库编程语言的异常处理:
3.1.1 SQL退出异常处理:

mysql>  delimiter //
mysql> create procedure test()
    -> begin
    ->   DECLARE EXIT HANDLER FOR SQLEXCEPTION
    ->   begin
    ->    rollback;
    ->    SELECT  'SQL Error occurred.' as message;
    ->   end;
    ->  select a from city;
    -> end//
Query OK, 0 rows affected (0.003 sec)

mysql> delimiter ;
mysql> call test;
+---------------------+
| message             |
+---------------------+
| SQL Error occurred. |
+---------------------+
1 row in set (0.001 sec)

Query OK, 0 rows affected (0.001 sec)

3.1.2 游标的NOT FOUND handler异常处理:
   如果不进行游标的NOT FOUND handler异常处理,那么游标取数据到最后一行后,由于取不到下一行数据,将会报错:

mysql> delimiter //
mysql> CREATE PROCEDURE test2()
    -> BEGIN
    ->    declare v_id int;
    ->    DECLARE city_cursor CURSOR FOR
    ->         SELECT id  FROM city;
    ->        
    -> OPEN city_cursor;
    ->     SET @done = 0;    
    ->     read_loop: LOOP
    ->      
    ->         FETCH  city_cursor INTO v_id;       
    ->         IF @done = 1 THEN
    ->             LEAVE read_loop;  
    ->         END IF;    
    -> 
    -> END LOOP read_loop;   
    ->         CLOSE city_cursor;     
    ->   end//
Query OK, 0 rows affected (0.003 sec)

mysql> delimiter ;
mysql> call test2();
ERROR 1329 (02000): No data - zero rows fetched, selected, or processed

    进行游标的NOT FOUND handler异常处理:

mysql> drop procedure test2;
Query OK, 0 rows affected (0.003 sec)

mysql> delimiter //
mysql> CREATE PROCEDURE test2()
    -> BEGIN
    ->    declare v_id int;
    ->    DECLARE city_cursor CURSOR FOR
    ->         SELECT id  FROM city;
    ->   DECLARE CONTINUE HANDLER FOR NOT FOUND
    ->     SET @done = 1;      
    -> OPEN city_cursor;
    ->     SET @done = 0;    
    ->     read_loop: LOOP
    ->      
    ->         FETCH  city_cursor INTO v_id;       
    ->         IF @done = 1 THEN
    ->             LEAVE read_loop;  
    ->         END IF;    
    -> 
    -> END LOOP read_loop;   
    ->         CLOSE city_cursor;     
    ->   end//
Query OK, 0 rows affected (0.002 sec)

mysql> delimiter ;
mysql> call test2();
Query OK, 0 rows affected (0.025 sec)

3.2 Oracle数据库编程语言的异常处理:
3.2.1 Oracle数据库可以自定义异常处理器,还可以把自定义的异常处理器与Oracle的标准错误号相联系:

SQL> create or replace procedure test3
  2  is
  3      v_id city.id%type;  
  4      v_a int;
  5      cursor city_cursor is SELECT id  FROM city;
  6      e_custom EXCEPTION; 
  7      PRAGMA EXCEPTION_INIT( e_custom,-06502);
  8  BEGIN
  9      select 'a' into v_a from dual; 
 10       open city_cursor;
 11       loop
 12        fetch city_cursor into v_id;
 13        exit when city_cursor%notfound;
 14       end loop;
 15       close city_cursor;
 16   EXCEPTION
 17       when  e_custom then
 18           dbms_output.put_line('Character to number conversion error.');
 19  END;
 20  /

Procedure created.

SQL> set serveroutput on
SQL> exec test3;
Character to number conversion error.

PL/SQL procedure successfully completed.

3.2.2 Oracle数据库有很多预定义的异常处理场景:

SQL> create or replace procedure test4
  2  is
  3   v_a int;
  4  begin
  5   select  id into v_a from city;
  6  exception
  7    when too_many_rows then
  8       dbms_output.put_line('More rows.');
  9  end;
 10  /

Procedure created.

SQL> set serveroutput on
SQL> exec test4;
More rows.

PL/SQL procedure successfully completed.

3.2.3 Oracle数据库甚至还可以在执行体中直接抛出异常,错误号可以-20000到-20999之间自己定义:

SQL> create or replace procedure test5
  2  is
  3  begin
  4   raise_application_error(-20004,'No error is error.');
  5  end;
  6  /

Procedure created.

SQL> exec test5;
BEGIN test5; END;

*
ERROR at line 1:
ORA-20004: No error is error.
ORA-06512: at "HR.TEST5", line 4
ORA-06512: at line 1

   如果要处理这个异常,就使用3.2.1中的方法:

SQL> create or replace procedure test6
  2  is
  3    e_custom EXCEPTION; 
  4    PRAGMA EXCEPTION_INIT( e_custom,-20004);
  5  begin
  6   raise_application_error(-20004,'No error is error.');
  7  exception
  8    when  e_custom then
  9        dbms_output.put_line('Not an error.');
 10  end;
 11  /

Procedure created.

SQL> set serveroutput on
SQL> exec test6;
Not an error.

PL/SQL procedure successfully completed.



路过

雷人

握手

鲜花

鸡蛋

QQ|手机版|Bo's Oracle Station   

GMT+8, 2026-4-8 19:05 , Processed in 0.084774 second(s), 30 queries .

返回顶部