数据库迁移1 --- 将MySQL的数据迁移至金仓¶
一. 环境准备¶
| 主机名 | IP地址 | 服务器配置 | 数据库 | 端口 |
|---|---|---|---|---|
| Rocky7 | 192.168.100.106 | 2C2G | MySql | 3306 |
| OE2 | 192.168.100.110 | 4C4G | 金仓 | 54321 |
二. 在Rocky7上安装MySQL数据库¶
0. 事先准备¶
# 设置服务器名称
hostnamectl set-hostname Rocky7
# 关闭防火墙
systemctl disable --now firewalld
# 关闭selinux并永久生效
setenforce 0
# 永久生效(改配置文件,重启后仍生效)
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
# 安装依赖
dnf -y install cmake make gcc gcc-c++ bison libaio libaio-devel rpcgen ncurses ncurses-devel openssl openssl-devel wget
1. 安装MySQL¶
1) 从官网下载mysql-8.4.10¶
2) 解压到/usr/local¶
tar -xf mysql-8.4.10-linux-glibc2.28-x86_64.tar.xz -C /usr/local
mv /usr/local/mysql-8.4.10-linux-glibc2.28-x86_64 /usr/local/mysql
3) 创建mysql用户与同名组¶
4) 创建存放MySQL的数据和日志目录,然后修改目录权限¶
mkdir -p /data/mysql/{data,log}
chown -R mysql.mysql /data/mysql
chown -R mysql:mysql /usr/local/mysql/
5) 编写配置文件my.cnf¶
[client]
socket = /usr/local/mysql/mysql.sock
[mysqld]
server_id=10
port = 3306
user = mysql
character-set-server = utf8mb4
default_storage_engine = innodb
log_timestamps = SYSTEM
socket = /usr/local/mysql/mysql.sock
basedir = /usr/local/mysql
datadir = /data/mysql/data
pid-file = /usr/local/mysql/mysql.pid
log_error = /data/mysql/log/mysql-error.log
sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'
skip-name-resolve
6) 初始化数据库¶
/usr/local/mysql/bin/mysqld \
--initialize-insecure \
--user=mysql \
--basedir=/usr/local/mysql \
--datadir=/data/mysql/data
7) 查看验证¶
[root@Rocky7 ~]# ls /data/mysql/data
auto.cnf client-key.pem ibdata1 mysql.ibd public_key.pem undo_001
ca-key.pem '#ib_16384_0.dblwr' '#innodb_redo' mysql_upgrade_history server-cert.pem undo_002
ca.pem '#ib_16384_1.dblwr' '#innodb_temp' performance_schema server-key.pem
client-cert.pem ib_buffer_pool mysql private_key.pem sys
[root@Rocky7 ~]# ls /data/mysql/log
mysql-error.log
8) 写systemd启动服务¶
[Unit]
Description=MySQL Server
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
User=mysql
Group=mysql
TimeoutSec=300
PrivateTmp=true
[Install]
WantedBy=multi-user.target
9) 重载后启动服务,然后设置开机自动启动并验证¶
[root@Rocky7 ~]# ss -tuanp|grep mysql
tcp LISTEN 0 70 *:33060 *:* users:(("mysqld",pid=1443,fd=18))
tcp LISTEN 0 151 *:3306 *:* users:(("mysqld",pid=1443,fd=20))
[root@Rocky7 ~]# systemctl status mysqld
● mysqld.service - MySQL Server
Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; preset: disabled)
Active: active (running) since Tue 2026-09-15 08:39:46 CST; 3min 28s ago
Main PID: 1443 (mysqld)
Tasks: 34 (limit: 10844)
Memory: 434.5M
CPU: 2.219s
CGroup: /system.slice/mysqld.service
└─1443 /usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
9月 15 08:39:46 Rocky7 systemd[1]: Started MySQL Server.
10) 配置环境变量¶
echo "export PATH=/usr/local/mysql/bin:$PATH">>/etc/profile.d/mysql.sh
#配置环境变量
source /etc/profile #让环境变量生效
三. 在欧拉上安装金仓数据库¶
注: 国产数据库占用较大,建议改成4核4G
0. 环境准备¶
# 设置服务器名称
hostnamectl set-hostname OE2
# 关闭防火墙
systemctl disable --now firewalld
# 关闭selinux并永久生效
setenforce 0
# 永久生效(改配置文件,重启后仍生效)
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
# RemoveIPC=no
sed -i 's/^#RemoveIPC=no/RemoveIPC=no/' /etc/systemd/logind.conf
systemctl restart systemd-logind
# 修改文件句柄数
cat >> /etc/security/limits.conf <<'EOF'
kingbase soft nofile 65536
kingbase hard nofile 65536
kingbase soft nproc 65536
kingbase hard nproc 65536
EOF
# 修改内核参数
cat >> /etc/sysctl.conf <<'EOF'
vm.swappiness = 10
fs.aio-max-nr = 1048576
EOF
sysctl -p
1. 安装金仓¶
1) 下载数据库¶
官网的数据库需要输入个人信息,不能直接wget拉,这里我们就提前下好传过来了


[root@OE2 ~]# ls -lh
总计 176M
-rw-------. 1 root root 1.1K 9月14日 17:33 anaconda-ks.cfg
-rw-r--r--. 1 root root 176M 9月15日 09:17 KingbaseES-Server-V009R002-C016B0006.x86_64.rpm
# .rpm结尾的就是我们所需要的数据库
2) 安装¶
[root@OE2 ~]# ls
anaconda-ks.cfg KingbaseES-Server-V009R002-C016B0006.x86_64.rpm
[root@OE2 ~]# rpm -ivh KingbaseES-Server-V009R002-C016B0006.x86_64.rpm
Verifying... ################################# [100%]
准备中... ################################# [100%]
Locale zh_CN.UTF-8 exist.
block size is 8k.
正在升级/安装...
1:KingbaseES-Server-1:V009R002-C016################################# [100%]
数据库簇将使用本地化语言 "zh_CN.UTF-8"进行初始化.
属于此数据库系统的文件宿主为用户 "kingbase".
此用户也必须为服务器进程的宿主.
initdb: could not find suitable text search configuration for locale "zh_CN.UTF-8"
缺省的文本搜索配置将会被设置到"simple"
字符串的比较区分大小写.
禁止为数据页生成校验和.
创建目录 /opt/Kingbase/ES/Server/data ... 成功
正在创建子目录 ... 成功
选择动态共享内存实现 ......posix
选择默认最大连接数 (max_connections) ... 100
选择默认共享缓冲区大小 (shared_buffers) ... 128MB
选择默认时区...Asia/Shanghai
创建配置文件 ... 成功
开始设置加密设备
正在初始化加密设备...成功
开始初始化撤销子系统元数据
初始化撤销子系统元数据 ... 成功
正在运行自举脚本 ...成功
正在执行自举后初始化 ...成功
创建安全数据库...成功
加载安全数据库...成功
同步数据到磁盘...成功
成功。您现在可以用下面的命令开启数据库服务器:
/opt/Kingbase/ES/Server/bin/sys_ctl -D /opt/Kingbase/ES/Server/data -l 日志文件 start
Using the following command can start the Kingbase system service:
systemctl enable kingbased.service
systemctl start kingbased.service
2. 启动服务并设置开机自启动¶
验证
[root@OE2 ~]# systemctl status kingbased --no-pager | head -10
● kingbased.service - KingbaseES Service
Loaded: loaded (/usr/lib/systemd/system/kingbased.service; enabled; preset: disabled)
Active: active (running) since Tue 2026-09-15 09:26:52 CST; 58s ago
Process: 2863 ExecStart=/opt/Kingbase/ES/Server/bin/sys_ctl start -D /opt/Kingbase/ES/Server/data -l /opt/Kingbase/ES/Server/data/startup.log (code=exited, status=0/SUCCESS)
Main PID: 2865 (kingbase)
Tasks: 13 (limit: 21362)
Memory: 483.6M ()
CGroup: /system.slice/kingbased.service
├─2865 /opt/Kingbase/ES/Server/bin/kingbase -D /opt/Kingbase/ES/Server/data
├─2867 "kingbase: logger "
[root@OE2 ~]# ss -tlnp | grep 54321
LISTEN 0 324 0.0.0.0:54321 0.0.0.0:* users:(("kingbase",pid=2865,fd=17))
LISTEN 0 324 [::]:54321 [::]:* users:(("kingbase",pid=2865,fd=18))
3. 连接数据库¶
[root@OE2 ~]# su - kingbase
上一次登录: 2026年09月15日 星期二 09:17:57 CST
Welcome to 6.6.0-98.0.0.103.oe2403sp2.x86_64
System information as of time: 2026年 09月 15日 星期二 09:29:17 CST
System load: 0.00
Memory used: 9.8%
Swap used: 0%
Usage On: 8%
IP address: 192.168.100.110
Users online: 2
To run a command as administrator(user "root"),use "sudo <command>".
[kingbase@OE2 ~]$ /opt/Kingbase/ES/Server/bin/ksql -U system -l -p 54321
用户 system 的口令:12345678ab
数据库列表
名称 | 拥有者 | 字元编码 | 校对规则 | Ct
ype | 存取权限
-----------+--------+----------+-------------+------
-------+-------------------
kingbase | system | UTF8 | zh_CN.UTF-8 | zh_CN
.UTF-8 |
security | system | UTF8 | zh_CN.UTF-8 | zh_CN
.UTF-8 |
template0 | system | UTF8 | zh_CN.UTF-8 | zh_CN
.UTF-8 | =c/system +
| | | |
| system=CTc/system
template1 | system | UTF8 | zh_CN.UTF-8 | zh_CN
.UTF-8 | =c/system +
| | | |
| system=CTc/system
test | system | UTF8 | zh_CN.UTF-8 | zh_CN
.UTF-8 |
(5 行记录)
# 列出库之后进默认库:
[kingbase@OE2 ~]$ /opt/Kingbase/ES/Server/bin/ksql -U system -d test -p 54321
用户 system 的口令:
授权类型: 企业版.
输入 "help" 来获取帮助信息.
test=#
4. 设置数据库¶
-- 先改口令
test=# ALTER USER system WITH PASSWORD 'Kingbase@2026';
ALTER ROLE
-- 确认迁移相关事项
test=# SELECT version();
version
-------------------------
KingbaseES V009R002C016
(1 行记录)
test=# SHOW server_encoding;
server_encoding
-----------------
UTF8
(1 行记录)
test=# SELECT name, setting FROM sys_settings WHERE name LIKE '%mode%';
name | setting
------------------------+--------------------------------
archive_mode | always
copy_mode | kes
data_directory_mode | 0700
database_mode | oracle
force_parallel_mode | off
log_file_mode | 0600
pg_enable_param_mode | off
plan_cache_mode | auto
simple_plan_cache_mode | off
sql_mode | ONLY_FULL_GROUP_BY,ANSI_QUOTES
(10 行记录)
| 项目 | 结果 | 判断 |
|---|---|---|
| 版本 | KingbaseES V009R002C016 | ✅ 就是 V9R2 |
| server_encoding | UTF8 | ✅ 中文迁移的前提满足 |
| database_mode | oracle | ✅ 与预期一致(rpm 默认值) |
| 口令 | 已改成 Kingbase@2026 | ✅ 默认口令隐患已消除 |
5. 建目标库与用户¶
test=# -CREATE USER migadm WITH PASSWORD 'Mig@2026' LOGIN;
CREATE ROLE
test=# CREATE DATABASE shop OWNER migadm;
CREATE DATABASE
test=# \l
数据库列表
名称 | 拥有者 | 字元编码 | 校对规则 | Ctype | 存取权限
-----------+--------+----------+-------------+-------------+-------------------
kingbase | system | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 |
security | system | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 |
shop | migadm | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 |
template0 | system | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 | =c/system +
| | | | | system=CTc/system
template1 | system | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 | =c/system +
| | | | | system=CTc/system
test | system | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 |
(6 行记录)
6. 兼容性探针¶
# 写用于探测的文件
cat > /tmp/probe.sql <<'EOF'
-- ============================================================
-- MySQL → 金仓(KingbaseES V9R2) 兼容性探针 v2(合并修正版)
-- 用法:ksql -U system -d probe_db -p 54321 -f /tmp/probe.sql 2>&1 | tee /tmp/probe_result.txt
--
-- v2 相对 v1 的四处修正:
-- 1) 每个建表前加 DROP TABLE IF EXISTS → 可重复执行(v1 重跑报"关系已存在")
-- 2) 探针 6 改用【可空列】,并分两次测:默认参数 / 关闭 ora_input_emptystr_isnull 后
-- (v1 用 NOT NULL 列,插入被拦住,只能靠报错间接证明)
-- 3) 顺序重排:默认状态的测试全部前置,改参数的补救实验全部后置
-- (v1 里探针 9 关掉 sql_mode 后恢复失败,污染了后面 12a 的前提)
-- 4) 新增探针 20~24:内联 KEY / UNIQUE KEY / FOREIGN KEY、SET NAMES、
-- USING BTREE、DISABLE KEYS、INSERT 里的反引号(都是 mysqldump 常见写法)
-- ============================================================
\echo ''
\echo '########## 第 0 部分:基线快照 ##########'
SELECT version() AS 版本;
SHOW server_encoding;
SHOW sql_mode;
SHOW ora_input_emptystr_isnull;
\echo ''
\echo '########## 第 1 部分:默认状态下的语法兼容性 ##########'
\echo ''
\echo '===== 1. 反引号标识符 ====='
DROP TABLE IF EXISTS p1;
CREATE TABLE p1 (`id` int, `name` varchar(20));
\echo ''
\echo '===== 2. AUTO_INCREMENT ====='
DROP TABLE IF EXISTS p2;
CREATE TABLE p2 (id int AUTO_INCREMENT PRIMARY KEY, name varchar(20));
\echo ''
\echo '===== 3. 建表尾缀 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ====='
DROP TABLE IF EXISTS p3;
CREATE TABLE p3 (id int) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\echo ''
\echo '===== 4a. 类型 tinyint ====='
DROP TABLE IF EXISTS p4a;
CREATE TABLE p4a (a tinyint);
\echo ''
\echo '===== 4b. 类型 int unsigned ====='
DROP TABLE IF EXISTS p4b;
CREATE TABLE p4b (b int unsigned);
\echo ''
\echo '===== 4c. 类型 datetime ====='
DROP TABLE IF EXISTS p4c;
CREATE TABLE p4c (c datetime);
\echo ''
\echo '===== 4d. 类型 text ====='
DROP TABLE IF EXISTS p4d;
CREATE TABLE p4d (d text);
\echo ''
\echo '===== 4e. 类型 double(10,2) ====='
DROP TABLE IF EXISTS p4e;
CREATE TABLE p4e (e double(10,2));
\echo ''
\echo '===== 5. LIMIT offset,count(MySQL 逗号写法)====='
SELECT * FROM p2 LIMIT 0,10;
\echo ''
\echo '===== 6a. 空串写入【可空列】(默认 ora_input_emptystr_isnull)====='
DROP TABLE IF EXISTS p6;
CREATE TABLE p6 (id int, v varchar(10));
INSERT INTO p6 VALUES (1, '');
SELECT count(*) AS 总行数 FROM p6;
SELECT count(*) AS 空串匹配数 FROM p6 WHERE v = '';
SELECT count(*) AS IS_NULL匹配数 FROM p6 WHERE v IS NULL;
SELECT id, v IS NULL AS 是否为NULL FROM p6;
\echo ''
\echo '===== 7. GROUP BY 宽松写法(默认 sql_mode 严格性)====='
DROP TABLE IF EXISTS p7;
CREATE TABLE p7 (dept varchar(10), name varchar(10));
INSERT INTO p7 VALUES ('a','x'),('a','y');
SELECT dept, name FROM p7 GROUP BY dept;
\echo ''
\echo '===== 8. MySQL 常用函数 IFNULL / NOW / CONCAT ====='
SELECT IFNULL(NULL,'x') AS ifnull结果, NOW() AS now结果, CONCAT('a','b') AS concat结果;
\echo ''
\echo '===== 10. MySQL 专属命令 SHOW TABLES / DESC ====='
SHOW TABLES;
DESC p7;
\echo ''
\echo '===== 11. INSERT ... ON DUPLICATE KEY UPDATE ====='
DROP TABLE IF EXISTS p11;
CREATE TABLE p11 (id int PRIMARY KEY, v varchar(10));
INSERT INTO p11 VALUES (1,'a');
INSERT INTO p11 VALUES (1,'b') ON DUPLICATE KEY UPDATE v='b';
SELECT * FROM p11;
\echo ''
\echo '===== 12a. 双引号语义(默认 sql_mode:ANSI_QUOTES 开启态)====='
SELECT "name" FROM p7;
\echo ''
\echo '===== 13. DEFAULT CHARSET=utf8mb4 + 中文存取 ====='
DROP TABLE IF EXISTS p13;
CREATE TABLE p13 (v varchar(20)) DEFAULT CHARSET=utf8mb4;
INSERT INTO p13 VALUES ('中文测试');
SELECT * FROM p13;
\echo ''
\echo '===== 14. COLLATE 子句 ====='
DROP TABLE IF EXISTS p14;
CREATE TABLE p14 (v varchar(20) COLLATE utf8mb4_unicode_ci);
\echo ''
\echo '===== 15. 建表尾缀 ENGINE=InnoDB AUTO_INCREMENT=100 ====='
DROP TABLE IF EXISTS p15;
CREATE TABLE p15 (id int AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB AUTO_INCREMENT=100;
\echo ''
\echo '===== 16. LOCK TABLES / UNLOCK TABLES ====='
LOCK TABLES p7 WRITE;
UNLOCK TABLES;
\echo ''
\echo '===== 17. SET @@SESSION.SQL_MODE ====='
SET @@SESSION.SQL_MODE = 'ONLY_FULL_GROUP_BY';
\echo ''
\echo '===== 18. MySQL 条件注释 /*!40101 SET NAMES utf8mb4 */ ====='
/*!40101 SET NAMES utf8mb4 */;
\echo ''
\echo '===== 19. 反引号 + 库名限定 ====='
SELECT * FROM `p7`;
\echo ''
\echo '===== 20a. 内联 KEY(普通索引)====='
DROP TABLE IF EXISTS q20a;
CREATE TABLE q20a (id int, cat_id int, KEY idx_cat (cat_id));
\echo ''
\echo '===== 20b. 内联 UNIQUE KEY ====='
DROP TABLE IF EXISTS q20b;
CREATE TABLE q20b (id int, code varchar(10), UNIQUE KEY uk_code (code));
\echo ''
\echo '===== 20c. 内联 CONSTRAINT FOREIGN KEY ====='
DROP TABLE IF EXISTS q20c;
CREATE TABLE q20c (id int, cat_id int, CONSTRAINT fk_cat FOREIGN KEY (cat_id) REFERENCES p2 (id));
\echo ''
\echo '===== 20d. NOT NULL + AUTO_INCREMENT + PRIMARY KEY 组合 ====='
DROP TABLE IF EXISTS q20d;
CREATE TABLE q20d (id int NOT NULL AUTO_INCREMENT, name varchar(20), PRIMARY KEY (id));
\echo ''
\echo '===== 21. SET NAMES utf8mb4 ====='
SET NAMES utf8mb4;
\echo ''
\echo '===== 22. 索引 USING BTREE ====='
DROP TABLE IF EXISTS q22;
CREATE TABLE q22 (id int, v varchar(10), KEY idx_v (v) USING BTREE);
\echo ''
\echo '===== 23. ALTER TABLE DISABLE KEYS / ENABLE KEYS ====='
ALTER TABLE q22 DISABLE KEYS;
ALTER TABLE q22 ENABLE KEYS;
\echo ''
\echo '===== 24. INSERT 语句里的反引号 ====='
INSERT INTO `p2` (`id`,`name`) VALUES (1,'x');
\echo ''
\echo '########## 第 2 部分:补救方案实验(会改参数,故放在最后)##########'
\echo ''
\echo '===== 6b. 关闭 ora_input_emptystr_isnull 后重试空串(验证补救方案)====='
SET ora_input_emptystr_isnull = off;
SHOW ora_input_emptystr_isnull;
DELETE FROM p6;
INSERT INTO p6 VALUES (2, '');
SELECT count(*) AS 空串匹配数 FROM p6 WHERE v = '';
SELECT count(*) AS IS_NULL匹配数 FROM p6 WHERE v IS NULL;
SELECT id, v IS NULL AS 是否为NULL FROM p6;
\echo ''
\echo '===== 9a. 关闭 sql_mode 后重试宽松 GROUP BY(验证逃生门)====='
SET sql_mode = '';
SELECT dept, name FROM p7 GROUP BY dept;
SHOW sql_mode;
\echo ''
\echo '===== 9b-1. sql_mode 合法取值(enumvals)====='
SELECT name, setting, enumvals FROM sys_settings WHERE name = 'sql_mode';
\echo ''
\echo '===== 9b-2. 单值设置语法 ====='
SET sql_mode = 'ONLY_FULL_GROUP_BY';
SHOW sql_mode;
\echo ''
\echo '===== 9b-3. TO 语法 ====='
SET sql_mode TO 'ANSI_QUOTES';
SHOW sql_mode;
\echo ''
\echo '===== 9b-4. RESET 恢复默认 ====='
RESET sql_mode;
SHOW sql_mode;
\echo ''
\echo '===== 12b. 关闭 ANSI_QUOTES 后双引号语义(与 12a 对比)====='
SET sql_mode = '';
SELECT "name" FROM p7;
\echo ''
\echo '===== 探针 v2 结束:请保存上面全部输出 ====='
EOF
# 先建探针库(用 system 超级用户)
su - kingbase
# 然后执行探针
/opt/Kingbase/ES/Server/bin/ksql -U system -d probe_db -p 54321 -f /tmp/probe.sql 2>&1 | tee /tmp/probe_result.txt
[root@OE2 ~]# cat /tmp/probe_result.txt
########## 第 0 部分:基线快照 ##########
版本
-------------------------
KingbaseES V009R002C016
(1 行记录)
server_encoding
-----------------
UTF8
(1 行记录)
sql_mode
--------------------------------
ONLY_FULL_GROUP_BY,ANSI_QUOTES
(1 行记录)
ora_input_emptystr_isnull
---------------------------
on
(1 行记录)
########## 第 1 部分:默认状态下的语法兼容性 ##########
===== 1. 反引号标识符 =====
ksql:/tmp/probe.sql:27: NOTICE: table "p1" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:28: ERROR: 语法错误 在 "`" 或附近的
第1行CREATE TABLE p1 (`id` int, `name` varchar(20));
^
===== 2. AUTO_INCREMENT =====
DROP TABLE
CREATE TABLE
===== 3. 建表尾缀 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 =====
ksql:/tmp/probe.sql:37: NOTICE: table "p3" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:38: ERROR: 语法错误 在 "ENGINE" 或附近的
第1行CREATE TABLE p3 (id int) ENGINE=InnoDB DEFAULT CHARSET=utf8m...
^
===== 4a. 类型 tinyint =====
DROP TABLE
CREATE TABLE
===== 4b. 类型 int unsigned =====
ksql:/tmp/probe.sql:47: NOTICE: table "p4b" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:48: ERROR: 语法错误 在 "unsigned" 或附近的
第1行CREATE TABLE p4b (b int unsigned);
^
===== 4c. 类型 datetime =====
DROP TABLE
CREATE TABLE
===== 4d. 类型 text =====
DROP TABLE
CREATE TABLE
===== 4e. 类型 double(10,2) =====
ksql:/tmp/probe.sql:62: NOTICE: table "p4e" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:63: ERROR: 语法错误 在 "(" 或附近的
第1行CREATE TABLE p4e (e double(10,2));
^
===== 5. LIMIT offset,count(MySQL 逗号写法)=====
id | name
----+------
(0 行记录)
===== 6a. 空串写入【可空列】(默认 ora_input_emptystr_isnull)=====
DROP TABLE
CREATE TABLE
INSERT 0 1
总行数
--------
1
(1 行记录)
空串匹配数
------------
0
(1 行记录)
is_null匹配数
---------------
1
(1 行记录)
id | 是否为null
----+------------
1 | t
(1 行记录)
===== 7. GROUP BY 宽松写法(默认 sql_mode 严格性)=====
DROP TABLE
CREATE TABLE
INSERT 0 2
ksql:/tmp/probe.sql:84: ERROR: 字段 "p7.name" 必须出现在 GROUP BY 子句中或者在聚合函数中使用
第1行SELECT dept, name FROM p7 GROUP BY dept;
^
===== 8. MySQL 常用函数 IFNULL / NOW / CONCAT =====
ifnull结果 | now结果 | concat结果
------------+-------------------------------+------------
x | 2026-09-15 10:25:01.107064+08 | ab
(1 行记录)
===== 10. MySQL 专属命令 SHOW TABLES / DESC =====
ksql:/tmp/probe.sql:92: ERROR: unrecognized configuration parameter "tables"
ksql:/tmp/probe.sql:93: ERROR: 语法错误 在 "DESC" 或附近的
第1行DESC p7;
^
===== 11. INSERT ... ON DUPLICATE KEY UPDATE =====
DROP TABLE
CREATE TABLE
INSERT 0 1
ksql:/tmp/probe.sql:100: 警告: 当前数据库模式建议使用on conflict子句
INSERT 0 1
id | v
----+---
1 | b
(1 行记录)
===== 12a. 双引号语义(默认 sql_mode:ANSI_QUOTES 开启态)=====
name
------
x
y
(2 行记录)
===== 13. DEFAULT CHARSET=utf8mb4 + 中文存取 =====
ksql:/tmp/probe.sql:109: NOTICE: table "p13" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:110: ERROR: 语法错误 在 "DEFAULT" 或附近的
第1行CREATE TABLE p13 (v varchar(20)) DEFAULT CHARSET=utf8mb4;
^
ksql:/tmp/probe.sql:111: ERROR: 关系 "p13" 不存在
第1行INSERT INTO p13 VALUES ('中文测试');
^
ksql:/tmp/probe.sql:112: ERROR: 关系 "p13" 不存在
第1行SELECT * FROM p13;
^
===== 14. COLLATE 子句 =====
ksql:/tmp/probe.sql:116: NOTICE: table "p14" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:117: ERROR: 编码 "UTF8" 中的排序规则 "utf8mb4_unicode_ci" 不存在
第1行CREATE TABLE p14 (v varchar(20) COLLATE utf8mb4_unicode_ci);
^
===== 15. 建表尾缀 ENGINE=InnoDB AUTO_INCREMENT=100 =====
ksql:/tmp/probe.sql:121: NOTICE: table "p15" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:122: ERROR: 语法错误 在 "ENGINE" 或附近的
第1行...ATE TABLE p15 (id int AUTO_INCREMENT PRIMARY KEY) ENGINE=Inn...
^
===== 16. LOCK TABLES / UNLOCK TABLES =====
ksql:/tmp/probe.sql:126: ERROR: 语法错误 在 "p7" 或附近的
第1行LOCK TABLES p7 WRITE;
^
ksql:/tmp/probe.sql:127: ERROR: 语法错误 在 "UNLOCK" 或附近的
第1行UNLOCK TABLES;
^
===== 17. SET @@SESSION.SQL_MODE =====
ksql:/tmp/probe.sql:131: ERROR: 语法错误 在 "@@" 或附近的
第1行SET @@SESSION.SQL_MODE = 'ONLY_FULL_GROUP_BY';
^
===== 18. MySQL 条件注释 /*!40101 SET NAMES utf8mb4 */ =====
===== 19. 反引号 + 库名限定 =====
ksql:/tmp/probe.sql:139: ERROR: 语法错误 在 "`" 或附近的
第1行SELECT * FROM `p7`;
^
===== 20a. 内联 KEY(普通索引)=====
ksql:/tmp/probe.sql:143: NOTICE: table "q20a" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:144: ERROR: 类型 "idx_cat" 不存在
第1行CREATE TABLE q20a (id int, cat_id int, KEY idx_cat (cat_id))...
^
===== 20b. 内联 UNIQUE KEY =====
ksql:/tmp/probe.sql:148: NOTICE: table "q20b" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:149: ERROR: 语法错误 在 "KEY" 或附近的
第1行...EATE TABLE q20b (id int, code varchar(10), UNIQUE KEY uk_cod...
^
===== 20c. 内联 CONSTRAINT FOREIGN KEY =====
ksql:/tmp/probe.sql:153: NOTICE: table "q20c" does not exist, skipping
DROP TABLE
CREATE TABLE
===== 20d. NOT NULL + AUTO_INCREMENT + PRIMARY KEY 组合 =====
ksql:/tmp/probe.sql:158: NOTICE: table "q20d" does not exist, skipping
DROP TABLE
CREATE TABLE
===== 21. SET NAMES utf8mb4 =====
ksql:/tmp/probe.sql:163: ERROR: 语法错误 在 "utf8mb4" 或附近的
第1行SET NAMES utf8mb4;
^
===== 22. 索引 USING BTREE =====
ksql:/tmp/probe.sql:167: NOTICE: table "q22" does not exist, skipping
DROP TABLE
ksql:/tmp/probe.sql:168: ERROR: 语法错误 在 "USING" 或附近的
第1行...E TABLE q22 (id int, v varchar(10), KEY idx_v (v) USING BTRE...
^
===== 23. ALTER TABLE DISABLE KEYS / ENABLE KEYS =====
ksql:/tmp/probe.sql:172: ERROR: 语法错误 在 "KEYS" 或附近的
第1行ALTER TABLE q22 DISABLE KEYS;
^
ksql:/tmp/probe.sql:173: ERROR: 语法错误 在 "KEYS" 或附近的
第1行ALTER TABLE q22 ENABLE KEYS;
^
===== 24. INSERT 语句里的反引号 =====
ksql:/tmp/probe.sql:177: ERROR: 语法错误 在 "`" 或附近的
第1行INSERT INTO `p2` (`id`,`name`) VALUES (1,'x');
^
########## 第 2 部分:补救方案实验(会改参数,故放在最后)##########
===== 6b. 关闭 ora_input_emptystr_isnull 后重试空串(验证补救方案)=====
SET
ora_input_emptystr_isnull
---------------------------
off
(1 行记录)
DELETE 1
INSERT 0 1
空串匹配数
------------
1
(1 行记录)
is_null匹配数
---------------
0
(1 行记录)
id | 是否为null
----+------------
2 | f
(1 行记录)
===== 9a. 关闭 sql_mode 后重试宽松 GROUP BY(验证逃生门)=====
SET
dept | name
------+------
a | x
(1 行记录)
sql_mode
----------
""
(1 行记录)
===== 9b-1. sql_mode 合法取值(enumvals)=====
name | setting | enumvals
----------+---------+----------
sql_mode | "" |
(1 行记录)
===== 9b-2. 单值设置语法 =====
SET
sql_mode
----------------------
"ONLY_FULL_GROUP_BY"
(1 行记录)
===== 9b-3. TO 语法 =====
SET
sql_mode
---------------
"ANSI_QUOTES"
(1 行记录)
===== 9b-4. RESET 恢复默认 =====
RESET
sql_mode
--------------------------------
ONLY_FULL_GROUP_BY,ANSI_QUOTES
(1 行记录)
===== 12b. 关闭 ANSI_QUOTES 后双引号语义(与 12a 对比)=====
SET
name
------
x
y
(2 行记录)
===== 探针 v2 结束:请保存上面全部输出 =====
7. 探针结论汇总¶
1) 基线快照¶
| 项目 | 值 | 说明 |
|---|---|---|
| 版本 | KingbaseES V009R002C016 | V9R2 |
| 服务端编码 | UTF8 | 与 MySQL 的 utf8mb4 对应 |
| 默认 sql_mode | ONLY_FULL_GROUP_BY,ANSI_QUOTES | 与源库 MySQL 的严格性一致 |
| ora_input_emptystr_isnull | on | ⚠️ 空串会被当作 NULL,见下方关键发现① |
2) 结论一:原生支持(不用改造)¶
| 探针 | MySQL 写法 | 结果 |
|---|---|---|
| 2 | AUTO_INCREMENT 关键字 |
✅ 支持 |
| 4a | tinyint |
✅ 支持 |
| 4c | datetime |
✅ 支持 |
| 4d | text |
✅ 支持 |
| 5 | LIMIT 0,10(MySQL 逗号写法) |
✅ 支持 |
| 8 | IFNULL / NOW / CONCAT |
✅ 支持 |
| 11 | INSERT ... ON DUPLICATE KEY UPDATE |
✅ 支持(有警告"建议使用 on conflict 子句",但语句生效) |
| 12c | 中文字符串常量 | ✅ 支持 |
| 18 | /*!40101 SET NAMES utf8mb4 */ 条件注释 |
✅ 被当普通注释忽略,不报错 |
| 20c | 内联 CONSTRAINT ... FOREIGN KEY |
✅ 支持 |
| 20d | NOT NULL + AUTO_INCREMENT + PRIMARY KEY 组合 |
✅ 支持 |
注:探针 18 的意义在于 mysqldump 头部有几十行
/*!40101 ... */语句,金仓直接忽略不报错,这部分不需要清洗。
3) 结论二:必须改造(直接报错)¶
| 探针 | MySQL 写法 | 金仓报错 | 改造方式 |
|---|---|---|---|
| 1 / 19 / 24 | 反引号 ` |
语法错误 在 "" 或附近的` |
全局删除反引号 |
| 3 / 15 | ENGINE=InnoDB |
语法错误 在 "ENGINE" 或附近的 |
删除 |
| 3 / 13 | DEFAULT CHARSET=utf8mb4 |
语法错误 在 "DEFAULT" 或附近的 |
删除 |
| 14 | COLLATE utf8mb4_unicode_ci |
编码 "UTF8" 中的排序规则 "utf8mb4_unicode_ci" 不存在 |
删除 |
| 15 | AUTO_INCREMENT=100 |
语法错误 在 "ENGINE" 或附近的 |
删除 |
| 4b | int unsigned |
语法错误 在 "unsigned" 或附近的 |
改为 bigint(MySQL 无符号 int 上限 42 亿,超出金仓 int4 的 21 亿) |
| 4e | double(10,2) |
语法错误 在 "(" 或附近的 |
改为 numeric(10,2) |
| 20a | 内联 KEY idx_cat (cat_id) |
类型 "idx_cat" 不存在 |
拆成独立 CREATE INDEX(见关键发现③) |
| 20b | 内联 UNIQUE KEY uk_code (code) |
语法错误 在 "KEY" 或附近的 |
拆成独立 CREATE UNIQUE INDEX |
| 22 | 索引 USING BTREE |
语法错误 在 "USING" 或附近的 |
删除 |
| 23 | ALTER TABLE ... DISABLE/ENABLE KEYS |
语法错误 在 "KEYS" 或附近的 |
整行删除 |
| 16 | LOCK TABLES / UNLOCK TABLES |
语法错误 在 "p7" 或附近的 |
整行删除 |
| 17 | SET @@SESSION.SQL_MODE = ... |
语法错误 在 "@@" 或附近的 |
整行删除 |
| 21 | SET NAMES utf8mb4; |
语法错误 在 "utf8mb4" 或附近的 |
删除或改写为 SET client_encoding = 'UTF8'; |
| 10 | SHOW TABLES / DESC |
unrecognized configuration parameter "tables" / 语法错误 在 "DESC" |
手工操作改用 \dt / \d |
注:探针 20a 的报错是"类型 idx_cat 不存在"而不是语法错误——说明金仓把索引名当成了列的数据类型来解析,这是内联索引写法不被支持的典型表现。
4) 结论三:语义变化(不报错,但结果不同 —— 最需要警惕的一类)¶
| 项目 | 现象 | 证据 | 能否补救 |
|---|---|---|---|
空字符串 '' |
被转换成 NULL | 探针 6a:插入 '' 后 空串匹配数=0、IS_NULL匹配数=1 |
✅ 能,见关键发现① |
双引号 "..." |
恒被当作标识符(MySQL 默认是字符串) | 探针 12a(ANSI_QUOTES 开)与 12b(关)结果相同,都返回列值 x,y |
❌ 不能(参数无法改变) |
| GROUP BY 严格性 | 非聚合列必须出现在 GROUP BY 中 | 探针 7:字段 "p7.name" 必须出现在 GROUP BY 子句中 |
⚠️ 可用 SET sql_mode = '' 临时放宽,但源库 MySQL 同样是严格模式,两边一致,无需处理 |
注:双引号这条虽然不能补救,但mysqldump 生成的字符串常量用单引号,所以导入过程不受影响;只有应用程序自己的 SQL 里用双引号当字符串时才会出问题。
5) 关键发现①:空串被转成 NULL —— 已找到补救方案 ⭐¶
默认状态(探针 6a,ora_input_emptystr_isnull = on):
===== 6a. 空串写入【可空列】(默认 ora_input_emptystr_isnull)=====
INSERT 0 1
总行数
--------
1
空串匹配数
------------
0 ← SELECT ... WHERE v = '' 查不到
is_null匹配数
---------------
1 ← 它变成了 NULL
id | 是否为null
----+------------
1 | t ← 确认是 NULL
关闭参数后(探针 6b):
===== 6b. 关闭 ora_input_emptystr_isnull 后重试空串 =====
SET
ora_input_emptystr_isnull
---------------------------
off
INSERT 0 1
空串匹配数
------------
1 ← 空串能被查到了
is_null匹配数
---------------
0 ← 不再是 NULL
id | 是否为null
----+------------
2 | f ← 确认是空串
对比结论:
| 参数状态 | 插入 '' 后实际存的值 |
与 MySQL 语义 |
|---|---|---|
ora_input_emptystr_isnull = on(默认) |
NULL | ❌ 不一致 |
ora_input_emptystr_isnull = off |
空串 '' |
✅ 完全一致 |
这意味着:不需要为了这个差异把实例重建为 pg 模式,只需把参数关掉。
迁移前持久化该参数(会话级设置对应用连接无效,必须写配置文件):
sed -i 's/^ora_input_emptystr_isnull.*/ora_input_emptystr_isnull = off/' /opt/Kingbase/ES/Server/data/kingbase.conf
grep -n ora_input_emptystr_isnull /opt/Kingbase/ES/Server/data/kingbase.conf
systemctl restart kingbased
验证(看输出中的值应为 off):
[kingbase@OE2 ~]$ /opt/Kingbase/ES/Server/bin/ksql -U system -d test -p 54321 -c "SHOW ora_input_emptystr_isnull;"
ora_input_emptystr_isnull
---------------------------
off
(1 行记录)
⚠️ 为什么这条最重要:空串变 NULL 不报错,会静默改变数据语义——
WHERE x = ''查不到数据、COUNT(x)数字变小、''的唯一性判断失效。如果不在迁移前处理,迁完之后"数据看起来都在,但业务查询结果不对",排查起来极其困难。
6) 关键发现②:sql_mode 的正确操作法¶
| 操作 | 结论 | 命令 |
|---|---|---|
| 设为单值 | ✅ 可用 | SET sql_mode = 'ONLY_FULL_GROUP_BY'; |
| TO 语法 | ✅ 也可用 | SET sql_mode TO 'ANSI_QUOTES'; |
| 恢复默认 | ✅ 用 RESET | RESET sql_mode;(恢复为 ONLY_FULL_GROUP_BY,ANSI_QUOTES) |
| 设多值 | ❌ 报错 未知的关键字: "A,B" |
不能照搬 MySQL 的 SET sql_mode = 'A,B' |
本环境不需要调整:源库 MySQL 的 my.cnf 中 sql_mode 已包含 ONLY_FULL_GROUP_BY,与金仓默认一致,因此 GROUP BY 行为两边相同。
如果确实需要放宽(老应用 SQL 不合严格模式),用探针 9a 验证过的逃生门:
===== 9a. 关闭 sql_mode 后重试宽松 GROUP BY(验证逃生门)=====
SET
dept | name
------+------
a | x ← 关闭后,宽松 GROUP BY 成功执行
(1 行记录)
注:
sql_mode是会话级参数,断开重连即恢复默认值(探针第二轮重新执行时,探针 7 又报了严格模式错误,正好证明这一点)。
7) 关键发现③:内联索引不支持,但外键支持¶
| 写法 | 结果 |
|---|---|
内联 KEY idx_cat (cat_id) |
❌ 类型 "idx_cat" 不存在 |
内联 UNIQUE KEY uk_code (code) |
❌ 语法错误 在 "KEY" 或附近的 |
内联 CONSTRAINT fk_cat FOREIGN KEY (...) REFERENCES ... |
✅ 建表成功 |
这是本次迁移最大的改造工作量——mysqldump 每张表的索引都要从建表语句里搬出来。改造前后对比:
-- 改造前(mysqldump 原始输出)
CREATE TABLE product (
id int NOT NULL AUTO_INCREMENT,
cat_id int NOT NULL,
code varchar(20) DEFAULT NULL,
PRIMARY KEY (id),
KEY idx_cat (cat_id),
UNIQUE KEY uk_code (code),
CONSTRAINT fk_cat FOREIGN KEY (cat_id) REFERENCES category (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 改造后(金仓可执行)
CREATE TABLE product (
id int NOT NULL AUTO_INCREMENT,
cat_id int NOT NULL,
code varchar(20) DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT fk_cat FOREIGN KEY (cat_id) REFERENCES category (id)
);
CREATE INDEX idx_cat ON product (cat_id);
CREATE UNIQUE INDEX uk_code ON product (code);
8) 迁移改造清单(清洗规则)¶
第一批:可用 sed 批量处理(覆盖大部分改动)
cat > /tmp/fix.sed <<'EOF'
s/`//g
s/ ENGINE=[A-Za-z]+//g
s/ AUTO_INCREMENT=[0-9]+//g
s/ DEFAULT CHARSET[= ][A-Za-z0-9_]+//g
s/ DEFAULT CHARACTER SET[= ][A-Za-z0-9_]+//g
s/ COLLATE[= ][A-Za-z0-9_]+//g
s/ USING BTREE//g
s/\bint unsigned\b/bigint/g
s/\bdouble\(([0-9]+),([0-9]+)\)/numeric(\1,\2)/g
/^LOCK TABLES/d
/^UNLOCK TABLES/d
/^SET @@/d
/^SET NAMES/d
/DISABLE KEYS/d
/ENABLE KEYS/d
EOF
sed -i -E -f /tmp/fix.sed shop.sql
第二批:必须人工或脚本处理
| 项目 | 处理方式 |
|---|---|
内联 KEY / UNIQUE KEY |
抽出来改写成独立的 CREATE INDEX / CREATE UNIQUE INDEX 语句(见关键发现③) |
SET NAMES utf8mb4; |
删除,或改写为 SET client_encoding = 'UTF8'; |
| 应用 SQL 里的双引号字符串 | 无法通过参数补救,需改写为单引号 |
9) 小结¶
| 分类 | 数量 | 处理 |
|---|---|---|
| 原生支持 | 11 项 | 无需处理 |
| 必须改造 | 15 项 | sed 批量清洗 + 内联索引人工搬家 |
| 语义变化 | 3 项 | 空串靠参数补救(已解决);双引号需改 SQL;GROUP BY 两边一致 |
结论:金仓(Oracle 兼容模式)对 MySQL 语法的兼容度中等偏上——常用函数、LIMIT 逗号写法、AUTO_INCREMENT、ON DUPLICATE KEY UPDATE、内联外键都能直接用;主要障碍集中在 mysqldump 的建表装饰语(ENGINE/CHARSET/COLLATE)和反引号,这些用 sed 就能批量清理;唯一需要参数层面处理的是空串语义,已在探针 6b 中验证可通过 ora_input_emptystr_isnull = off 解决。
四. 迁移开始¶
1.先将本地事先准备好的数据文件导入到mysql中¶
验证
[root@Rocky7 ~]# mysql -u root -p -e 'show databases;'
Enter password:
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| world |
+--------------------+
2. 金仓侧改参数¶
su - kingbase
/opt/Kingbase/ES/Server/bin/ksql -U system -d test -p 54321 -c "ALTER SYSTEM SET ora_input_emptystr_isnull = off;"
exit
systemctl restart kingbased
验证: 需要看到off
[root@OE2 ~]# /opt/Kingbase/ES/Server/bin/ksql -U system -d test -p 54321 -c "SHOW ora_input_emptystr_isnull;"
用户 system 的口令:
ora_input_emptystr_isnull
---------------------------
off
(1 行记录)
ALTER SYSTEM SET 是 PG 风格写法,会把配置写进 kingbase.auto.conf;如果它报错,退回到直接改配置文件:
sed -i 's/^ora_input_emptystr_isnull.*/ora_input_emptystr_isnull = off/' /opt/Kingbase/ES/Server/data/kingbase.conf
systemctl restart kingbased
3. 顺手补测一个探针¶
world 库有 2 个 enum(...) 列,我们的探针没测过这个类型:
cat > /tmp/probe4.sql <<'EOF'
-- ============================================================
-- 探针 27:列级 CHECK 约束(MySQL enum 的替代方案是否可行)
-- 用法:ksql -U system -d probe_db -p 54321 -f /tmp/probe4.sql
-- ============================================================
\echo ''
\echo '===== 27. 列级 CHECK ... IN (...),替代 MySQL enum ====='
DROP TABLE IF EXISTS q27;
CREATE TABLE q27 (v varchar(50) NOT NULL DEFAULT 'F' CHECK (v IN ('T','F')));
INSERT INTO q27 VALUES ('T');
SELECT * FROM q27;
\echo ''
\echo '----- 27b. 插入非法值 X,应被 CHECK 拒绝(证明约束真的生效)-----'
INSERT INTO q27 VALUES ('X');
SELECT * FROM q27;
\echo ''
\echo '----- 27c. 默认值 DEFAULT 是否生效 -----'
INSERT INTO q27 DEFAULT VALUES;
SELECT * FROM q27 ORDER BY v;
\echo ''
\echo '===== 探针 27 结束 ====='
EOF
su - kingbase
export KINGBASE_PASSWORD='Kingbase@2026'
/opt/Kingbase/ES/Server/bin/ksql -U system -d probe_db -p 54321 -f /tmp/probe4.sql
4. 源端导出¶
在7上
/usr/local/mysql/bin/mysqldump -uroot \
--databases world \
--single-transaction \
--set-gtid-purged=OFF \
--default-character-set=utf8mb4 \
--no-tablespaces \
--skip-add-locks \
--skip-comments \
> /root/world_migrate.sql
5. 写脚本,然后清洗¶
这两个脚本分别是
s/`//g
s/ ENGINE=[A-Za-z]+//g
s/ AUTO_INCREMENT=[0-9]+//g
s/ DEFAULT CHARSET[= ][A-Za-z0-9_]+//g
s/ DEFAULT CHARACTER SET[= ][A-Za-z0-9_]+//g
s/ COLLATE[= ][A-Za-z0-9_]+//g
s/ USING BTREE//g
s/\bint unsigned\b/bigint/g
s/\bdouble\(([0-9]+),([0-9]+)\)/numeric(\1,\2)/g
s/^([[:space:]]*)([A-Za-z0-9_]+)[[:space:]]+enum\(([^)]*)\)(.*),$/\1\2 varchar(50)\4 CHECK (\2 IN (\3)),/
s/\)[[:space:]]*;/);/
/^LOCK TABLES/d
/^UNLOCK TABLES/d
/^SET @/d
/^SET @@/d
/^SET autocommit/d
/^SET NAMES/d
/DISABLE KEYS/d
/ENABLE KEYS/d
# ============================================================
# move_index.awk —— 把 CREATE TABLE 内的内联 KEY / UNIQUE KEY 行
# 搬到建表语句之后,改写成独立的 CREATE INDEX 语句
# (金仓 KingbaseES 不支持内联 KEY/UNIQUE KEY —— 探针 20a/20b 实测)
# 用法:awk -f move_index.awk 已用fix.sed清洗过.sql > 最终.sql
# 注意:本脚本按"无反引号"的写法匹配,必须先跑 fix.sed
# ============================================================
BEGIN { inct = 0; nb = 0; nk = 0; tbl = "" }
# ① CREATE TABLE 起始行:抓表名
inct == 0 && /^CREATE TABLE/ {
inct = 1; nb = 0; nk = 0
tbl = $0
sub(/^CREATE TABLE[ \t]+/, "", tbl)
sub(/[ \t(].*$/, "", tbl)
print
next
}
# ② 表内的内联索引行 → 暂存,不输出
inct == 1 && /^[ \t]*(UNIQUE[ \t]+KEY|FULLTEXT[ \t]+KEY|SPATIAL[ \t]+KEY|KEY)[ \t]/ {
l = $0
sub(/^[ \t]+/, "", l)
sub(/,[ \t]*$/, "", l)
gsub(/[ \t]+USING[ \t]+BTREE/, "", l)
u = 0
if (l ~ /^UNIQUE[ \t]+KEY/) { u = 1; sub(/^UNIQUE[ \t]+KEY[ \t]+/, "", l) }
else if (l ~ /^FULLTEXT[ \t]+KEY/) { sub(/^FULLTEXT[ \t]+KEY[ \t]+/, "", l) }
else if (l ~ /^SPATIAL[ \t]+KEY/) { sub(/^SPATIAL[ \t]+KEY[ \t]+/, "", l) }
else { sub(/^KEY[ \t]+/, "", l) }
nk++
nm[nk] = l; sub(/[ \t]*\(.*$/, "", nm[nk])
co[nk] = l; sub(/^[^(]*\(/, "", co[nk]); sub(/\)[ \t]*$/, "", co[nk])
uq[nk] = u
next
}
# ③ 建表结束行:先输出缓冲的列定义(修正末行逗号),再输出独立索引
inct == 1 && /^\)/ {
if (nb > 0) {
c = body[nb]
sub(/,[ \t]*$/, "", c)
body[nb] = c
for (i = 1; i <= nb; i++) print body[i]
}
print
for (i = 1; i <= nk; i++) {
if (uq[i]) printf "CREATE UNIQUE INDEX %s ON %s (%s);\n", nm[i], tbl, co[i]
else printf "CREATE INDEX %s ON %s (%s);\n", nm[i], tbl, co[i]
}
inct = 0; nb = 0; nk = 0; tbl = ""
next
}
# ④ 表内其他行 → 缓冲
inct == 1 { nb++; body[nb] = $0; next }
# ⑤ 表外普通行 → 原样输出
{ print }