MySQL迁移Kingbase?一文告诉你有哪些语法兼容的坑
信创浪潮下,“MySQL & Oracle 兼容”是很多国产数据库厂商的标配宣传语,但兼容到什么程度、哪些细节会踩坑,往往只有真刀真枪的测过才知道。语法层面能跑通不代表业务逻辑能对齐,一个 REPLACE INTO 的主键校验差异,或者某些函数的执行结果不符合预期,都有可能在生产环境酿成事故。
本文以 Kingbase V9 为例,从多个维度验证其与 MySQL 在语法、函数等方面的实际表现,供各位朋友参考。
KingbaseES V009R001C010 MySQL 8.4.7 OS Linux 7.9语法兼容测试
SQL 语法兼容性
Kingbase V9 支持show create table的方式获取具体的创建表 SQL,
但不支持通过show index from语法获取索引信息,
仍然需要通过传统的\d方式。
SHOW DATABASES/SHOW TABLES/SHOW VARIABLES/SHOW ENGINES等语法都不支持,好在是 Kingbase 中获取这类信息的方式也足够简便,并不会影响到实际的使用体验。
数据类型兼容性
MySQL 中常见的数据类型在 KingbaseV9 中都能够支持,类似 ZEROFILL 等极少部分特殊用法不支持。
对于数值类型的边界和枚举类型中的未知字符都能够很好的识别。
DML 兼容性测试
DML 兼容性测试中主要验证 MySQL 中比较特殊的 DML 用法,绝大多数常用的场景都是兼容的。
例如,INSERT IGNORE INTO 测试,Kingbase 和 MySQL 的预期结果一致。
但是在 REPLACE INTO 中却表现出了不同的结果,
CREATE TABLE t_replace (id INT PRIMARY KEY, sku VARCHAR(20) UNIQUE, stock INT); INSERT INTO t_replace VALUES (1, 'A001', 100); REPLACE INTO t_replace VALUES (1, 'A001', 200); REPLACE INTO t_replace (sku, stock) VALUES ('A001', 50); SELECT * FROM t_replace;这段代码中的第二条 REPLACE INTO 语句,在 MySQL 中因为缺少主键没有能执行成功,但在 KingbaseV9 中却直接忽略主键更新了其中的数据,导致两者结果不一致。
这里不深究两者实现上的底层逻辑,因为是测试 Kingbase 对于 MySQL 的兼容性,那么我认为这个场景是不符合预期的。
如果说上述结果有可能只是设计逻辑上的差异,那么接下来的这段测试结果就更加让人摸不着头脑。
CREATE TABLE t_replace (id INT PRIMARY KEY, sku VARCHAR(20) UNIQUE, stock INT); INSERT INTO t_replace VALUES (0, 'A000', 100); INSERT INTO t_replace VALUES (1, 'A001', 100); INSERT INTO t_replace VALUES (2, 'A002', 100); REPLACE INTO t_replace VALUES (1, 'A001', 200); REPLACE INTO t_replace (sku, stock) VALUES ('A001', 50);没有详细分析具体的原因,MySQL 是 InnoDB 引擎,对于 update 采用的是原值更新的方式;而 Kingbase 的更新采用的是“版本追加”的方式,更新前后的数据都存在,通过事务 ID 来控制数据是否可见。猜测可能是由于版本控制上的异常导致这种情况的发生。
函数兼容测试
NULL 值处理测试
NULL 在数据库中是个特殊的数据,不同的数据库对于 NULL 值的处理常常有一定的差别。
测试结果倒是让我有些意外,本以为两者之间会有比较大的差异,测试下来发现 IFNULL、COALESCE、LOCATE、SUBSTRING_INDEX、GROUP_CONCAT 等函数在处理空值的行为都是一致的。
但是也遇到了一个特殊情况, 在使用 SELECT LPAD(‘ab’, 5, ‘’); 进行填充测试的时,MySQL 返回的是空值,而 Kingbase 则返回的是 ‘ab’ 字符。
比较奇怪的是,两者在处理 NULL 和 ‘’ 值的行为是一致的,为什么会出现上图的现象,有机会向 Kingbase 的开发人员讨教一二。
开窗函数兼容测试
开窗函数中大部分的语法都是兼容的,但是在 LAG 函数上有些差异。
CREATE TABLE t_sales ( id INT PRIMARY KEY, dept VARCHAR(10), emp_name VARCHAR(20), salary DECIMAL(10,2), sale_date DATE ); INSERT INTO t_sales VALUES (1, 'A', 'Tom', 8000, '2026-01-05'), (2, 'A', 'Jerry', 9000, '2026-01-10'), (3, 'A', 'Anna', 9000, '2026-01-15'), (4, 'B', 'Mike', 7000, '2026-01-08'), (5, 'B', 'Lucy', 7500, '2026-01-12'), (6, 'B', 'John', 6000, '2026-01-20'); -- 测试SQL1 SELECT dept, emp_name, sale_date, salary, LAG(salary, 1) OVER (PARTITION BY dept ORDER BY sale_date) AS prev_salary, LEAD(salary, 1) OVER (PARTITION BY dept ORDER BY sale_date) AS next_salary FROM t_sales; -- 测试SQL2 SELECT dept, emp_name, sale_date, salary, LAG(salary, 1, 0) OVER (PARTITION BY dept ORDER BY sale_date) AS prev_salary, LEAD(salary, 1, 0) OVER (PARTITION BY dept ORDER BY sale_date) AS next2_salary FROM t_sales;上述测试 SQL1 的预期结果是,按照 sale_date 排序后,每行取同 dept 内上一条/下一条的 salary,第一行的 prev_salary 和最后一行的 next_salary 均为 NULL。两库的测试结果均符合预期。
测试 SQL2 的预期结果是,如果 prev_salary 的前值和 next_salary 的后值为空的情况下,取其默认值。
这个测试中,MySQL 的结果是符合预期的;
但 Kingbase 直接报错了,从报错信息上看应该是参数上的错误。
从 Kingbase 官网上的文档看,LAG 函数是支持默认值参数的,而且根据官方给出的测试案例也是能够执行成功的,具体的文档参考 https://docs.kingbase.com.cn/cn/KES-V9R1C10/application/application-develop-guide/reference/mysql/functions_operators/window_functions#lag。为什么上述的测试 SQL2 会报错,也希望能够得到 Kingbase 开发人员的分析和解释。
JSON 函数测试
JSON 支持也是各大数据库重点宣传的功能之一,因此这里将 JSON 单列出来进行测试。
常规的插入和更新操作,Kingbase 和 MySQL 的预期行为一致,数据更新和插入过程中都会严格验证是否符合 JSON 语法,对于错误的数据是不允许插入的。
同时对于更高阶的嵌套路径访问和数组下标访问等场景,两个数据库表现出来的结果均符合预期。
CREATE TABLE t_json ( id INT PRIMARY KEY, data JSON ); INSERT INTO t_json VALUES (1, '{"name":"Tom","age":20,"tags":["a","b","c"],"addr":{"city":"Hangzhou","zip":"310000"}}'), (2, '{"name":"Jerry","age":25,"tags":["b","d"],"addr":{"city":"Shanghai","zip":"200000"}}'), (3, '{"name":"Anna","age":null,"tags":[],"addr":null}'); -- '->'和'-->'操作符 -- 预期: raw_name带双引号 "Tom",unquoted_name不带引号 Tom(->>相当于JSON_UNQUOTE(JSON_EXTRACT(...))) SELECT id, />和在 MySQL 中的执行结果是一致的。
![]()
写在最后
从总体测试结果来看,Kingbase V9 和 MySQL 的兼容性还是非常不错的,绝大部分的 SQL 语法和函数都不需要任何改造,可以直接使用。
不过对于一些特殊的用法,尤其是 MySQL 特有的“方言”的使用上,有些部分还是存在一些差异的。这也提醒我们数据库的迁移改造需要经过严格的测试验证,不要等到上线后发现问题再去调整。
最后声明一点,虽然在这篇文章里列举了不少兼容性上的差异,是因为对于预期结果一致的部分一笔带过了,仅仅将差异部分记录了下来。
