Oracle 19c数据库迁移到IvorySQL 4.6实战

SpringBoot+Vue+MySQL 高校学科竞赛平台 完整可运行版(源码+文档) 本项目基于SpringBoot+Vue+MySQL构建高校学科竞赛管理平台,覆盖学生、教师、领队教师、管理员四大角色,实现竞赛发布、报名、刷题、评分、公示等全流程闭环管理。系统架构清晰,技术主流,配套完整源码、数据库脚本、万字论文、开题报告、答辩PPT及部署文档,支持一键运行,适用于毕业设计、课程设计及院系二次开发,真正实现“开箱即用”。 阅读详情

1. 背景

在国家数字化转型与信创产业加速推进的背景下,数据库作为信息系统的核心基础设施,其国产化替代已成为企业数字化建设的重要战略方向。Oracle 数据库凭借成熟的技术体系长期占据市场主导地位,但在自主可控、成本优化及适配本土生态等需求驱动下,基于开源技术演进的国产数据库逐渐成为迁移替代的优选方案。

IvorySQL 作为一款兼容 PostgreSQL 生态的国产数据库,不仅继承了 PostgreSQL 的开源特性与扩展性,还针对企业级场景增强了兼容性与稳定性,尤其在兼容 Oracle 语法、数据类型及存储过程等方面进行了优化,成为 Oracle 迁移的理想目标之一。

本次实践旨在通过 Ora2Pg 工具(一款专注于 Oracle 到 PostgreSQL 生态的迁移工具),完整演示从 Oracle 19c 到 IvorySQL 4.6 的全量迁移过程,包括环境搭建、对象转换、数据迁移、兼容性处理及验证等关键环节。通过构建模拟业务场景的测试数据(含表、自定义类型、存储过程、触发器等典型对象),还原真实迁移中的技术细节与问题解决思路,为企业级数据库国产化迁移提供可复用的实践参考。

2. 环境说明

主机名ip 地址OS 版本内存、CPU安装软件用途
node1192.*.*.60Centos7.94G 、 1 个双核IvorySQL 4.6 数据库IvorySQL 4.6 数据库
node1192.*.*.60Centos7.94G 、 1 个双核ora2pg 软件迁移数据库 oracle->IvorySQL
node3192.*.*.64Centos7.94G 、 1 个双核Oracle19c 数据库Oracle19c 数据库

3. 迁移工具 Ora2Pg 部署

Ora2Pg 是一款免费工具,用于将 Oracle 数据库迁移到与 PostgreSQL 数据库中。它会连接 Oracle 数据库,自动扫描并提取其结构或数据,然后生成可加载到 PostgreSQL 数据库中的 SQL 脚本。

3.1 在迁移主机上安装 Ora2Pg 工具

3.1.1 安装依赖包

yum install -y perl perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker perl-CPAN

3.1.2 下载并安装 Oracle 客户端

下载地址:https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html


#安装oracle客户端 rpm -ivh oracle-instantclient19.28-basic-19.28.0.0.0-1.x86_64.rpm rpm -ivh oracle-instantclient19.28-devel-19.28.0.0.0-1.x86_64.rpm rpm -ivh oracle-instantclient19.28-jdbc-19.28.0.0.0-1.x86_64.rpm rpm -ivh oracle-instantclient19.28-sqlplus-19.28.0.0.0-1.x86_64.rpm

安装好 Oracle 客户端后,配置 tnsnames.ora 文件,以便能正常连接到 Oracle 19c 数据库。


vi /usr/lib/oracle/19.28/client64/network/admin/tnsnames.ora ORCL = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.*.*.64)(PORT = 1539)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = orcl) ) )

3.1.3 下载并安装 DBI

下载地址:https://www.cpan.org/modules/by-module/DBI/


tar -zxvf DBI-1.647.tgz cd DBI-1.647 perl Makefile.PL make && make install

3.1.4 下载并安装 DBD::Oracle

下载地址:https://www.cpan.org/modules/by-module/DBD/


#设置环境变量 export ORACLE_HOME=/usr/lib/oracle/19.28/client64 export PATH=$ORACLE_HOME/bin:/data/ivory-4/bin:$PATH export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH #解压DBD::Oracle tar -zxvf DBD-Oracle-1.90.tar.gz #编译安装DBD::Oracle cd DBD-Oracle-1.90 perl Makefile.PL make && make install

3.1.5 下载并安装 DBD::Pg

下载地址:https://www.cpan.org/modules/by-module/DBD/


#解压DBD::Pg tar -zxvf DBD-Pg-3.18.0.tar.gz #编译安装DBD::Pg cd DBD-Pg-3.18.0 perl Makefile.PL make && make install

3.1.6 下载并安装 Ora2Pg

下载地址:https://github.com/darold/ora2pg/releases/tag/v25.0/ora2pg-25.0.tar.gz


tar -zxvf ora2pg-25.0.tar.gz cd ora2pg-25.0 perl Makefile.PL make && make install

Ora2Pg 工具安装完之后,在 /etc/ora2pg 目录下会生成 ora2pg.conf.dist 配置文件。

[root@node1 ora2pg]# pwd
/etc/ora2pg
[root@node1 ora2pg]# ll
total 72
-rw-r--r-- 1 root root 71836 Sep 20 20:34 ora2pg.conf.dist
3.1.7 检查 DBI,DBD::Oracle,DBD::Pg ,Ora2Pg 组件是否已经安装完成

vi check.pl 加入: #!/usr/bin/perl use strict; use ExtUtils::Installed; my $inst=ExtUtils::Installed->new(); my @modules = $inst->modules(); foreach(@modules){ my $ver = $inst->version($_) || "???"; printf("%-12s -- %s\n",$_,$ver); } exit;


perl /check.pl

执行结果如下:


[root@node1 ~]# perl /check.pl DBD::Oracle -- 1.90 DBD::Pg -- 3.18.0 DBI -- 1.647 Ora2Pg -- 25.0 Perl -- 5.16.3 [root@node1 ~]#

至此,Ora2Pg 工具正常安装完成。

3.2 Ora2Pg 工具使用

3.2.1 生成配置文件 ora2pg.conf

#去掉ora2pg.conf.dist的注释和空行 cd /etc/ora2pg grep -v -E '^\s*(#|$)' ora2pg.conf.dist >ora2pg.conf

3.2.2 编辑配置文件 ora2pg.conf

根据 Oracle 19c 数据库信息,在 ora2pg.conf 中修改下面信息,如下:


ORACLE_HOME /data/app/oracle/product/19.3.0/db ORACLE_DSN dbi:Oracle:host=192.*.*.64;sid=orcl;port=1539 ORACLE_USER system ORACLE_PWD ******

3.2.3 测试是否可以正常连接 Oracle 19c 数据库

[root@node1 ora2pg]# ora2pg -t SHOW_VERSION -c /etc/ora2pg/ora2pg.conf Oracle Database 19c Enterprise Edition Release 19.0.0.0.0

4. 迁移前准备

4.1 数据库对象构造脚本

由于生产环境的数据库对象有敏感数据,因此本次实验全部使用自己构造的测试脚本来进行,如有雷同纯属巧合。


-- 创建用户 CREATE USER ywuser IDENTIFIED BY ******; GRANT CONNECT,RESOURCE,CREATE SYNONYM TO ywuser; ALTER USER ywuser QUOTA UNLIMITED ON USERS; conn ywuser/pass -- 创建自定义类型 CREATE OR REPLACE TYPE address_type AS OBJECT ( street VARCHAR2(100), city VARCHAR2(50), zip_code VARCHAR2(10) ); / -- 创建表 CREATE TABLE departments ( department_id NUMBER PRIMARY KEY, department_name VARCHAR2(100) NOT NULL, location address_type ); CREATE TABLE employees ( employee_id NUMBER PRIMARY KEY, first_name VARCHAR2(50), last_name VARCHAR2(50) NOT NULL, email VARCHAR2(100), hire_date DATE DEFAULT SYSDATE, department_id NUMBER REFERENCES departments(department_id), salary NUMBER(10,2) ); CREATE TABLE audit_log ( log_id NUMBER PRIMARY KEY, table_name VARCHAR2(100), action VARCHAR2(10), change_date TIMESTAMP, user_name VARCHAR2(30) ); CREATE TABLE salary_history ( history_id NUMBER PRIMARY KEY, employee_id NUMBER REFERENCES employees(employee_id), old_salary NUMBER(10,2), new_salary NUMBER(10,2), change_date DATE ); CREATE TABLE timestamp_demo ( id NUMBER PRIMARY KEY, description VARCHAR2(100), created_time TIMESTAMP, -- 精确到小数秒的时间戳 last_updated TIMESTAMP WITH TIME ZONE -- 带时区信息的时间戳 ); -- 创建索引 CREATE INDEX IDX_salary_history ON salary_history(employee_id); -- 创建序列 CREATE SEQUENCE employees_seq START WITH 1 INCREMENT BY 1; CREATE SEQUENCE departments_seq START WITH 1 INCREMENT BY 1; CREATE SEQUENCE salary_history_seq START WITH 1 INCREMENT BY 1; -- 插入 departments 表数据 INSERT INTO departments (department_id, department_name, location) VALUES (departments_seq.NEXTVAL, '人力资源', address_type('人民路100号', '北京', '100000')); INSERT INTO departments (department_id, department_name, location) VALUES (departments_seq.NEXTVAL, '财务部', address_type('金融大街88号', '上海', '200000')); INSERT INTO departments (department_id, department_name, location) VALUES (departments_seq.NEXTVAL, '技术研发', address_type('科技园路1号', '深圳', '518000')); INSERT INTO departments (department_id, department_name, location) VALUES (departments_seq.NEXTVAL, '市场营销', address_type('商业中心广场', '广州', '510000')); INSERT INTO departments (department_id, department_name, location) VALUES (departments_seq.NEXTVAL, '客户服务', address_type('服务大道5号', '杭州', '310000')); -- 插入 employees 表数据 INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, department_id, salary) VALUES (employees_seq.NEXTVAL, '张', '明', 'zhang.ming@example.com', TO_DATE('2020-01-15', 'YYYY-MM-DD'), 1, 8000); INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, department_id, salary) VALUES (employees_seq.NEXTVAL, '李', '华', 'li.hua@example.com', TO_DATE('2019-03-20', 'YYYY-MM-DD'), 2, 9500); INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, department_id, salary) VALUES (employees_seq.NEXTVAL, '王', '强', 'wang.qiang@example.com', TO_DATE('2021-05-10', 'YYYY-MM-DD'), 3, 12000); INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, department_id, salary) VALUES (employees_seq.NEXTVAL, '赵', '雪', 'zhao.xue@example.com', TO_DATE('2018-11-05', 'YYYY-MM-DD'), 4, 8500); INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, department_id, salary) VALUES (employees_seq.NEXTVAL, '陈', '亮', 'chen.liang@example.com', TO_DATE('2022-02-28', 'YYYY-MM-DD'), 5, 7800); -- 插入 salary_history 表数据 INSERT INTO salary_history (history_id, employee_id, old_salary, new_salary, change_date) VALUES (salary_history_seq.NEXTVAL, 1, 7500, 8000, TO_DATE('2023-01-01', 'YYYY-MM-DD')); INSERT INTO salary_history (history_id, employee_id, old_salary, new_salary, change_date) VALUES (salary_history_seq.NEXTVAL, 2, 9000, 9500, TO_DATE('2023-01-01', 'YYYY-MM-DD')); INSERT INTO salary_history (history_id, employee_id, old_salary, new_salary, change_date) VALUES (salary_history_seq.NEXTVAL, 3, 11000, 12000, TO_DATE('2023-01-01', 'YYYY-MM-DD')); INSERT INTO salary_history (history_id, employee_id, old_salary, new_salary, change_date) VALUES (salary_history_seq.NEXTVAL, 4, 8000, 8500, TO_DATE('2023-01-01', 'YYYY-MM-DD')); INSERT INTO salary_history (history_id, employee_id, old_salary, new_salary, change_date) VALUES (salary_history_seq.NEXTVAL, 5, 7000, 7800, TO_DATE('2023-01-01', 'YYYY-MM-DD')); -- 插入 timestamp_demo 表数据 INSERT INTO timestamp_demo VALUES (1, '第一条记录',TO_TIMESTAMP('2023-10-01 08:00:00.123456', 'YYYY-MM-DD HH24:MI:SS.FF6'),SYSTIMESTAMP); INSERT INTO timestamp_demo VALUES (2, '第二条记录', SYSTIMESTAMP, -- 使用系统当前时间戳 CURRENT_TIMESTAMP -- 带时区的当前时间戳 ); INSERT INTO timestamp_demo VALUES (3, '第三条记录', TIMESTAMP '2023-10-03 14:30:45.789123', -- 时间戳字面量 SYSTIMESTAMP AT TIME ZONE 'UTC' -- 指定UTC时区 ); commit; -- 创建同义词 CREATE SYNONYM emp_syn FOR employees; -- 创建函数(使用自定义类型) CREATE OR REPLACE FUNCTION get_department_address( p_department_id IN NUMBER ) RETURN address_type IS v_address address_type; BEGIN SELECT location INTO v_address FROM departments WHERE department_id = p_department_id ; RETURN v_address; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END; / -- 创建存储过程 CREATE OR REPLACE PROCEDURE increase_salary ( p_emp_id IN NUMBER, p_percent IN NUMBER ) IS BEGIN UPDATE employees SET salary = salary * (1 + p_percent/100) WHERE employee_id = p_emp_id; COMMIT; END; / -- 创建包规范 CREATE OR REPLACE PACKAGE employee_pkg AS TYPE emp_cursor IS REF CURSOR; PROCEDURE get_employees(dept_id IN NUMBER, emp_list OUT emp_cursor); FUNCTION get_avg_salary(dept_id IN NUMBER) RETURN NUMBER; END employee_pkg; / -- 创建包体 CREATE OR REPLACE PACKAGE BODY employee_pkg AS PROCEDURE get_employees(dept_id IN NUMBER, emp_list OUT emp_cursor) IS BEGIN OPEN emp_list FOR SELECT * FROM employees WHERE department_id = dept_id; END; FUNCTION get_avg_salary(dept_id IN NUMBER) RETURN NUMBER IS v_avg_salary NUMBER; BEGIN SELECT AVG(salary) INTO v_avg_salary FROM employees WHERE department_id = dept_id; RETURN v_avg_salary; END; END employee_pkg; / -- 创建触发器 CREATE OR REPLACE TRIGGER salary_audit_trigger BEFORE UPDATE OF salary ON employees FOR EACH ROW BEGIN INSERT INTO salary_history (history_id, employee_id, old_salary, new_salary, change_date) VALUES (employees_seq.NEXTVAL, :OLD.employee_id, :OLD.salary, :NEW.salary, SYSDATE); END; / CREATE OR REPLACE TRIGGER dml_audit_trigger AFTER INSERT OR UPDATE OR DELETE ON employees DECLARE v_action VARCHAR2(10); BEGIN IF INSERTING THEN v_action := 'INSERT'; ELSIF UPDATING THEN v_action := 'UPDATE'; ELSIF DELETING THEN v_action := 'DELETE'; END IF; INSERT INTO audit_log (log_id, table_name, action, change_date, user_name) VALUES (employees_seq.NEXTVAL, 'EMPLOYEES', v_action, SYSTIMESTAMP, USER); END; /

4.2 查看 Oracle 19c 数据库字符集和 sid 信息


SQL> SELECT value FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET'; VALUE -------------------------------------------------------------------------------- ZHS16GBK SQL> show parameter instance_name NAME TYPE VALUE ------------------------------------ --------------------------------- ------------------------------ instance_name string orcl SQL>

4.3 迁移前评估成本

在评估过程中,Ora2Pg 会检查所有数据库对象,包括所有函数和存储过程,以检测是否仍有一些对象和 PL / SQL 代码无法由 Ora2Pg 自动转换,最终,通过内容分析模式,生成有关 Oracle 数据库包含的内容和无法导出的内容的文本报告。

生成评估报告:


ora2pg -t SHOW_REPORT --estimate_cost -c ora2pg.conf

执行过程如下:


[root@node1 ora2pg]# ora2pg -t SHOW_REPORT --estimate_cost -c ora2pg.conf [2025-09-21 10:20:54] [========================>] 5/5 tables (100.0%) end of scanning. [2025-09-21 10:21:01] [========================>] 13/13 objects types (100.0%) end of objects auditing. ------------------------------------------------------------------------------- Ora2Pg v25.0 - Database Migration Report ------------------------------------------------------------------------------- Version Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 Schema YWUSER Size 0.56 MB ------------------------------------------------------------------------------- Object Number Invalid Estimated cost Comments Details ------------------------------------------------------------------------------- DATABASE LINK 0 0 0.00 Database links will be exported as SQL/MED PostgreSQL's Foreign Data Wrapper (FDW) extensions using oracle_fdw. FUNCTION 1 0 4.00 Total size of function code: 458 bytes. get_department_address: 3. GLOBAL TEMPORARY TABLE 0 0 0.00 Global temporary table are not supported by PostgreSQL and will not be exported. You will have to rewrite some application code to match the PostgreSQL temporary table behavior. INDEX 6 0 1.10 1 index(es) are concerned by the export, others are automatically generated and will do so on PostgreSQL. Bitmap will be exported as btree_gin index(es). Domain index are exported as b-tree but commented to be edited to mainly use FTS. Cluster, bitmap join and IOT indexes will not be exported at all. Reverse indexes are not exported too, you may use a trigram-based index (see pg_trgm) or a reverse() function based index and search. Use 'varchar_pattern_ops', 'text_pattern_ops' or 'bpchar_pattern_ops' operators in your indexes to improve search with the LIKE operator respectively into varchar, text or char columns. 1 b-tree index(es). JOB 0 0 0.00 Job are not exported. You may set external cron job with them. PACKAGE BODY 1 0 12.00 Total size of package code: 504 bytes. Number of procedures and functions found inside those packages: 2. employee_pkg.get_avg_salary: 3. employee_pkg.get_employees: 3. PROCEDURE 1 0 4.00 Total size of procedure code: 339 bytes. increase_salary: 3. SEQUENCE 3 0 1.00 Sequences are fully supported, but all call to sequence_name.NEXTVAL or sequence_name.CURRVAL will be transformed into NEXTVAL('sequence_name') or CURRVAL('sequence_name'). SYNONYM 1 0 1.00 SYNONYMs will be exported as views. SYNONYMs do not exists with PostgreSQL but a common workaround is to use views or set the PostgreSQL search_path in your session to access object outside the current schema. emp_syn is an alias to YWUSER.EMPLOYEES. TABLE 5 0 1.00 Total number of rows: 18. Top 10 of tables sorted by number of rows:. salary_history has 5 rows. departments has 5 rows. employees has 5 rows. timestamp_demo has 3 rows. Top 10 of largest tables:. TRIGGER 2 0 8.00 Total size of trigger code: 0 bytes. dml_audit_trigger: 3. salary_audit_trigger: 3. TYPE 1 0 1.00 1 type(s) are concerned by the export, others are not supported. Note that Type inherited and Subtype are converted as table, type inheritance is not supported. 1 object type. ------------------------------------------------------------------------------- Total 21 0 33.10 33.10 cost migration units means approximatively 1 person-day(s). The migration unit was set to 5 minute(s) ------------------------------------------------------------------------------- Migration level : A-3 ------------------------------------------------------------------------------- Migration levels: A - Migration that might be run automatically B - Migration with code rewrite and a human-days cost up to 5 days C - Migration with code rewrite and a human-days cost above 5 days Technical levels: 1 = trivial: no stored functions and no triggers 2 = easy: no stored functions but with triggers, no manual rewriting 3 = simple: stored functions and/or triggers, no manual rewriting 4 = manual: no stored functions but with triggers or views with code rewriting 5 = difficult: stored functions and/or triggers with code rewriting ------------------------------------------------------------------------------- Details of cost assessment per function Function get_department_address total estimated cost: 3 TEST => 2 SIZE => 1 ------------------------------------------------------------------------------- Details of cost assessment per procedure Function increase_salary total estimated cost: 3 TEST => 2 SIZE => 1 ------------------------------------------------------------------------------- Details of cost assessment per package function Function employee_pkg.get_employees total estimated cost: 3 TEST => 2 SIZE => 1 Function employee_pkg.get_avg_salary total estimated cost: 3 TEST => 2 SIZE => 1 ------------------------------------------------------------------------------- Details of cost assessment per trigger Trigger salary_audit_trigger total estimated cost: 3 TEST => 2 SIZE => 1 Trigger dml_audit_trigger total estimated cost: 3 TEST => 2 SIZE => 1 ------------------------------------------------------------------------------- [root@node1 ora2pg]#

可以从评估报告中看到迁移过程中哪些对象会发生转换,哪些对象不被支持,迁移大概花费时间、成本等信息。

5. 数据库迁移

5.1 创建项目

Ora2pg 可以针对每次迁移任务创建一个项目,项目下存放着迁移过程中生成的各个类型的对象创建脚本及数据。


ora2pg --project_base /data/migration --init_project test_project

执行过程如下:


[root@node1 ~]# ora2pg --project_base /data/migration --init_project test_project Creating project test_project. /data/migration/test_project/ schema/ dblinks/ directories/ functions/ grants/ mviews/ packages/ partitions/ procedures/ sequences/ sequence_values/ synonyms/ tables/ tablespaces/ triggers/ types/ views/ sources/ functions/ mviews/ packages/ partitions/ procedures/ triggers/ types/ views/ data/ config/ reports/ Generating generic configuration file Creating script export_schema.sh to automate all exports. Creating script import_all.sh to automate all imports. [root@node1 migration]# cd /data/migration/test_project/ [root@node1 test_project]# ll total 24 drwxr-xr-x 2 root root 25 Sep 21 22:39 config drwxr-xr-x 2 root root 6 Sep 21 22:39 data -rwx------ 1 root root 2216 Sep 21 22:39 export_schema.sh -rwx------ 1 root root 17201 Sep 21 22:39 import_all.sh drwxr-xr-x 2 root root 6 Sep 21 22:39 reports drwxr-xr-x 18 root root 268 Sep 21 22:39 schema drwxr-xr-x 10 root root 131 Sep 21 22:39 sources [root@node1 test_project]#

5.2 开始迁移

5.2.1 查看 oracle 数据库中有哪些类型的对象

SQL> select distinct object_type from dba_objects WHERE owner='YWUSER'; OBJECT_TYPE --------------------------------------------------------------------- INDEX SYNONYM PACKAGE BODY TRIGGER PROCEDURE PACKAGE FUNCTION TYPE SEQUENCE TABLE 10 rows selected.

5.2.2 所有对象一次迁移
5.2.2.1 Oracle 数据库对象定义(DDL)全部导出

cd /data/migration/test_project cp /etc/ora2pg/ora2pg.conf /data/migration/test_project/config/ sh export_schema.sh

执行过程如下:


[root@node1 test_project]# cp /etc/ora2pg/ora2pg.conf /data/migration/test_project/config/ cp: overwrite ‘./ora2pg.conf’? y [root@node1 test_project]# sh export_schema.sh [2025-09-21 22:49:58] [========================>] 5/5 tables (100.0%) end of scanning. Aborting export... Running: ora2pg -p -t SEQUENCE -o sequence.sql -b ./schema/sequences -c ./config/ora2pg.conf [2025-09-21 22:49:59] [========================>] 3/3 sequences (100.0%) end of output. Running: ora2pg -p -t SEQUENCE_VALUES -o sequence_value.sql -b ./schema/sequence_values -c ./config/ora2pg.conf [2025-09-21 22:50:00] [========================>] 3/3 sequences (100.0%) end of output. Running: ora2pg -p -t TABLE -o table.sql -b ./schema/tables -c ./config/ora2pg.conf [2025-09-21 22:50:41] [========================>] 5/5 tables (100.0%) end of scanning. [2025-09-21 22:50:43] [========================>] 5/5 tables (100.0%) end of table export. Running: ora2pg -p -t PACKAGE -o package.sql -b ./schema/packages -c ./config/ora2pg.conf [2025-09-21 22:50:44] [========================>] 1/1 packages (100.0%) end of output. Running: ora2pg -p -t VIEW -o view.sql -b ./schema/views -c ./config/ora2pg.conf [2025-09-21 22:50:46] [========================>] 0/0 views (100.0%) end of output. Running: ora2pg -p -t GRANT -o grant.sql -b ./schema/grants -c ./config/ora2pg.conf Running: ora2pg -p -t TRIGGER -o trigger.sql -b ./schema/triggers -c ./config/ora2pg.conf [2025-09-21 22:51:14] [========================>] 2/2 triggers (100.0%) end of output. Running: ora2pg -p -t FUNCTION -o function.sql -b ./schema/functions -c ./config/ora2pg.conf [2025-09-21 22:51:15] [========================>] 1/1 functions (100.0%) end of functions export. Running: ora2pg -p -t PROCEDURE -o procedure.sql -b ./schema/procedures -c ./config/ora2pg.conf [2025-09-21 22:51:16] [========================>] 1/1 procedures (100.0%) end of procedures export. Running: ora2pg -p -t TABLESPACE -o tablespace.sql -b ./schema/tablespaces -c ./config/ora2pg.conf Running: ora2pg -p -t PARTITION -o partition.sql -b ./schema/partitions -c ./config/ora2pg.conf [2025-09-21 22:52:56] [========================>] 0/0 partitions (100.0%) end of output. Running: ora2pg -p -t TYPE -o type.sql -b ./schema/types -c ./config/ora2pg.conf [2025-09-21 22:52:57] [========================>] 1/1 types (100.0%) end of output. Running: ora2pg -p -t MVIEW -o mview.sql -b ./schema/mviews -c ./config/ora2pg.conf [2025-09-21 22:52:58] [========================>] 0/0 materialized views (100.0%) end of output. Running: ora2pg -p -t DBLINK -o dblink.sql -b ./schema/dblinks -c ./config/ora2pg.conf [2025-09-21 22:52:59] [========================>] 0/0 dblink (100.0%) end of output. Running: ora2pg -p -t SYNONYM -o synonym.sql -b ./schema/synonyms -c ./config/ora2pg.conf [2025-09-21 22:53:00] [========================>] 1/1 synonyms (100.0%) end of output. Running: ora2pg -p -t DIRECTORY -o directorie.sql -b ./schema/directories -c ./config/ora2pg.conf [2025-09-21 22:53:02] [========================>] 0/0 directory (100.0%) end of output. Running: ora2pg -t PACKAGE -o package.sql -b ./sources/packages -c ./config/ora2pg.conf [2025-09-21 22:53:03] [========================>] 1/1 packages (100.0%) end of output. Running: ora2pg -t VIEW -o view.sql -b ./sources/views -c ./config/ora2pg.conf [2025-09-21 22:53:06] [========================>] 0/0 views (100.0%) end of output. Running: ora2pg -t TRIGGER -o trigger.sql -b ./sources/triggers -c ./config/ora2pg.conf [2025-09-21 22:53:07] [========================>] 2/2 triggers (100.0%) end of output. Running: ora2pg -t FUNCTION -o function.sql -b ./sources/functions -c ./config/ora2pg.conf [2025-09-21 22:53:08] [========================>] 1/1 functions (100.0%) end of functions export. Running: ora2pg -t PROCEDURE -o procedure.sql -b ./sources/procedures -c ./config/ora2pg.conf [2025-09-21 22:53:09] [========================>] 1/1 procedures (100.0%) end of procedures export. Running: ora2pg -t PARTITION -o partition.sql -b ./sources/partitions -c ./config/ora2pg.conf [2025-09-21 22:53:35] [========================>] 0/0 partitions (100.0%) end of output. Running: ora2pg -t TYPE -o type.sql -b ./sources/types -c ./config/ora2pg.conf [2025-09-21 22:53:36] [========================>] 1/1 types (100.0%) end of output. Running: ora2pg -t MVIEW -o mview.sql -b ./sources/mviews -c ./config/ora2pg.conf [2025-09-21 22:53:37] [========================>] 0/0 materialized views (100.0%) end of output. To extract data use the following command: ora2pg -t COPY -o data.sql -b ./data -c ./config/ora2pg.conf [root@node1 test_project]#

5.2.2.2 Oracle 数据库中的数据导出

ora2pg -t COPY -o data.sql -b ./data -c ./config/ora2pg.conf

执行过程如下:


[root@node1 test_project]# ora2pg -t COPY -o data.sql -b ./data -c ./config/ora2pg.conf [2025-09-22 00:04:47] [========================>] 5/5 tables (100.0%) end of scanning. [2025-09-22 00:05:10] [========================>] 0/0 rows (100.0%) Table AUDIT_LOG (0 recs/sec) [2025-09-22 00:05:10] [> ] 0/18 total rows (0.0%) - (0 sec., avg: 0 recs/sec). [2025-09-22 00:05:10] [========================>] 5/5 rows (100.0%) Table DEPARTMENTS (5 recs/sec) [2025-09-22 00:05:10] [======> ] 5/18 total rows (27.8%) - (0 sec., avg: 5 recs/sec). [2025-09-22 00:05:10] [========================>] 5/5 rows (100.0%) Table EMPLOYEES (5 recs/sec) [2025-09-22 00:05:10] [=============> ] 10/18 total rows (55.6%) - (0 sec., avg: 10 recs/sec). [2025-09-22 00:05:10] [========================>] 5/5 rows (100.0%) Table SALARY_HISTORY (5 recs/sec) [2025-09-22 00:05:10] [====================> ] 15/18 total rows (83.3%) - (0 sec., avg: 15 recs/sec). [2025-09-22 00:05:11] [========================>] 3/3 rows (100.0%) Table TIMESTAMP_DEMO (3 recs/sec) [2025-09-22 00:05:11] [========================>] 18/18 total rows (100.0%) - (1 sec., avg: 18 recs/sec). [2025-09-22 00:05:11] [========================>] 18/18 rows (100.0%) on total estimated data (1 sec., avg: 18 recs/sec) Schema Export Complete

5.2.2.3 IvorySQL 数据库中创建数据库、用户、schema

ivorysql=# create database ywdb; CREATE DATABASE ivorysql=# \c ywdb You are now connected to database "ywdb" as user "ivorysql". ywdb=# create schema ywuser; CREATE SCHEMA ywdb=# create user ywuser with password 'ywuser'; CREATE ROLE ywdb=# alter user ywuser with superuser; ALTER ROLE

5.2.2.4 Oracle 数据库对象及数据导入 Ivorysql 数据库

sh import_all.sh -h 127.0.0.1 -p 5432 -d ywdb -o ywuser -U ivorysql -n ywuser

执行过程如下:


[root@node1 test_project]# sh import_all.sh -h 127.0.0.1 -p 5432 -d ywdb -o ywuser -U ywuser -n ywuser Database owner ywuser already exists, skipping creation. Would you like to drop the database ywdb before recreate it? [y/N/q] y Running: dropdb -h 127.0.0.1 -p 5432 -U ywuser ywdb Running: createdb -h 127.0.0.1 -p 5432 -U ywuser -E UTF8 --owner ywuser ywdb Would you like to create schema ywuser in database ywdb? [y/N/q] y Running: psql -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -c "CREATE SCHEMA ywuser;" CREATE SCHEMA Would you like to change search_path of the database owner? [y/N/q] y Running: psql -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -c "ALTER ROLE ywuser SET search_path TO ywuser,public;" ALTER ROLE Would you like to import TYPE from ./schema/types/type.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/types/type.sql SET CREATE TYPE Would you like to import SEQUENCE from ./schema/sequences/sequence.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/sequences/sequence.sql SET CREATE SEQUENCE CREATE SEQUENCE CREATE SEQUENCE Would you like to import SEQUENCE_VALUES from ./schema/sequence_values/sequence_value.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/sequence_values/sequence_value.sql SET ALTER SEQUENCE ALTER SEQUENCE ALTER SEQUENCE Would you like to import TABLE from ./schema/tables/table.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/tables/table.sql SET CREATE TABLE ALTER TABLE CREATE TABLE ALTER TABLE ALTER TABLE CREATE TABLE ALTER TABLE ALTER TABLE CREATE TABLE CREATE INDEX ALTER TABLE CREATE TABLE ALTER TABLE ALTER TABLE ALTER TABLE Would you like to import PACKAGE from ./schema/packages/package.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/packages/package.sql SET psql:schema/packages/package.sql:13: NOTICE: schema "employee_pkg" does not exist, skipping DROP SCHEMA CREATE SCHEMA psql:schema/packages/package.sql:16: ERROR: syntax error at or near "TYPE" LINE 1: CREATE OR REPLACE TYPE employee_pkg.emp_cursor AS REFCURSOR; ^ ERROR: an error occurs when importing file ./schema/packages/package.sql. [root@node1 test_project]#

package 导入错误,修改一下 package.sql 代码,解决办法如下:


vi ./schema/packages/package.sql #注释掉这行 --CREATE OR REPLACE TYPE employee_pkg.emp_cursor AS REFCURSOR; #下面的代码修改成这样: CREATE OR REPLACE FUNCTION employee_pkg.get_employees(dept_id INT) RETURNS SETOF employees LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT * FROM employees WHERE department_id = dept_id; END; $$; CREATE OR REPLACE FUNCTION employee_pkg.get_avg_salary(dept_id INT) RETURNS NUMERIC LANGUAGE plpgsql AS $$ DECLARE avg_salary NUMERIC; BEGIN SELECT AVG(salary) INTO avg_salary FROM employees WHERE department_id = dept_id; RETURN avg_salary; END; $$;

重新导入数据库对象和数据,如下:


[root@node1 test_project]# sh import_all.sh -h 127.0.0.1 -p 5432 -d ywdb -o ywuser -U ywuser -n ywuser Database owner ywuser already exists, skipping creation. Would you like to drop the database ywdb before recreate it? [y/N/q] y Running: dropdb -h 127.0.0.1 -p 5432 -U ywuser ywdb Running: createdb -h 127.0.0.1 -p 5432 -U ywuser -E UTF8 --owner ywuser ywdb Would you like to create schema ywuser in database ywdb? [y/N/q] y Running: psql -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -c "CREATE SCHEMA ywuser;" CREATE SCHEMA Would you like to change search_path of the database owner? [y/N/q] y Running: psql -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -c "ALTER ROLE ywuser SET search_path TO ywuser,public;" ALTER ROLE Would you like to import TYPE from ./schema/types/type.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/types/type.sql SET CREATE TYPE Would you like to import SEQUENCE from ./schema/sequences/sequence.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/sequences/sequence.sql SET CREATE SEQUENCE CREATE SEQUENCE CREATE SEQUENCE Would you like to import SEQUENCE_VALUES from ./schema/sequence_values/sequence_value.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/sequence_values/sequence_value.sql SET ALTER SEQUENCE ALTER SEQUENCE ALTER SEQUENCE Would you like to import TABLE from ./schema/tables/table.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/tables/table.sql SET CREATE TABLE ALTER TABLE CREATE TABLE ALTER TABLE ALTER TABLE CREATE TABLE ALTER TABLE ALTER TABLE CREATE TABLE CREATE INDEX ALTER TABLE CREATE TABLE ALTER TABLE ALTER TABLE ALTER TABLE Would you like to import PACKAGE from ./schema/packages/package.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/packages/package.sql SET psql:schema/packages/package.sql:13: NOTICE: schema "employee_pkg" does not exist, skipping DROP SCHEMA CREATE SCHEMA CREATE FUNCTION CREATE FUNCTION Would you like to import TRIGGER from ./schema/triggers/trigger.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/triggers/trigger.sql SET psql:schema/triggers/trigger.sql:9: NOTICE: trigger "dml_audit_trigger" for relation "employees" does not exist, skipping DROP TRIGGER CREATE FUNCTION CREATE TRIGGER psql:schema/triggers/trigger.sql:39: NOTICE: trigger "salary_audit_trigger" for relation "employees" does not exist, skipping DROP TRIGGER CREATE FUNCTION CREATE TRIGGER Would you like to import FUNCTION from ./schema/functions/function.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/functions/function.sql SET CREATE FUNCTION Would you like to import PROCEDURE from ./schema/procedures/procedure.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/procedures/procedure.sql SET CREATE PROCEDURE Would you like to import SYNONYM from ./schema/synonyms/synonym.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/synonyms/synonym.sql SET CREATE VIEW Would you like to process indexes and constraints before loading data? [y/N/q] y Would you like to import TRIGGER from ./schema/triggers/trigger.sql? [y/N/q] y Running: psql --single-transaction -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/triggers/trigger.sql SET DROP TRIGGER CREATE FUNCTION CREATE TRIGGER DROP TRIGGER CREATE FUNCTION CREATE TRIGGER Would you like to import TABLESPACE from ./schema/tablespaces/tablespace.sql? [y/N/q] n Would you like to import data from ./data/data.sql? [y/N/q] y Running: psql -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./data/data.sql BEGIN SET SET COPY 0 SET SET psql:data/data.sql:21: ERROR: malformed record literal: "({"(?oo?°?è·ˉ100??·,????o?,100000)"})" DETAIL: Too few columns. CONTEXT: COPY departments, line 1, column location: "({"(?oo?°?è·ˉ100??·,????o?,100000)"})" psql:data/data.sql:23: ERROR: current transaction is aborted, commands ignored until end of transaction block psql:data/data.sql:24: ERROR: current transaction is aborted, commands ignored until end of transaction block psql:data/data.sql:27: ERROR: current transaction is aborted, commands ignored until end of transaction block psql:data/data.sql:33: error: invalid command \. psql:data/data.sql:35: ERROR: syntax error at or near "1" LINE 1: 1 张 明 zhang.ming@example.com 2020-01-15 00:00:00 1 8000 ^ psql:data/data.sql:36: ERROR: current transaction is aborted, commands ignored until end of transaction block psql:data/data.sql:39: ERROR: current transaction is aborted, commands ignored until end of transaction block psql:data/data.sql:45: error: invalid command \. psql:data/data.sql:47: ERROR: syntax error at or near "1" LINE 1: 1 1 7500 8000 2023-01-01 00:00:00 ^ psql:data/data.sql:48: ERROR: current transaction is aborted, commands ignored until end of transaction block psql:data/data.sql:51: ERROR: current transaction is aborted, commands ignored until end of transaction block psql:data/data.sql:55: error: invalid command \. psql:data/data.sql:57: ERROR: syntax error at or near "1" LINE 1: 1 第一条记录 2023-10-01 08:00:00.123456 2025-09-20 16:07:00.... ^ Ora2Pg ending [root@node1 test_project]#

导入数据时报错,因为涉及到自定义 type 类型,解决办法:


--ORACLE端导出数据 SET pagesize 0 SET feedback off SET linesize 1000 SPOOL departments.csv SELECT department_id || ',' || '"' || REPLACE(department_name, '"', '""') || '"' || ',' || '"' || REPLACE(d.location.street, '"', '""') || '"' || ',' || '"' || REPLACE(d.location.city, '"', '""') || '"' || ',' || '"' || d.location.zip_code || '"' FROM departments d; SPOOL OFF --departments.csv传输到迁移工具所在主机上 scp departments.csv 192.*.*.60:/data/migration/test_project/data/ --Ivorysql端导入数据 --1、先创建临时表 ywdb=# CREATE TABLE temp_departments ( ywdb(# department_id INTEGER, ywdb(# department_name VARCHAR(100), ywdb(# street VARCHAR(100), ywdb(# city VARCHAR(100), ywdb(# zip_code VARCHAR(20) ywdb(# ); CREATE TABLE --2、把csv文件中的数据先导入临时表 ywdb=# COPY temp_departments(department_id, department_name, street, city, zip_code) ywdb-# FROM '/data/migration/test_project/data/departments.csv' ywdb-# WITH (FORMAT CSV); COPY 5 --3、把临时表中的数据经过格式转换后插入正式表 ywdb=# INSERT INTO departments(department_id, department_name, location) ywdb-# SELECT ywdb-# department_id, ywdb-# department_name, ywdb-# (street, city, zip_code)::address_type ywdb-# FROM temp_departments; INSERT 0 5 ywdb=# SELECT * FROM departments; department_id | department_name | location ---------------+-----------------+---------------------------------- 1 | 人力资源 | (人民路100号,北京,"100000 ") 2 | 财务部 | (金融大街88号,上海,"200000 ") 3 | 技术研发 | (科技园路1号,深圳,"518000 ") 4 | 市场营销 | (商业中心广场,广州,"510000 ") 5 | 客户服务 | (服务大道5号,杭州,"310000 ") (5 rows) --4、删除临时表 ywdb=# DROP TABLE temp_departments; DROP TABLE

修改 ./data/data.sql,去掉 departments 表数据后,继续导入其他表数据:

psql -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./data/data.sql

执行过程如下:


[root@node1 test_project]# psql -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./data/data.sql BEGIN SET SET COPY 0 SET SET SET SET COPY 5 SET SET COPY 5 SET SET COPY 3 COMMIT [root@node1 test_project]#

至此,Oracle 中的全部对象和数据全部导入 IvorySQL 数据库。

5.3 迁移后校验

5.3.1 对象校验

--Oracle数据库中对象 SQL> Select object_name,object_type from dba_objects where owner='YWUSER' OBJECT_NAME OBJECT_TYPE -------------------------------------------------- ------------------------------ ADDRESS_TYPE TYPE DEPARTMENTS TABLE SYS_C008164 INDEX EMPLOYEES TABLE SYS_C008166 INDEX AUDIT_LOG TABLE SYS_C008168 INDEX SALARY_HISTORY TABLE SYS_C008169 INDEX TIMESTAMP_DEMO TABLE SYS_C008171 INDEX EMPLOYEES_SEQ SEQUENCE DEPARTMENTS_SEQ SEQUENCE SALARY_HISTORY_SEQ SEQUENCE EMP_SYN SYNONYM GET_DEPARTMENT_ADDRESS FUNCTION INCREASE_SALARY PROCEDURE EMPLOYEE_PKG PACKAGE BODY EMPLOYEE_PKG PACKAGE SALARY_AUDIT_TRIGGER TRIGGER DML_AUDIT_TRIGGER TRIGGER IDX_SALARY_HISTORY INDEX 22 rows selected. --Ivorysql数据库中对象 ywdb=# SELECT n.nspname, 'TABLE', c.relname, pg_get_userbyid(c.relowner) ywdb-# FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ywdb-# WHERE c.relkind = 'r' ywdb-# and n.nspname='ywuser' ywdb-# UNION ALL ywdb-# SELECT n.nspname, 'INDEX', c.relname, pg_get_userbyid(c.relowner) ywdb-# FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ywdb-# WHERE c.relkind = 'i' ywdb-# and n.nspname='ywuser' ywdb-# UNION ALL ywdb-# SELECT n.nspname, 'SEQUENCE', c.relname, pg_get_userbyid(c.relowner) ywdb-# FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ywdb-# WHERE c.relkind = 'S' ywdb-# and n.nspname='ywuser' ywdb-# UNION ALL ywdb-# SELECT n.nspname, 'VIEW', c.relname, pg_get_userbyid(c.relowner) ywdb-# FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ywdb-# WHERE c.relkind = 'v' ywdb-# and n.nspname='ywuser' ywdb-# UNION ALL ywdb-# SELECT n.nspname, 'MATERIALIZED_VIEW', c.relname, pg_get_userbyid(c.relowner) ywdb-# FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ywdb-# WHERE c.relkind = 'm' ywdb-# and n.nspname='ywuser' ywdb-# UNION ALL ywdb-# SELECT n.nspname, 'FUNCTION', p.proname, pg_get_userbyid(p.proowner) ywdb-# FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace ywdb-# WHERE p.prokind = 'f' ywdb-# and n.nspname='ywuser' ywdb-# UNION ALL ywdb-# SELECT n.nspname, 'PROCEDURE', p.proname, pg_get_userbyid(p.proowner) ywdb-# FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace ywdb-# WHERE p.prokind = 'p' ywdb-# and n.nspname='ywuser' ywdb-# UNION ALL ywdb-# SELECT n.nspname, 'TRIGGER', t.tgname, pg_get_userbyid(c.relowner) ywdb-# FROM pg_trigger t ywdb-# JOIN pg_class c ON c.oid = t.tgrelid ywdb-# JOIN pg_namespace n ON n.oid = c.relnamespace ywdb-# WHERE NOT t.tgisinternal ywdb-# and n.nspname='ywuser'; nspname | ?column? | relname | pg_get_userbyid ---------+-----------+----------------------------------+----------------- ywuser | TABLE | audit_log | ywuser ywuser | TABLE | timestamp_demo | ywuser ywuser | TABLE | departments | ywuser ywuser | TABLE | employees | ywuser ywuser | TABLE | salary_history | ywuser ywuser | INDEX | audit_log_pkey | ywuser ywuser | INDEX | timestamp_demo_pkey | ywuser ywuser | INDEX | departments_pkey | ywuser ywuser | INDEX | employees_pkey | ywuser ywuser | INDEX | idx_salary_history | ywuser ywuser | INDEX | salary_history_pkey | ywuser ywuser | SEQUENCE | departments_seq | ywuser ywuser | SEQUENCE | employees_seq | ywuser ywuser | SEQUENCE | salary_history_seq | ywuser ywuser | VIEW | emp_syn | ywuser ywuser | FUNCTION | get_department_address | ywuser ywuser | FUNCTION | trigger_fct_dml_audit_trigger | ywuser ywuser | FUNCTION | trigger_fct_salary_audit_trigger | ywuser ywuser | PROCEDURE | increase_salary | ywuser ywuser | TRIGGER | salary_audit_trigger | ywuser ywuser | TRIGGER | dml_audit_trigger | ywuser (21 rows) ywdb=# \dT List of data types Schema | Name | Description --------+--------------+------------- ywuser | address_type | (1 row) ywdb=#

经比对,Oracle 数据库中的对象和 IvorySQL 中的对象一致,都是 22 个。但是需要注意的是,Oracle 数据库中的同义词迁移到 IvorySQL 数据库之后是以 view 的方式存在的。

5.3.2 数据校验

--Oracle数据库中的数据 SQL> Select 'DEPARTMENTS',count(*) from YWUSER.DEPARTMENTS 2 union all 3 Select 'EMPLOYEES',count(*) from YWUSER.EMPLOYEES 4 union all 5 Select 'AUDIT_LOG',count(*) from YWUSER.AUDIT_LOG 6 union all 7 Select 'SALARY_HISTORY',count(*) from YWUSER.SALARY_HISTORY 8 union all 9 Select 'TIMESTAMP_DEMO',count(*) from YWUSER.TIMESTAMP_DEMO; 'DEPARTMENTS' COUNT(*) ------------------------------------------ ---------- DEPARTMENTS 5 EMPLOYEES 5 AUDIT_LOG 0 SALARY_HISTORY 5 TIMESTAMP_DEMO 3 SQL> --Ivorysql数据库中的数据 ywdb=# Select 'DEPARTMENTS',count(*) from YWUSER.DEPARTMENTS ywdb-# union all ywdb-# Select 'EMPLOYEES',count(*) from YWUSER.EMPLOYEES ywdb-# union all ywdb-# Select 'AUDIT_LOG',count(*) from YWUSER.AUDIT_LOG ywdb-# union all ywdb-# Select 'SALARY_HISTORY',count(*) from YWUSER.SALARY_HISTORY ywdb-# union all ywdb-# Select 'TIMESTAMP_DEMO',count(*) from YWUSER.TIMESTAMP_DEMO; ?column? | count ----------------+------- DEPARTMENTS | 5 EMPLOYEES | 5 AUDIT_LOG | 1 <----这里不一致,是因为Oracle数据库中是先在表中插入数据,最后创建的触发器,IvorySQL数据库中是先创建的触发器,最后导入的数据,触发器生效了,这个完全可以忽略。 SALARY_HISTORY | 5 TIMESTAMP_DEMO | 3 (5 rows)

经比对,Oracle 数据库中的对象和 IvorySQL 中的数据记录数一致。

在生产中如果需要更详细的数据一致性校验,需要业务同事配合完成。

也可以按照对象类型单独迁移对象及,可以自行尝试下。下面是简单示例:

(1)、导出表 ddl


ora2pg -t TABLE -b /data/migration/test_project/schema/tables -o ddl.sql -c /etc/ora2pg/ora2pg.conf

执行过程如下:


[root@node1 ora2pg]# ora2pg -t TABLE -b /data/migration/test_project/schema/tables -c /etc/ora2pg/ora2pg.conf [2025-09-21 20:52:46] [========================>] 5/5 tables (100.0%) end of scanning. [2025-09-21 20:53:39] [========================>] 5/5 tables (100.0%) end of table export. [root@node1 ora2pg]#

迁移后在/data/migration/test_project/schema/tables 目录下生成 ddl.sql 文件,里面保存着表结构、主外键、约束、index 信息。

(2)、导出表数据


ora2pg -t copy -b /data/migration/test_project/schema/tables -o data.sql -c /etc/ora2pg/ora2pg.conf

执行过程如下:


[root@node1 tables]# ora2pg -t copy -b /data/migration/test_project/schema/tables -o data.sql -c /etc/ora2pg/ora2pg.conf [2025-09-21 22:17:49] [========================>] 5/5 tables (100.0%) end of scanning. [2025-09-21 22:19:19] [========================>] 0/0 rows (100.0%) Table AUDIT_LOG (0 recs/sec) [2025-09-21 22:19:19] [> ] 0/18 total rows (0.0%) - (0 sec., avg: 0 recs/sec). [2025-09-21 22:19:20] [========================>] 5/5 rows (100.0%) Table DEPARTMENTS (5 recs/sec) [2025-09-21 22:19:20] [======> ] 5/18 total rows (27.8%) - (1 sec., avg: 5 recs/sec). [2025-09-21 22:19:20] [========================>] 5/5 rows (100.0%) Table EMPLOYEES (5 recs/sec) [2025-09-21 22:19:20] [=============> ] 10/18 total rows (55.6%) - (1 sec., avg: 10 recs/sec). [2025-09-21 22:19:20] [========================>] 5/5 rows (100.0%) Table SALARY_HISTORY (5 recs/sec) [2025-09-21 22:19:20] [====================> ] 15/18 total rows (83.3%) - (1 sec., avg: 15 recs/sec). [2025-09-21 22:19:20] [========================>] 3/3 rows (100.0%) Table TIMESTAMP_DEMO (3 recs/sec) [2025-09-21 22:19:20] [========================>] 18/18 total rows (100.0%) - (1 sec., avg: 18 recs/sec). [2025-09-21 22:19:20] [========================>] 18/18 rows (100.0%) on total estimated data (1 sec., avg: 18 recs/sec) Schema Export Complete [root@node1 tables]#

迁移后在/data/migration/test_project/schema/tables 目录下生成 data.sql 文件,里面保存着表数据。

(3)、导出其他对象


ora2pg -t SEQUENCE,TRIGGER,FUNCTION,PROCEDURE,PROCEDURE,PACKAGE -b /data/migration/test_project/schema/ -c /etc/ora2pg/ora2pg.conf

(4)、导入 ivorysql

psql --single-transaction  -h 127.0.0.1 -p 5432 -U ywuser -d ywdb -f ./schema/******.sql

6. 总结

Oracle 数据库的表中有自定义类型时,数据导入 IvorySQL 数据库时需要手工处理。Oracle 19c 数据库迁移到 IvorySQL 4.6 的整个过程还是比较简单的。

参考文章:https://ora2pg.darold.net/documentation.html

关于作者

  • 网名:飞天,墨天轮 2024 年度优秀原创作者,拥有 Oracle 10g OCM 认证、PGCE 认证、MySQL 8.0 OCP 认证以及 OBCA、KCP、KCSM、ACP、YCP、磐维等众多国产数据库认证证书,目前从事 Oracle、Mysql、PostgresSQL、磐维数据库管理运维工作,喜欢结交更多志同道合的朋友,热衷于研究、分享数据库技术。
  • 微信公众号:飞天 online
  • 墨天轮:https://www.modb.pro/u/15197

如有任何疑问,欢迎大家留言,共同探讨~~~

无人机蜂群联合指挥作战技术详解 随着人工智能、通信技术、新材料等领域的不断进步,无人机蜂群联合指挥作战技术将迎来更加广阔的发展前景。未来,无人机蜂群将更加智能化、自主化,能够执行更加复杂、精细的作战任务。同时,加强国际合作,共同制定国际规则和标准,将是推动无人机蜂群技术健康发展的重要方向。无人机蜂群技术,是指通过先进的通信技术、自主导航与决策算法,将大量小型、低成本、功能各异的无人机组织成一个高度协同的群体,执行复杂多样的军事任务。2. 通信与数据链:采用高速、低延迟、抗干扰的通信技术,保障群体内无人机间的信息交换。 阅读详情

相关推荐

用Python玩转共现分析:从NLTK到热力图可视化(附完整代码)

本文详细介绍了如何使用Python进行文本共现分析,从NLTK基础文本处理到构建共现矩阵,并实现热力图可视化。通过完整的代码示例,帮助数据分析师和开发者掌握这一揭示词语间关联网络的实用NLP技术,快速从文本中提取语义关系和主题结构。

weixin_29313817的博客 352

PostgreSQL笔记4: 市场地位、生态全景与行业应用实践

技术能力实现方式适用场景全文检索tsvectortsquery+ GIN 索引内容搜索、日志分析半结构化数据JSONB类型 + GIN 索引灵活 Schema、配置存储向量检索pgvector扩展 + IVFFlat/HNSW 索引RAG、推荐系统、语义搜索混合查询上述能力的 SQL 组合复杂业务场景的统一数据访问表结构创建(若已存在则跳过)插入三条测试数据(包含 JSONB 和向量字段)全文检索查询(tsvectortsqueryJSONB 包含查询(@>运算符)

Wang的专栏 524

Vim 入门到够用:Linux 文本编辑器的三种模式、常用操作与实战技巧

i 进入编辑模式Esc 退出编辑模式回到命令模式: 从命令模式进入底行模式编辑,不一定非得“打开就直接打字”。一旦你接受了“模式”这件事,后面很多操作都会顺起来。学 Vim,不要追求一次学全,先把“命令模式—编辑模式—底行模式”这条主线练熟。只要这条主线通了,后面的复制、删除、搜索、替换,都是顺手补上去的能力。

Web安全工具库 804

177.Agent-LangGraph-MCP的构成说明

MCP架构通过MCP主机、客户端与服务器的协同,实现工具、资源与提示词的统一管理。MCP服务器提供Tools、Resources、Prompts能力,MCP客户端则负责发现、调用与读取,使Agent可按需获取上下文。其核心在于将工具从Agent中抽离,封装为可复用的服务,提升灵活性与可维护性,推动AI应用高效协作。

计算机王的博客 291

【论文阅读】2021-(Pidinet)Pixel Difference Networks for Efficient Edge Detection

文章目录摘要1. Introduction 摘要 motivations: 基于CNN的边缘检测的高性能是通过一个大的预训练CNN主干来实现的,该主干占用大量内存和能量。传统边缘检测器(如Canny、Sobel和LBP)很少被研究。 contributions: 提出了一种简单、轻量级但有效的架构,称为像素差分网络(PiDiNet),用于有效的边缘检测。PiDiNet采用了新颖的像素差卷积,将传统的边缘检测算子集成到现代CNN中流行的卷积运算中,以增强任务性能。 experiments: 在BSDS500

轻舟的万重重山 1万+

Sentinel2 哨兵2数据下载方法(USGS)-史上最全讲解

本文讲述了从USGS官网下载Sentinel2的方法。主要包括USGS官网的注册和下载流程,以及选定研究区中所出现的问题、解决办法。

xiatiandexia123的博客 6万+

Docker Compose:搭建本地多服务开发环境

从一个 Java API、PostgreSQL 和 Redis 开始,配置服务名、端口、环境变量、健康检查、数据卷和依赖关系。理解 Compose 文件如何被解析、项目如何隔离,以及哪些数据不能依赖“重建容器”保留,能够把个人开发环境整理成团队可复用的启动入口。

m0_62221377的博客 477

损失函数大汇总(四十五)(clDice Loss附公式推导与代码)

clDice Loss是一种针对细长结构分割任务的拓扑保持损失函数,通过比较预测区域和真实区域的中心线重叠情况来评估分割质量。与传统Dice Loss关注区域重叠不同,clDice Loss重点考察连通性和骨架拓扑结构,能有效避免血管断裂、分支错误连接等问题。该损失函数包含两个核心指标:预测骨架在真实区域中的覆盖比例(Topology Precision)和真实骨架被预测区域覆盖的比例(Topology Sensitivity),通过调和平均得到最终评估值。由于传统骨架提取算法不可导,实际实现采用可微的so

悦学共鸣,温柔以待,汇聚光芒,共同成长 452

AI全栈实战 | 3.9-01 爬虫入门:requests 加 BeautifulSoup 三分钟跑通,但 90% 的人栽在这 4 个“礼貌“问题上

⚠️:本文内容仅用于。请遵守目标站点的服务条款与相关法律法规,不得对目标服务造成负担,不得采集、传播个人隐私数据。技术是中性的,边界在你手里。爬虫的入门技术(requests 发请求 + BeautifulSoup 解析)三分钟就能学会。真正区分"会用爬虫"和"会做爬虫的人"的,是这篇我们跑通"请求→解析→存储"的最小闭环,然后重点把"合规与礼貌"这根弦给你绷上。

源码获取 | 运行部署 | 定制二开修改,如果回复不及时请耐心等待,请见谅。 436

【定位模块算法】amcl(蒙特卡洛)粒子滤波定位算法(附C++代码)

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言(1)amcl定位框架介绍(2)amcl定位框架组成(1)ROS-TF坐标转换树【ROS的工具】(2)robot_pose_ekf扩展卡尔曼位姿估计算法(3)定位amcl自适应蒙特卡洛定位,adaptive monte carlo localizaion**(3)amcl定位节点的订阅与发布(4)amcl定位功能包用法(1)编写amcl节点相关的launch(amcl_omni.l

qq_35635374的博客 4403

基于STM32+OneNet设计的物联网智慧路灯(100)

本次设计的智能路灯采用STM32芯片和OneNet物联网平台进行连接,开发出支持太阳能充电锂电池供电、GPS定位上传位置信息、手动/自动控制路灯开关、环境光照调节、红外传感器检测周围人员、空气质量监测等多项功能。同时,当路灯发生异常情况时,系统可以自动发送邮件或短信到指定邮箱或电话。

7729

【基于 WebSocket 的多用户网页五子棋】—— 数据管理模块

但行好事,莫问前程

qq_42996461的博客 251

汇川Easy系列斜坡函数功能块(支持状态输出)

IN rSetValue REAL 0.000000 0.000000 不保持 目标值 1IN rMaxValue REAL 0.000000 0.000000 不保持 最大目标值 2IN rMinValue REAL 0.000000 0.000000 不保持 最小目标值 3IN rSmoothTime REAL 100.000000 100.0000 不保持 最小到最大值平滑时间 单位 秒 4IN diTs DINT 10 10 不保持 函数调用周期采样时间中断时间 5。

RXXW_Dor的博客 463

OpenClaw 深度调查:把 AI 助手做成你自己的控制平面

对 openclaw/openclaw 的完整技术尽调:Gateway 架构、Agent 运行时、工具与沙箱、30 个消息通道、181 个插件、安全信任边界、工程代价与采用建议。17 章 / 10 张图,全部数字标注口径,数据截至 2026-10-31。

厚积薄发 308

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

IDEA 简介:从零认识 IntelliJ IDEA

IDEA 全称,是由 JetBrains 公司开发的一款主流 Java 集成开发环境(IDE)。它集代码编辑、编译、调试、测试、版本控制、构建管理等功能于一体,被大量 Java 开发者视为日常开发的首选工具。所谓 IDE(Integrated Development Environment,集成开发环境),就是把写代码所需的各类工具整合到同一个界面中。相比只用文本编辑器加命令行编译,IDE 能显著减少重复操作,让开发者把精力集中在业务逻辑上。

ztI10的博客 179

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

TransactionInactiveError:await 之后事务已经不活跃了

TransactionInactiveError 出现于 IndexedDB 事务已结束(非 active 状态)时调用 put、get 等操作。常见于 await 后或 setTimeout 中执行请求,因任务已完成,事务已提交或进入 finished 状态。请求必须在创建事务的同一任务中挂起,否则无效。解决方法:在 onsuccess 回调内同步发起后续操作,或提前将所有请求挂入事务。避免在异步上下文中使用事务操作,确保请求在事务活跃期内完成。

qq_40822000的博客 159

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Docker部署linkding:HTTPS与数据备份

部署私人书签库,除了让页面能够打开,还需要考虑几个实际问题:重建容器后数据是否保留、登录和保存是否正常、升级前怎样备份,以及出现 502 或 CSRF 错误时从哪里检查。本文使用 Docker Compose 部署 linkding,采用默认 SQLite 数据库,通过 Caddy 提供 HTTPS,并完成账户创建、数据持久化验证和备份。如果重建后账户或书签消失,应先检查是否换了项目目录、改了挂载路径,或者启动了另一套实例,不要急着重新初始化。遇到权限问题,应根据日志确认具体目录和运行用户,再调整权限。

tedcloud123的博客 172

云计算课程小组PPT汇报任务书

36人分为6组,每组6人;形式:生活案例调研 + 理论解读;汇报时长7‑9min;PPT建议10‑14页。

Michelle8023的博客 208

大表 ALTER TABLE 怎么做?NineData OnlineDDL 配置与执行实践

介绍NineData OnlineDDL大表变更实践,涵盖执行策略、切表控制、拷贝批次与可视化进度追踪。

dbasenewbie的博客 295

软件项目交付时,源代码之外还应该交付哪些技术资料?

软件项目交付时,除可运行程序外,还应核对源代码、数据库结构、接口文档、部署运维、测试验收和账号资产。本文从实际交付环节拆解完整资料清单,并说明合同如何约定交付范围、知识产权与维护边界。

chengduyoushu的博客 306

Axum 初步入门学习笔记

)自定义错误实现后,可以直接作为Result的ErrAxum 是一个构建在 Tokio、Tower、Hyper 之上的 Rust Web 框架,核心能力是:Router 路由、Extractor 提取器、Tower 中间件、IntoResponse 响应机制、State 共享状态。那张分层架构图是“地图”不是“说明书”:它帮你建立全局观,但 Service、Repository、数据库、Redis、MQ 都是你自己在 Handler 内部调用的业务/基础设施层,不是 Axum 自带的。

weixin_43219667的博客 193

MySQL报Too many open files?句柄耗尽的排查与参数配置

文章讲清 max_connections 与 open_files_limit 两个额度、句柄被谁占走、分区表为什么最容易踩,给出从 error log 到 /proc/pid/limits 的定位顺序与句柄预算算法。

「一个在数据库赛道上边学边分享的学妹」——聚焦国产数据库/信创方向、分享学习笔记、踩坑经验和行业观察。 336

SQL Server 数据库双备份:简化配置,兼顾本地留存与异机归档

本文针对中小企业SQLServer数据库备份难题,提出简化配置与本地+异机双副本方案。传统原生备份依赖手动脚本,存在配置复杂、传输易中断、日志分散、静默失败等问题。推荐使用图形化工具实现备份、本地留存与异机同步一体化管理,支持断点续传、加密传输、自动清理与版本控制,降低运维负担。双副本策略提升数据安全性,本地快速恢复,异地防硬件故障。建议定期验证恢复流程,确保备份可用性。

2501_93776716的博客 309

Agent 安全实战:间接提示注入的四个攻击面与防御体系

当 Agent 从 "生成内容" 走向 "执行动作",安全边界就从内容合规变成了权限越界。本文拆解间接提示注入在网页抓取、工具返回值、MCP 连接器、长期记忆四个位置的真实攻击路径,对照 Claude、GPT、Gemini 在工具调用权限模型上的设计差异,给出可直接落地的五层防御体系。

阡陌数智,专注大模型网关与 LLM 工程落地 281

Redis 入门详解:核心特性、重要文件与全局命令实战

本文系统介绍了Redis的核心特性、重要文件及全局命令。Redis凭借内存存储、C语言实现和单线程设计,具备极高的读写性能;支持持久化(RDB/AOF)、主从复制与集群架构,适用于缓存、排行榜、计数器等场景,但不适合存储海量冷数据。文中还解析了常见面试题,如为何快速、单线程原理、持久化机制、KEYS *禁用原因及过期键处理策略,强调其在高并发场景下的优势与局限。

2303_77004089的博客 206

【FDE前沿部署工程师实战教程】第62章:Enterprise AI Developer Platform——企业级 AI 开发者平台与低代码 AI 应用工厂

企业从AI能力平台迈向AI应用生产平台,需构建统一的企业级AI开发者平台,将模型、RAG、Agent、Workflow等能力封装为可视化、可复用的开发工具。通过PromptIDE、AgentBuilder、WorkflowBuilder等模块,实现低代码与专业代码协同,降低开发门槛。平台覆盖应用全生命周期管理,集成测试评估、安全治理与CI/CD发布体系,推动AI应用规模化、标准化交付。最终形成“需求→设计→开发→测试→发布→运营”的企业AI应用工厂模式,助力制造等行业的智能化转型。

IT古董 315
上一篇: 数据库内核的降维观测方法
下一篇: 使用 PostgreSQL 时间点恢复(Point-In-Time Recovery)的多种数据恢复技术
IvorySQL
博客等级 码龄5年 745粉丝 566原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值