前言
做过数据库迁移的同学都知道,从Oracle迁到别的库,最怕的就是"语法不兼容、函数不支持、存储过程得重写"这老三样。KingbaseES在Oracle兼容这件事上下了不少功夫,28种数据类型、200+内置函数、21个PL/SQL内置包全给拿下了。今天咱们就掰开了揉碎了,从数据类型、内置函数、PL/SQL三个维度,把两者的兼容情况彻底说清楚。
文章目录
- 前言
-
- 一、先看全局:兼容能力到底覆盖了多广
- 二、数据类型:28种全覆盖,零改动迁移
-
- [2.1 字符类型:VARCHAR2、NVARCHAR2都能接](#2.1 字符类型:VARCHAR2、NVARCHAR2都能接)
- [2.2 数值类型:从INT到BINARY_DOUBLE全覆盖](#2.2 数值类型:从INT到BINARY_DOUBLE全覆盖)
- [2.3 日期时间类型:6种时间类型一个不落](#2.3 日期时间类型:6种时间类型一个不落)
- [2.4 大对象与特殊类型:BLOB、CLOB、ROWID都能接](#2.4 大对象与特殊类型:BLOB、CLOB、ROWID都能接)
- [2.5 半结构化类型:JSON和XML也不在话下](#2.5 半结构化类型:JSON和XML也不在话下)
- 三、内置函数:200+函数逐类对比
-
- [3.1 数字函数:26个,全部兼容](#3.1 数字函数:26个,全部兼容)
- [3.2 字符函数:19个全部兼容,正则也能用](#3.2 字符函数:19个全部兼容,正则也能用)
- [3.3 日期时间函数:22个全覆盖](#3.3 日期时间函数:22个全覆盖)
- [3.4 转换函数:34个类型互转全部支持](#3.4 转换函数:34个类型互转全部支持)
- [3.5 聚集函数:30个,包含高级统计](#3.5 聚集函数:30个,包含高级统计)
- [3.6 分析函数:31个窗口函数全部支持](#3.6 分析函数:31个窗口函数全部支持)
- [3.7 JSON和XML函数:34个半结构化处理函数](#3.7 JSON和XML函数:34个半结构化处理函数)
- [3.8 差异速查表:哪些函数有坑](#3.8 差异速查表:哪些函数有坑)
- 四、PL/SQL兼容性:迁移中最硬的骨头
-
- [4.1 数据类型:标量、RECORD、集合全支持](#4.1 数据类型:标量、RECORD、集合全支持)
- [4.2 控制语句:IF、CASE、三种LOOP全支持](#4.2 控制语句:IF、CASE、三种LOOP全支持)
- [4.3 存储过程与函数:IN/OUT/IN OUT参数、重载、递归都支持](#4.3 存储过程与函数:IN/OUT/IN OUT参数、重载、递归都支持)
- [4.4 游标:隐式、显式、REF CURSOR三种都支持](#4.4 游标:隐式、显式、REF CURSOR三种都支持)
- [4.5 动态SQL:EXECUTE IMMEDIATE和DBMS_SQL两种方式](#4.5 动态SQL:EXECUTE IMMEDIATE和DBMS_SQL两种方式)
- [4.6 触发器:行级、语句级、INSTEAD OF全覆盖](#4.6 触发器:行级、语句级、INSTEAD OF全覆盖)
- [4.7 包(Package):自定义包+21个内置包](#4.7 包(Package):自定义包+21个内置包)
- [4.8 异常处理:预定义、自定义、传播、捕获全支持](#4.8 异常处理:预定义、自定义、传播、捕获全支持)
- 五、SQL语法兼容性:伪列、MERGE、DBLink都支持
-
- [5.1 伪列:ROWNUM、ROWID、SEQUENCE、层次查询](#5.1 伪列:ROWNUM、ROWID、SEQUENCE、层次查询)
- [5.2 高级SQL操作](#5.2 高级SQL操作)
- 六、系统视图兼容性:70+视图,运维习惯不用改
-
- [6.1 静态数据字典视图](#6.1 静态数据字典视图)
- [6.2 动态性能视图](#6.2 动态性能视图)
- 七、实操:Oracle到KingbaseES的迁移步骤
-
- [7.1 导出导入的实操命令](#7.1 导出导入的实操命令)
- [7.2 导入后的验证脚本](#7.2 导入后的验证脚本)
- 八、写在最后
一、先看全局:兼容能力到底覆盖了多广
很多同学第一反应是:"兼容是不是就是支持个SELECT、INSERT?"真不是。KingbaseES走的是内核级别的兼容路线------不是说在外面套一层翻译壳子,而是在数据库引擎内部就把Oracle的那套语法、类型、函数体系给实现了。
目前Oracle常用能力的兼容率做到了100%,不光是基本的SQL语法和数据类型,连PL/SQL内置包、DBLink跨库访问、物化视图、分区操作这些高级特性也都支持。
下面这张图可以直观地看到兼容的全貌:
┌──────────────────────────────────────────────────────────────┐
│ Oracle 兼容能力全景图 │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ 基础能力层 │ 高级能力层 │ 工具链层 │ 接口层 │
├──────────────┼──────────────┼──────────────┼────────────────┤
│ SQL语法 │ ROWID │ sys_dump │ JDBC │
│ 28种数据类型 │ BFILE │ sys_restore │ ODBC │
│ 200+内置函数 │ DBLink │ exp/imp │ OCI │
│ 70+系统视图 │ 物化视图 │ sys_bulkload │ OCCI │
│ 完整PL/SQL │ 分区操作 │ ksql │ Pro*C │
│ 伪列/表达式 │ 21个内置包 │ │ DCI │
│ 条件/常量 │ XML/JSON │ │ │
└──────────────┴──────────────┴──────────────┴────────────────┘
来,直接上硬数据:
| 兼容维度 | 子项数量 | 兼容状态 |
|---|---|---|
| 数据类型 | 28种 | 全部兼容 |
| 数字函数 | 26个 | 全部兼容 |
| 字符函数 | 19个 | 全部兼容 |
| 日期时间函数 | 22个 | 全部兼容 |
| 转换函数 | 34个 | 全部兼容 |
| 聚集函数 | 30个 | 全部兼容 |
| 分析函数 | 31个 | 全部兼容 |
| XML函数 | 24个 | 全部兼容 |
| JSON函数 | 10个 | 全部兼容 |
| PL/SQL特性 | 9大类 | 全部支持 |
| 内置包 | 21个 | 全部支持 |
| 系统视图 | 70+ | 全部兼容 |
光看表格可能还不够直观,下面咱们一个维度一个维度地展开,配上代码,看完你就知道迁移的时候该注意什么了。
二、数据类型:28种全覆盖,零改动迁移
数据类型是迁移的第一关------如果连字段类型都对不上,后面的代码全白搭。好消息是,Oracle的28种数据类型在KingbaseES里全部都能直接用,建表语句从Oracle搬到KingbaseES,一行都不用改。
2.1 字符类型:VARCHAR2、NVARCHAR2都能接
字符类型是最常用的,Oracle有CHAR、VARCHAR2、NCHAR、NVARCHAR2、LONG这几位老熟人。KingbaseES对它们的支持是完整的,行为也一致。
sql
-- 这段建表语句,在Oracle和KingbaseES上跑出来的结果一模一样
CREATE TABLE employees (
emp_id NUMBER(10),
emp_name VARCHAR2(100),
emp_code CHAR(10),
region_name NVARCHAR2(50),
memo NCHAR(200)
);
来看完整的兼容情况:
| Oracle类型 | 兼容状态 | 说明 |
|---|---|---|
| CHAR | 兼容 | 定长字符,最大2000字节 |
| VARCHAR2 | 兼容 | 变长字符,最大32767字节 |
| NCHAR | 兼容 | Unicode定长字符 |
| NVARCHAR2 | 兼容 | Unicode变长字符 |
| LONG | 兼容 | 兼容旧版LONG类型 |
2.2 数值类型:从INT到BINARY_DOUBLE全覆盖
数值类型这块同样没问题,NUMBER、FLOAT、BINARY_FLOAT、BINARY_DOUBLE、INT、SMALLINT全都支持。
sql
-- 在PL/SQL块中使用各种数值类型,行为与Oracle一致
DECLARE
v_int INT := 100;
v_num NUMBER(10,2) := 12345.67;
v_float FLOAT := 3.14;
v_binary_f BINARY_FLOAT := 2.5f;
v_binary_d BINARY_DOUBLE := 3.14159d;
BEGIN
DBMS_OUTPUT.PUT_LINE('INT: ' || v_int);
DBMS_OUTPUT.PUT_LINE('NUMBER: ' || v_num);
DBMS_OUTPUT.PUT_LINE('FLOAT: ' || v_float);
END;
/
| Oracle类型 | 兼容状态 | 说明 |
|---|---|---|
| NUMBER(p,s) | 兼容 | 精确数值,最大精度38位 |
| FLOAT | 兼容 | 浮点数 |
| BINARY_FLOAT | 兼容 | 32位单精度浮点 |
| BINARY_DOUBLE | 兼容 | 64位双精度浮点 |
| INT / INTEGER | 兼容 | 整数类型 |
| SMALLINT | 兼容 | 小整数类型 |
2.3 日期时间类型:6种时间类型一个不落
日期时间类型对业务系统特别关键------订单时间、发货时间、账期计算,哪个都马虎不得。Oracle的6种日期时间类型,KingbaseES全部兼容。
sql
-- 一张表把Oracle所有日期时间类型都涵盖进来
CREATE TABLE order_events (
order_id NUMBER(10),
order_date DATE, -- 精确到秒
created_at TIMESTAMP, -- 带微秒
shipped_at TIMESTAMP WITH TIME ZONE, -- 带时区
local_time TIMESTAMP WITH LOCAL TIME ZONE, -- 本地时区
warranty INTERVAL YEAR TO MONTH, -- 年月间隔
delivery_time INTERVAL DAY TO SECOND -- 天秒间隔
);
-- 直接插入数据,语法跟Oracle一模一样
INSERT INTO order_events VALUES (
1001,
DATE '2025-03-15',
TIMESTAMP '2025-03-15 10:30:00.123456',
TIMESTAMP '2025-03-15 10:30:00 +08:00',
TIMESTAMP '2025-03-15 10:30:00',
INTERVAL '2-6' YEAR TO MONTH,
INTERVAL '5 12:30:00' DAY TO SECOND
);
| Oracle类型 | 兼容状态 | 说明 |
|---|---|---|
| DATE | 兼容 | 日期+时间(精确到秒) |
| TIMESTAMP | 兼容 | 带微秒的时间戳 |
| TIMESTAMP WITH TIME ZONE | 兼容 | 带时区的时间戳 |
| TIMESTAMP WITH LOCAL TIME ZONE | 兼容 | 本地时区时间戳 |
| INTERVAL YEAR TO MONTH | 兼容 | 年月间隔 |
| INTERVAL DAY TO SECOND | 兼容 | 天秒间隔 |
2.4 大对象与特殊类型:BLOB、CLOB、ROWID都能接
LOB类型和特殊类型在复杂业务里用得很多。BLOB存文件、CLOB存长文本、BFILE引用外部文件、ROWID标识行------这些在KingbaseES里全部支持。
sql
CREATE TABLE media_assets (
asset_id NUMBER(10) PRIMARY KEY,
doc_content CLOB, -- 大文本
file_data BLOB, -- 二进制大对象
unicode_doc NCLOB, -- Unicode大文本
raw_data RAW(500), -- 原始二进制
file_ptr BFILE, -- 外部文件引用
row_id ROWID, -- 物理行标识符
urow_id UROWID -- 通用行标识符
);
| Oracle类型 | 兼容状态 | 说明 |
|---|---|---|
| BLOB | 兼容 | 二进制大对象,最大1GB |
| CLOB | 兼容 | 字符大对象 |
| NCLOB | 兼容 | Unicode字符大对象 |
| BFILE | 兼容 | 外部二进制文件引用 |
| RAW | 兼容 | 原始二进制数据 |
| LONG RAW | 兼容 | 旧版二进制类型 |
| ROWID | 兼容 | 物理行标识符 |
| UROWID | 兼容 | 通用行标识符 |
2.5 半结构化类型:JSON和XML也不在话下
现在的系统,不处理点JSON和XML数据都不好意思出门。KingbaseES对Oracle的JSON和XML类型也做到了完整兼容。
sql
-- JSON类型:建表、插入、查询一条龙
CREATE TABLE json_data (
id NUMBER PRIMARY KEY,
info JSON,
payload JSONB
);
INSERT INTO json_data VALUES (
1,
'{"name": "张三", "age": 30, "skills": ["Java", "SQL"]}',
'{"verified": true}'
);
-- 用JSON_VALUE提取字段,跟Oracle写法完全一致
SELECT JSON_VALUE(info, '$.name') AS name FROM json_data;
-- XML类型也一样
CREATE TABLE xml_docs (
id NUMBER PRIMARY KEY,
content XMLType
);
INSERT INTO xml_docs VALUES (
1,
XMLType('<book><title>数据库迁移指南</title><author>技术团队</author></book>')
);
三、内置函数:200+函数逐类对比
函数兼容性直接决定了你的SQL要不要改。KingbaseES兼容了Oracle 200+个内置函数,涵盖数字、字符、日期、转换、聚合、分析、JSON、XML八大类。咱们一类一类来看。
3.1 数字函数:26个,全部兼容
数字函数是最基础的一类。ABS取绝对值、CEIL向上取整、ROUND四舍五入......这26个函数在KingbaseES里的行为跟Oracle完全一致。
sql
-- 这段代码在Oracle和KingbaseES上跑出来的结果一模一样
SELECT
ABS(-15) AS abs_val, -- 15
CEIL(4.3) AS ceil_val, -- 5
FLOOR(4.7) AS floor_val, -- 4
ROUND(3.1415, 2) AS round_val, -- 3.14
TRUNC(3.1415, 2) AS trunc_val, -- 3.14
MOD(10, 3) AS mod_val, -- 1
POWER(2, 10) AS power_val, -- 1024
SQRT(144) AS sqrt_val, -- 12
SIGN(-5) AS sign_val, -- -1
LN(100) AS ln_val, -- 4.605...
LOG(10, 100) AS log_val -- 2
FROM dual;
这里提一句:KingbaseES支持Oracle的
dual虚拟表。你的SQL里要是用了FROM dual,直接搬过来就行,不用改。
3.2 字符函数:19个全部兼容,正则也能用
字符处理函数在业务代码里出现频率极高------截取、拼接、查找、替换,一个都不能少。
sql
-- 日常字符串操作
SELECT
UPPER('hello world') AS upper_str, -- HELLO WORLD
LOWER('HELLO WORLD') AS lower_str, -- hello world
INITCAP('hello world') AS initcap_str, -- Hello World
SUBSTR('KingbaseES', 1, 4) AS substr_val, -- King
INSTR('KingbaseES', 'base') AS instr_val, -- 5
LPAD('123', 10, '0') AS lpad_val, -- 0000000123
RPAD('abc', 10, '*') AS rpad_val, -- abc*******
TRIM(' hello ') AS trim_val, -- hello
REPLACE('Jelly', 'J', 'K') AS replace_val, -- Kelly
CONCAT('Hello', ' World') AS concat_val -- Hello World
FROM dual;
-- 正则表达式函数也完全兼容
SELECT
REGEXP_SUBSTR('abc123def456', '[0-9]+') AS first_num,
REGEXP_REPLACE('Hello 123 World 456', '[0-9]', 'X') AS replaced
FROM dual;
有个小坑要注意 :
CHR函数,Oracle允许传0,KingbaseES不行。如果你代码里用了CHR(0),迁移的时候得单独处理。
3.3 日期时间函数:22个全覆盖
日期函数是业务系统里用得最勤的------算账期、排班次、统计月报,全靠它们。
sql
-- 常用日期函数一览
SELECT
SYSDATE AS now,
CURRENT_DATE AS cur_date,
ADD_MONTHS(DATE '2025-01-15', 3) AS after_3m, -- 3个月后的日期
LAST_DAY(DATE '2025-02-10') AS feb_last, -- 该月最后一天
NEXT_DAY(DATE '2025-03-15', 'MONDAY') AS next_mon, -- 下一个周一
MONTHS_BETWEEN(DATE '2025-06-15', DATE '2025-01-15') AS months_diff,
TRUNC(SYSDATE, 'MONTH') AS month_start, -- 月初
ROUND(SYSDATE, 'DAY') AS rounded_day -- 四舍五入到最近周日
FROM dual;
-- 格式化输出也完全兼容Oracle的写法
SELECT
TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') AS formatted_date,
TO_CHAR(SYSDATE, 'YYYY"年"MM"月"DD"日"') AS cn_date
FROM dual;
注意 :
CURRENT_TIMESTAMP在精度上可能和Oracle有细微差异。如果业务对时间戳精度敏感,建议显式指定精度参数,比如CURRENT_TIMESTAMP(6)。
3.4 转换函数:34个类型互转全部支持
从字符串转数字、从数字转日期、从RAW转十六进制......Oracle的34个转换函数,KingbaseES全都兼容。
sql
-- 常用转换函数示例
SELECT
TO_NUMBER('123.45') AS num_val,
TO_DATE('2025-03-15', 'YYYY-MM-DD') AS date_val,
TO_TIMESTAMP('2025-03-15 10:30:00',
'YYYY-MM-DD HH24:MI:SS') AS ts_val,
TO_CHAR(12345.67, '999,999.99') AS formatted_num,
TO_CHAR(DATE '2025-03-15', 'YYYY-MM-DD') AS date_str,
CAST('123' AS NUMBER) AS cast_val
FROM dual;
-- NULL处理函数(迁移中最常用的一组)
SELECT
NVL(NULL, 'default') AS nvl_val, -- default
NVL2('value', 'yes', 'no') AS nvl2_val, -- yes
COALESCE(NULL, NULL, 'third') AS coal_val, -- third
NULLIF('same', 'same') AS nullif_val, -- NULL
DECODE(1, 1, '一', 2, '二', '其他') AS decode_val -- 一
FROM dual;
实际做迁移的时候,NVL和DECODE是用得最多的两个------很多老Oracle项目的SQL里到处都是。好消息是这两个函数行为完全一致,直接搬就行。
3.5 聚集函数:30个,包含高级统计
sql
-- 基础聚合:日常统计报表离不开这些
SELECT
department_id,
COUNT(*) AS emp_count,
AVG(salary) AS avg_salary,
SUM(salary) AS total_salary,
MAX(salary) AS max_salary,
MIN(salary) AS min_salary,
STDDEV(salary) AS salary_stddev,
VARIANCE(salary) AS salary_var
FROM employees
GROUP BY department_id;
-- LISTAGG:把多行拼成一条字符串,做报表特别好用
SELECT
department_id,
LISTAGG(last_name, ', ') WITHIN GROUP (ORDER BY hire_date) AS name_list
FROM employees
GROUP BY department_id;
3.6 分析函数:31个窗口函数全部支持
分析函数(也叫窗口函数)是OLAP场景的杀手锏。ROW_NUMBER排名、LAG/LEAD取前后行、SUM累加------这31个函数在KingbaseES里全部兼容。
sql
-- 三种排名函数的区别一目了然
SELECT
emp_name,
department_id,
salary,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num,
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank_val,
DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dense_rank_val
FROM employees;
-- LAG/LEAD:环比增长、同比对比就靠它们
SELECT
order_date,
amount,
LAG(amount, 1) OVER (ORDER BY order_date) AS prev_amount,
LEAD(amount, 1) OVER (ORDER BY order_date) AS next_amount,
amount - LAG(amount, 1) OVER (ORDER BY order_date) AS diff
FROM daily_sales;
-- 累计窗口:算累计工资、累计销售额
SELECT
emp_name,
department_id,
salary,
SUM(salary) OVER (
PARTITION BY department_id
ORDER BY hire_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_salary
FROM employees;
3.7 JSON和XML函数:34个半结构化处理函数
现在越来越多的业务场景需要处理JSON和XML数据。KingbaseES兼容了Oracle全部10个JSON函数和24个XML函数。
sql
-- JSON函数:构建、查询、提取
SELECT
JSON_OBJECT('name' VALUE '张三', 'age' VALUE 30) AS json_obj,
JSON_ARRAY(1, 2, 3, 4, 5) AS json_arr,
JSON_VALUE('{"a": 1, "b": 2}', '$.a') AS json_val,
JSON_QUERY('{"a": [1,2,3]}', '$.a') AS json_query;
-- XML函数:从关系数据生成XML
SELECT
XMLELEMENT(NAME "emp",
XMLFOREST(emp_name AS "name", salary AS "salary")
) AS xml_result
FROM employees
WHERE department_id = 10;
-- JSON_TABLE:把JSON数据展开成关系表来查
SELECT jt.*
FROM json_data,
JSON_TABLE(info, '$'
COLUMNS (
name VARCHAR2(100) PATH '$.name',
age NUMBER PATH '$.age'
)
) jt;
3.8 差异速查表:哪些函数有坑
虽然绝大多数函数是100%兼容的,但也有少数几个存在细微差异。做迁移的时候务必重点关注这几个:
| 函数名 | 差异在哪 | 怎么处理 |
|---|---|---|
| CHR | KingbaseES不允许输入参数为0 | 检查业务是否用了CHR(0),有的话需要改写 |
| CURRENT_TIMESTAMP | 时间戳精度可能不同 | 显式指定精度参数,如CURRENT_TIMESTAMP(6) |
| CONVERT | 第二、三个参数顺序跟Oracle反的 | 交换src_charset和dest_charset参数 |
| NULLIF | KingbaseES允许参数为NULL | 这个差异通常是有利的,不用特殊处理 |
| REGEXP_REPLACE | match_param部分含义不同 | 涉及正则的场景务必逐条测试 |
| REGEXP_COUNT | 某些数据类型(如time)结果可能不同 | 有time类型参与正则的要重点测试 |
| REGEXP_INSTR | 同上 | 同上 |
这几个差异点不多,但每个都可能在实际项目中踩到。建议迁移的时候把涉及这几个函数的SQL单独拎出来测试。
四、PL/SQL兼容性:迁移中最硬的骨头
说到Oracle迁移,PL/SQL才是真正的重头戏。存储过程、函数、触发器、包------这些都是业务逻辑的载体,如果不能兼容,基本等于重写。KingbaseES在这块做得比较彻底,从数据类型到控制语句,从游标到动态SQL,从异常处理到内置包,全给兼容了。
4.1 数据类型:标量、RECORD、集合全支持
PL/SQL里的数据类型比SQL层更丰富------除了基本的NUMBER、VARCHAR2,还有PLS_INTEGER、RECORD记录类型、嵌套表、关联数组、VARRAY集合类型。KingbaseES全部支持。
sql
DECLARE
-- 基本标量类型
v_count PLS_INTEGER := 0;
v_idx BINARY_INTEGER := 1;
v_name VARCHAR2(100) := 'KingbaseES';
-- SUBTYPE:基于已有类型定义子类型
SUBTYPE t_salary IS NUMBER(10, 2);
v_salary t_salary := 15000.50;
-- RECORD:自定义记录类型
TYPE emp_record IS RECORD (
emp_id NUMBER(10),
emp_name VARCHAR2(100),
salary NUMBER(10, 2)
);
v_emp emp_record;
-- 嵌套表(NESTED TABLE)
TYPE num_list IS TABLE OF NUMBER;
v_numbers num_list := num_list(10, 20, 30, 40, 50);
-- 关联数组(INDEX BY)
TYPE name_map IS TABLE OF VARCHAR2(100) INDEX BY PLS_INTEGER;
v_names name_map;
-- VARRAY(变长数组)
TYPE int_array IS VARRAY(10) OF INTEGER;
v_arr int_array := int_array(1, 2, 3);
BEGIN
v_names(1) := 'Alice';
v_names(2) := 'Bob';
v_names(3) := 'Charlie';
DBMS_OUTPUT.PUT_LINE('Count: ' || v_numbers.COUNT);
DBMS_OUTPUT.PUT_LINE('First: ' || v_numbers.FIRST);
DBMS_OUTPUT.PUT_LINE('Last: ' || v_numbers.LAST);
END;
/
4.2 控制语句:IF、CASE、三种LOOP全支持
控制语句是PL/SQL的骨架。IF条件判断、CASE分支选择、三种LOOP循环------KingbaseES全部兼容,写法跟Oracle完全一致。
sql
DECLARE
v_score NUMBER := 85;
v_grade VARCHAR2(2);
BEGIN
-- IF条件控制
IF v_score >= 90 THEN
v_grade := 'A';
ELSIF v_score >= 80 THEN
v_grade := 'B';
ELSIF v_score >= 70 THEN
v_grade := 'C';
ELSE
v_grade := 'D';
END IF;
-- CASE表达式(更简洁的写法)
v_grade := CASE
WHEN v_score >= 90 THEN 'A'
WHEN v_score >= 80 THEN 'B'
WHEN v_score >= 70 THEN 'C'
ELSE 'D'
END;
-- 三种循环方式
-- 1. 基本LOOP + EXIT WHEN
DECLARE i NUMBER := 0; BEGIN
LOOP
i := i + 1;
EXIT WHEN i > 5;
END LOOP;
END;
-- 2. FOR LOOP(最常用)
FOR j IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE('j = ' || j);
END LOOP;
-- 3. WHILE LOOP
DECLARE k NUMBER := 1; BEGIN
WHILE k <= 5 LOOP
k := k + 1;
END LOOP;
END;
END;
/
4.3 存储过程与函数:IN/OUT/IN OUT参数、重载、递归都支持
存储过程和函数是业务逻辑的核心载体。KingbaseES支持三种参数模式(IN、OUT、IN OUT),支持过程重载和递归调用,参数个数上限达到65536个。
sql
-- 创建存储过程:转账场景,带OUT参数返回状态
CREATE OR REPLACE PROCEDURE transfer_funds(
p_from_id IN NUMBER,
p_to_id IN NUMBER,
p_amount IN NUMBER,
p_status OUT VARCHAR2
) AS
v_balance NUMBER;
BEGIN
-- 先查余额
SELECT balance INTO v_balance
FROM accounts WHERE account_id = p_from_id;
IF v_balance < p_amount THEN
p_status := 'INSUFFICIENT_FUNDS';
RETURN;
END IF;
-- 扣款和入账
UPDATE accounts SET balance = balance - p_amount
WHERE account_id = p_from_id;
UPDATE accounts SET balance = balance + p_amount
WHERE account_id = p_to_id;
COMMIT;
p_status := 'SUCCESS';
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
p_status := 'ERROR: ' || SQLERRM;
END transfer_funds;
/
-- 创建函数:计算奖金,带异常处理
CREATE OR REPLACE FUNCTION calc_bonus(
p_salary NUMBER,
p_dept_id NUMBER
) RETURN NUMBER AS
v_rate NUMBER;
BEGIN
SELECT bonus_rate INTO v_rate
FROM dept_bonus WHERE dept_id = p_dept_id;
RETURN p_salary * v_rate;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN p_salary * 0.1; -- 没配置奖金比例就默认10%
END calc_bonus;
/
-- 调用存储过程
DECLARE
v_status VARCHAR2(100);
BEGIN
transfer_funds(1001, 1002, 5000, v_status);
DBMS_OUTPUT.PUT_LINE('转账结果: ' || v_status);
END;
/
4.4 游标:隐式、显式、REF CURSOR三种都支持
游标在PL/SQL里用得太多了------遍历数据、逐行处理、返回结果集。KingbaseES完整支持Oracle的三种游标模式。
sql
DECLARE
-- 显式游标
CURSOR emp_cur IS
SELECT emp_name, salary FROM employees WHERE department_id = 10;
-- 带参数的游标
CURSOR dept_emp_cur(p_dept_id NUMBER) IS
SELECT emp_name, salary FROM employees WHERE department_id = p_dept_id;
-- REF CURSOR(游标变量,最灵活的方式)
TYPE ref_cursor IS REF CURSOR;
v_ref_cur ref_cursor;
v_name VARCHAR2(100);
v_sal NUMBER;
BEGIN
-- 方式一:FOR循环遍历显式游标(最简洁)
FOR emp_rec IN emp_cur LOOP
DBMS_OUTPUT.PUT_LINE(emp_rec.emp_name || ': ' || emp_rec.salary);
END LOOP;
-- 方式二:OPEN/FETCH/CLOSE(手动控制,更灵活)
OPEN dept_emp_cur(20);
LOOP
FETCH dept_emp_cur INTO v_name, v_sal;
EXIT WHEN dept_emp_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name || ': ' || v_sal);
END LOOP;
CLOSE dept_emp_cur;
-- 方式三:REF CURSOR(动态绑定SQL)
OPEN v_ref_cur FOR SELECT emp_name, salary FROM employees WHERE salary > 10000;
LOOP
FETCH v_ref_cur INTO v_name, v_sal;
EXIT WHEN v_ref_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name || ': ' || v_sal);
END LOOP;
CLOSE v_ref_cur;
-- 隐式游标属性:拿影响行数
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 10;
DBMS_OUTPUT.PUT_LINE('更新了 ' || SQL%ROWCOUNT || ' 行');
END;
/
4.5 动态SQL:EXECUTE IMMEDIATE和DBMS_SQL两种方式
动态SQL在业务里用得很多------表名是运行时决定的、SQL语句是拼接出来的、返回列不确定的。Oracle提供了两种方式来处理,KingbaseES都支持。
方式一:EXECUTE IMMEDIATE(大多数场景够用)
sql
DECLARE
v_table_name VARCHAR2(100) := 'employees';
v_count NUMBER;
v_sql VARCHAR2(4000);
BEGIN
-- 动态查询
v_sql := 'SELECT COUNT(*) FROM ' || v_table_name;
EXECUTE IMMEDIATE v_sql INTO v_count;
DBMS_OUTPUT.PUT_LINE('总行数: ' || v_count);
-- 动态DDL
EXECUTE IMMEDIATE 'CREATE TABLE temp_log (id NUMBER, msg VARCHAR2(200))';
-- 带绑定变量的DML(防SQL注入,推荐写法)
v_sql := 'UPDATE employees SET salary = salary * :factor WHERE department_id = :dept';
EXECUTE IMMEDIATE v_sql USING 1.15, 10;
-- 带RETURNING子句的动态SQL
DECLARE
v_new_sal NUMBER;
BEGIN
EXECUTE IMMEDIATE
'UPDATE employees SET salary = :sal WHERE emp_id = :id RETURNING salary INTO :ret'
USING 20000, 1001
RETURNING INTO v_new_sal;
DBMS_OUTPUT.PUT_LINE('新工资: ' || v_new_sal);
END;
END;
/
方式二:DBMS_SQL包(列数和类型在编译时不确定的场景)
sql
DECLARE
v_cursor INTEGER;
v_sql VARCHAR2(4000) := 'SELECT emp_name, salary FROM employees WHERE department_id = :1';
v_name VARCHAR2(100);
v_sal NUMBER;
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor, v_sql, DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(v_cursor, ':1', 10);
DBMS_SQL.DEFINE_COLUMN(v_cursor, 1, v_name, 100);
DBMS_SQL.DEFINE_COLUMN(v_cursor, 2, v_sal);
DBMS_SQL.EXECUTE(v_cursor);
LOOP
EXIT WHEN DBMS_SQL.FETCH_ROWS(v_cursor) = 0;
DBMS_SQL.COLUMN_VALUE(v_cursor, 1, v_name);
DBMS_SQL.COLUMN_VALUE(v_cursor, 2, v_sal);
DBMS_OUTPUT.PUT_LINE(v_name || ': ' || v_sal);
END LOOP;
DBMS_SQL.CLOSE_CURSOR(v_cursor);
END;
/
4.6 触发器:行级、语句级、INSTEAD OF全覆盖
触发器在Oracle项目里很常见------审计日志、数据校验、视图更新。KingbaseES支持行级和语句级两种粒度,BEFORE、AFTER、INSTEAD OF三种时机。
sql
-- 行级BEFORE触发器:薪资变更审计
CREATE OR REPLACE TRIGGER trg_audit_salary
BEFORE UPDATE OF salary ON employees
FOR EACH ROW
BEGIN
IF :NEW.salary != :OLD.salary THEN
INSERT INTO salary_audit(emp_id, old_sal, new_sal, change_time)
VALUES(:NEW.emp_id, :OLD.salary, :NEW.salary, SYSDATE);
END IF;
END;
/
-- 语句级AFTER触发器:批量操作后汇总
CREATE OR REPLACE TRIGGER trg_log_bulk_update
AFTER UPDATE ON employees
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM salary_audit
WHERE change_time >= TRUNC(SYSDATE);
DBMS_OUTPUT.PUT_LINE('今日变更记录数: ' || v_count);
END;
/
-- INSTEAD OF触发器:让只读视图也能UPDATE
CREATE OR REPLACE VIEW emp_dept_view AS
SELECT e.emp_id, e.emp_name, e.salary, d.dept_name
FROM employees e JOIN departments d ON e.department_id = d.dept_id;
CREATE OR REPLACE TRIGGER trg_emp_dept_view
INSTEAD OF UPDATE ON emp_dept_view
FOR EACH ROW
BEGIN
UPDATE employees SET
emp_name = :NEW.emp_name,
salary = :NEW.salary
WHERE emp_id = :NEW.emp_id;
END;
/
-- 启用/禁用触发器
ALTER TRIGGER trg_audit_salary DISABLE; -- 禁用
ALTER TRIGGER trg_audit_salary ENABLE; -- 启用
4.7 包(Package):自定义包+21个内置包
包(Package)是Oracle PL/SQL最重要的组织单元------把相关的过程、函数、变量、类型封装到一起,对外提供清晰的接口。KingbaseES不仅支持自定义包,还内置了Oracle的21个常用包。
先来看一个完整的自定义包示例:
sql
-- 包规范(Specification):定义对外接口
CREATE OR REPLACE PACKAGE pkg_emp_mgmt AS
MAX_SALARY CONSTANT NUMBER := 999999.99;
DEFAULT_DEPT CONSTANT NUMBER := 10;
TYPE emp_cursor IS REF CURSOR;
g_total_employees NUMBER;
PROCEDURE add_employee(
p_name VARCHAR2, p_salary NUMBER, p_dept_id NUMBER
);
-- 支持函数重载:同名不同参
FUNCTION get_emp_count RETURN NUMBER;
FUNCTION get_emp_count(p_dept_id NUMBER) RETURN NUMBER;
PROCEDURE get_employees(p_dept_id NUMBER, p_cur OUT emp_cursor);
END pkg_emp_mgmt;
/
-- 包体(Body):实现具体逻辑
CREATE OR REPLACE PACKAGE BODY pkg_emp_mgmt AS
PROCEDURE add_employee(
p_name VARCHAR2, p_salary NUMBER, p_dept_id NUMBER
) AS
BEGIN
IF p_salary > MAX_SALARY THEN
RAISE_APPLICATION_ERROR(-20001, '薪资超出上限');
END IF;
INSERT INTO employees(emp_name, salary, department_id)
VALUES(p_name, p_salary, p_dept_id);
g_total_employees := g_total_employees + 1;
END add_employee;
FUNCTION get_emp_count RETURN NUMBER AS
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM employees;
RETURN v_count;
END get_emp_count;
FUNCTION get_emp_count(p_dept_id NUMBER) RETURN NUMBER AS
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count
FROM employees WHERE department_id = p_dept_id;
RETURN v_count;
END get_emp_count;
PROCEDURE get_employees(p_dept_id NUMBER, p_cur OUT emp_cursor) AS
BEGIN
OPEN p_cur FOR
SELECT emp_name, salary FROM employees
WHERE department_id = p_dept_id ORDER BY salary DESC;
END get_employees;
BEGIN
-- 包初始化块(首次调用时自动执行)
SELECT COUNT(*) INTO g_total_employees FROM employees;
END pkg_emp_mgmt;
/
-- 调用包里的过程和函数
DECLARE
v_count NUMBER;
v_cur pkg_emp_mgmt.emp_cursor;
v_name VARCHAR2(100);
v_sal NUMBER;
BEGIN
pkg_emp_mgmt.add_employee('赵六', 12000, 20);
v_count := pkg_emp_mgmt.get_emp_count; -- 全部员工数
DBMS_OUTPUT.PUT_LINE('总人数: ' || v_count);
v_count := pkg_emp_mgmt.get_emp_count(20); -- 20号部门人数
DBMS_OUTPUT.PUT_LINE('部门人数: ' || v_count);
pkg_emp_mgmt.get_employees(20, v_cur);
LOOP
FETCH v_cur INTO v_name, v_sal;
EXIT WHEN v_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name || ': ' || v_sal);
END LOOP;
CLOSE v_cur;
END;
/
21个内置包一览:
| 包名 | 干什么用的 | 包名 | 干什么用的 |
|---|---|---|---|
| DBMS_OUTPUT | 控制台打印输出 | DBMS_SQL | 动态SQL处理 |
| DBMS_LOB | 大对象读写操作 | DBMS_UTILITY | 通用工具函数 |
| DBMS_METADATA | 导出对象DDL | DBMS_XMLQUERY | XML查询处理 |
| DBMS_JOB | 定时任务调度 | UTL_HTTP | 发HTTP请求 |
| DBMS_SCHEDULER | 高级任务调度 | UTL_ENCODE | 编码解码 |
| DBMS_SESSION | 会话管理 | UTL_I18N | 国际化处理 |
| DBMS_RANDOM | 随机数生成 | UTL_RAW | RAW数据处理 |
| DBMS_DDL | 动态DDL操作 | OWA_UTIL | Web应用工具 |
| DBMS_MVIEW | 物化视图管理 | XMLTYPE | XML类型操作 |
| DBMS_OBFUSCATION_TOOLKIT | 数据加密 | DBMS_SQL_MONITOR | SQL执行监控 |
4.8 异常处理:预定义、自定义、传播、捕获全支持
异常处理保证了程序的健壮性。KingbaseES完整支持Oracle的异常处理体系。
sql
DECLARE
v_salary NUMBER;
-- 自定义异常
e_invalid_salary EXCEPTION;
PRAGMA EXCEPTION_INIT(e_invalid_salary, -20001);
BEGIN
SELECT salary INTO v_salary FROM employees WHERE emp_id = 9999;
EXCEPTION
-- Oracle预定义异常,直接捕获
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('查无此人');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('返回了多行数据');
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('违反唯一约束');
-- 自定义异常
WHEN e_invalid_salary THEN
DBMS_OUTPUT.PUT_LINE('薪资数据异常');
-- 兜底处理
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('错误码: ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('错误信息: ' || SQLERRM);
RAISE; -- 重新抛出,让上层处理
END;
/
五、SQL语法兼容性:伪列、MERGE、DBLink都支持
5.1 伪列:ROWNUM、ROWID、SEQUENCE、层次查询
Oracle的伪列用得很多,尤其是ROWNUM做分页、ROWID做快速定位、SEQUENCE生成序列号。KingbaseES对这四类伪列全部支持。
sql
-- ROWNUM:限制返回行数(最常用的分页方式)
SELECT emp_name, salary
FROM employees
WHERE ROWNUM <= 10
ORDER BY salary DESC;
-- ROWID:拿到行的物理地址
SELECT ROWID, emp_name FROM employees WHERE emp_id = 1001;
-- 序列伪列
CREATE SEQUENCE seq_emp START WITH 1 INCREMENT BY 1;
SELECT seq_emp.NEXTVAL FROM dual; -- 取下一个值
SELECT seq_emp.CURRVAL FROM dual; -- 取当前值
-- 层次查询伪列:组织架构树
SELECT
LEVEL,
LPAD(' ', 2 * (LEVEL - 1)) || emp_name AS org_chart,
CONNECT_BY_ISLEAF AS is_leaf
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;
5.2 高级SQL操作
除了基本CRUD,Oracle还有不少高级SQL操作,在迁移时经常碰到。KingbaseES对这些也都做了兼容:
sql
-- MERGE:有则更新,无则插入(数据同步常用)
MERGE INTO target_table t
USING source_table s
ON (t.id = s.id)
WHEN MATCHED THEN
UPDATE SET t.name = s.name, t.salary = s.salary
WHEN NOT MATCHED THEN
INSERT (id, name, salary) VALUES (s.id, s.name, s.salary);
-- INSERT ALL:一条SELECT插入多张表
INSERT ALL
INTO emp_archive (emp_id, emp_name, salary) VALUES (emp_id, emp_name, salary)
INTO emp_audit (emp_id, action, action_time) VALUES (emp_id, 'ARCHIVE', SYSDATE)
SELECT emp_id, emp_name, salary FROM employees WHERE status = 'INACTIVE';
-- INSERT RETURNING:插入后返回生成的ID
INSERT INTO employees(emp_name, salary)
VALUES ('新员工', 10000)
RETURNING emp_id INTO :new_id;
-- FLASHBACK查询:查看历史数据
SELECT * FROM employees AS OF TIMESTAMP (SYSDATE - INTERVAL '1' HOUR);
-- DBLink:跨库查询(支持同构和异构数据库)
SELECT * FROM employees@remote_oracle_db WHERE department_id = 10;
六、系统视图兼容性:70+视图,运维习惯不用改
Oracle DBA最熟悉的就是那一套数据字典视图------ALL_TABLES查表、DBA_USERS查用户、V S E S S I O N 查会话、 V SESSION查会话、V SESSION查会话、VLOCK查锁。KingbaseES把这些视图都兼容了,迁移后DBA可以继续用原来的运维习惯。
6.1 静态数据字典视图
sql
-- 查表结构(日常最常用的一组查询)
SELECT * FROM ALL_TABLES WHERE OWNER = 'HR';
SELECT * FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'EMPLOYEES';
SELECT * FROM ALL_INDEXES WHERE TABLE_NAME = 'EMPLOYEES';
SELECT * FROM ALL_CONSTRAINTS WHERE TABLE_NAME = 'EMPLOYEES';
-- 查代码和对象
SELECT * FROM ALL_SOURCE WHERE NAME = 'PKG_EMP_MGMT';
SELECT * FROM ALL_OBJECTS WHERE OBJECT_TYPE = 'PACKAGE';
SELECT * FROM ALL_TRIGGERS WHERE TABLE_NAME = 'EMPLOYEES';
SELECT * FROM ALL_VIEWS WHERE OWNER = 'HR';
SELECT * FROM ALL_SYNONYMS WHERE OWNER = 'HR';
SELECT * FROM ALL_SEQUENCES WHERE SEQUENCE_OWNER = 'HR';
SELECT * FROM ALL_DB_LINKS;
SELECT * FROM ALL_DIRECTORIES;
SELECT * FROM ALL_PART_TABLES WHERE OWNER = 'HR';
-- DBA级视图(需要更高权限)
SELECT * FROM DBA_USERS;
SELECT * FROM DBA_TABLESPACES;
SELECT * FROM DBA_FREE_SPACE;
SELECT * FROM DBA_ROLES;
SELECT * FROM DBA_ROLE_PRIVS WHERE GRANTEE = 'HR';
-- USER级视图(只看自己下的对象)
SELECT * FROM USER_TABLES;
SELECT * FROM USER_TAB_COMMENTS;
SELECT * FROM USER_CONSTRAINTS;
SELECT * FROM USER_SOURCE;
-- 回收站(删了还能找回来)
SELECT * FROM RECYCLEBIN;
6.2 动态性能视图
动态性能视图是排查问题的利器------看看谁在锁表、SQL执行多慢、系统负载多高。
sql
-- 实例和数据库基本信息
SELECT * FROM V$DATABASE;
SELECT * FROM V$INSTANCE;
-- 会话监控:谁连上来了,在执行什么
SELECT * FROM V$SESSION WHERE USERNAME = 'HR';
SELECT * FROM V$CONTEXT;
-- 锁诊断:谁锁了谁
SELECT * FROM V$LOCK;
SELECT * FROM V$LOCKED_OBJECT;
-- 性能统计:TPS、QPS等指标
SELECT * FROM V$SYSSTAT;
SELECT * FROM V$METRIC;
SELECT * FROM V$SYSMETRIC;
SELECT * FROM V$SYSMETRIC_HISTORY;
七、实操:Oracle到KingbaseES的迁移步骤
前面说了这么多兼容性,最后来点实在的------具体怎么迁。这里给出一套经过验证的六步迁移流程:
┌─────────────────────────────────────────────────────────────┐
│ 迁移六步走 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 第一步:评估 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ • 梳理Oracle版本和使用的特性清单 │ │
│ │ • 标记自定义类型、内置包、DBLink等高级特性 │ │
│ │ • 估算数据量、并发量和存储需求 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ 第二步:Schema迁移 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ • 用exp/imp工具导出Oracle Schema │ │
│ │ • 导入到KingbaseES(自动创建Oracle兼容模式) │ │
│ │ • 验证表结构、索引、约束、视图完整性 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ 第三步:PL/SQL迁移 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ • 存储过程、函数、包、触发器直接迁移 │ │
│ │ • 重点验证CHR(0)、CONVERT参数顺序、正则match_param │ │
│ │ • 测试DBMS_SQL、UTL_HTTP等内置包功能 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ 第四步:数据迁移 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ • 小数据量:exp/imp工具直接导 │ │
│ │ • 大数据量:用sys_bulkload高速加载 │ │
│ │ • 增量同步:配置逻辑复制 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ 第五步:应用适配 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ • JDBC/ODBC驱动替换,修改连接串 │ │
│ │ • OCI/OCCI/Pro*C接口适配 │ │
│ │ • 连接池参数调整 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ 第六步:验证上线 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ • 功能测试:业务全流程回归 │ │
│ │ • 性能测试:跑基准对比,用Hints做调优 │ │
│ │ • 建议双写或灰度上线,降低风险 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
7.1 导出导入的实操命令
Oracle端导出:
bash
# 用exp工具导出整个Schema
exp userid=hr/password@orcl OWNER=hr FILE=hr_backup.dmp LOG=hr_export.log
KingbaseES端导入:
bash
# 用imp工具导入,指定源用户和目标用户
imp userid=hr/password@kingbase FILE=hr_backup.dmp LOG=hr_import.log FROMUSER=hr TOUSER=hr
7.2 导入后的验证脚本
数据导入完了不能直接用,得先验证一下对象是否完整:
sql
-- 1. 对比表数量
SELECT COUNT(*) AS table_count FROM USER_TABLES;
-- 2. 对比存储过程和函数
SELECT NAME, TYPE, STATUS FROM USER_SOURCE
GROUP BY NAME, TYPE, STATUS
ORDER BY TYPE, NAME;
-- 3. 对比索引
SELECT INDEX_NAME, TABLE_NAME, STATUS FROM USER_INDEXES;
-- 4. 编译状态检查(重点!有INVALID的要排查)
SELECT OBJECT_NAME, OBJECT_TYPE, STATUS
FROM USER_OBJECTS
WHERE OBJECT_TYPE IN ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY', 'TRIGGER', 'VIEW')
ORDER BY STATUS, OBJECT_TYPE;
-- 5. 约束检查
SELECT CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE, STATUS
FROM USER_CONSTRAINTS
ORDER BY TABLE_NAME;
八、写在最后
最后用一张汇总表收个尾:
| 维度 | Oracle特性数量 | 兼容数量 | 兼容率 |
|---|---|---|---|
| 数据类型 | 28种 | 28种 | 100% |
| 数字函数 | 26个 | 26个 | 100% |
| 字符函数 | 19个 | 19个 | 100% |
| 日期时间函数 | 22个 | 22个 | 100% |
| 转换函数 | 34个 | 34个 | 100% |
| 聚集函数 | 30个 | 30个 | 100% |
| 分析函数 | 31个 | 31个 | 100% |
| JSON函数 | 10个 | 10个 | 100% |
| XML函数 | 24个 | 24个 | 100% |
| PL/SQL数据类型 | 4大类 | 4大类 | 100% |
| PL/SQL控制语句 | 3大类 | 3大类 | 100% |
| PL/SQL内置包 | 21个 | 21个 | 100% |
| 静态数据字典视图 | 70+ | 70+ | 100% |
| 动态性能视图 | 14个 | 14个 | 100% |
总结三句话:
-
绝大多数Oracle SQL和PL/SQL代码可以直接在KingbaseES上跑,不需要改写。数据类型、函数、存储过程、包、触发器------这些都做到了内核级兼容。
-
DBA的运维习惯可以平移 。ALL_TABLES、DBA_USERS、V S E S S I O N 、 V SESSION、V SESSION、VLOCK这些常用视图全都兼容,排查问题的套路不用变。
-
少数差异点要重点测试 。
CHR(0)、CONVERT参数顺序、正则表达式的match_param------这几个有细微差异,迁移时把它们涉及的SQL单独拎出来跑一遍就够了。