Oracle到PostgreSQL迁移实战:语法差异、工具选型与避坑指南
1. 项目概述:为什么我们需要一本语法迁移手册?
如果你正在负责一个将核心业务系统从 Oracle 迁移到 PostgreSQL 的项目,或者正在评估这种可能性,那么你大概率已经感受到了压力。这不仅仅是换个数据库那么简单,它更像是一次“器官移植”——两个系统虽然都遵循 SQL 标准,但各自的“方言”、功能特性和运行机制有着深刻的差异。直接运行 Oracle 的 SQL 脚本在 PostgreSQL 上,几乎百分之百会报错。市面上虽然有一些自动化迁移工具,但它们往往只能处理 70%-80% 的语法转换,剩下的那些“硬骨头”——比如复杂的 PL/SQL 逻辑、特定的日期处理函数、独有的并发控制机制——都需要靠人工去啃。
这就是为什么我们需要一本详尽的、由一线工程师编写的《语法迁移手册》。它不是一个简单的函数对照表,而是一套融合了原理、场景、避坑经验和实操验证的方法论。我经历过多次从 Oracle 到 PostgreSQL 的迁移,从最初的小心翼翼到后来的驾轻就熟,中间踩过的坑、总结的技巧,远比官方文档来得直接和实用。本手册的目的,就是将这些经验系统化,让你在迁移过程中,不仅能知道“怎么改”,更能理解“为什么要这样改”,从而做出更优的设计决策,确保迁移后的系统不仅能用,而且高效、稳定。
2. 迁移前的核心评估与准备工作
在动第一行代码之前,充分的评估和准备是项目成功的基石。盲目开始迁移,往往会陷入“改不完的语法错误”和“理不清的逻辑依赖”的泥潭。
2.1 深度扫描与差异分析
首先,你需要对你的 Oracle 数据库进行一次全面的“体检”。这远不止是导出表结构。
1. 对象与依赖关系梳理:使用DBMS_METADATA.GET_DDL或类似工具,批量获取所有数据库对象的定义,包括:
- 表、视图、物化视图:注意带有
WITH READ ONLY的视图在 PostgreSQL 中对应的是普通视图,其“只读”是逻辑上的。 - 序列(Sequence):Oracle 和 PostgreSQL 都支持序列,但默认行为和缓存机制不同。需要检查序列的
START WITH、INCREMENT BY、CACHE等属性。 - 函数、存储过程、包(Package):这是迁移的重灾区。需要逐行分析 PL/SQL 代码,识别其中使用的 Oracle 特有函数、系统包(如
DBMS_OUTPUT,DBMS_JOB,UTL_FILE)、以及隐式游标等特性。 - 触发器(Trigger):注意触发器的触发时机(
BEFORE/AFTER)、触发事件(INSERT/UPDATE/DELETE)以及行级与语句级触发的区别。特别要检查触发器体内是否引用了:NEW和:OLD伪记录,在 PostgreSQL 中它们对应NEW和OLD,但访问方式略有不同(如NEW.column_name)。 - 同义词(Synonym):PostgreSQL 没有同义词概念,通常需要转换为视图,或者直接在应用层修改连接和对象引用。
- 作业(Job):Oracle 的
DBMS_JOB或DBMS_SCHEDULER需要迁移到 PostgreSQL 的pg_cron扩展或外部任务调度系统(如 Apache Airflow)。
2. SQL 语句收集与分析:通过数据库的 AWR 报告、SQL 追踪(v$sql)或应用日志,收集高频、核心的 SQL 语句。重点关注:
- 分页查询:Oracle 的
ROWNUM和ROW_NUMBER()与 PostgreSQL 的LIMIT/OFFSET或ROW_NUMBER() OVER()写法不同。 - 层次查询:Oracle 的
CONNECT BY在 PostgreSQL 中需要使用递归公共表表达式(Recursive CTE)重写。 - 外连接语法:Oracle 的
(+)运算符必须改为标准的LEFT/RIGHT/FULL JOIN。 - MERGE 语句:Oracle 的
MERGE在 PostgreSQL 15 及以上版本有原生支持,但语法微调。低版本需用INSERT ... ON CONFLICT DO UPDATE模拟。
3. 数据类型映射确认:这是基础但容易出错的一环。例如:
- VARCHAR2 -> VARCHAR/TEXT:通常直接映射为
VARCHAR(指定长度)或TEXT(不限长度)。注意 Oracle 的VARCHAR2最大 4000 字节(32k in extended),而 PostgreSQL 的TEXT几乎无限制。 - NUMBER -> NUMERIC/DECIMAL:对于需要精确小数运算的字段,使用
NUMERIC(p,s)。对于整数,可考虑INT,BIGINT。 - DATE/TIMESTAMP -> TIMESTAMP:Oracle 的
DATE包含日期和时间,更接近 PostgreSQL 的TIMESTAMP(0)。Oracle 的TIMESTAMP可对应TIMESTAMP或TIMESTAMPTZ(带时区)。 - RAW/BLOB -> BYTEA:二进制数据。
- CLOB/NCLOB -> TEXT:大文本。
- ROWID:Oracle 的物理行地址,PostgreSQL 没有直接对应物。如果应用依赖
ROWID做快速访问,需要重构逻辑,通常用主键替代。
2.2 工具选型与环境搭建
“工欲善其事,必先利其器”。选择合适的工具能极大提升效率。
1. 评估自动化迁移工具:
- ora2pg:开源命令行工具,功能强大,是评估和迁移的瑞士军刀。它可以生成详细的迁移评估报告(HTML),估算工作量,并能将大多数对象定义转换为 PostgreSQL 语法。但它不是银弹,对于复杂的 PL/SQL 和包,转换结果通常需要人工复核和重写。
- 使用心得:我通常先用
ora2pg -t SHOW_REPORT生成评估报告,对迁移难度和耗时有个整体把握。它的转换模板(Type,Function,Procedure等)可以自定义,适合企业内特定规范。
- 使用心得:我通常先用
- AWS SCT (Schema Conversion Tool)或Azure DMA (Database Migration Assistant):如果迁移目标云平台是 AWS RDS/Aurora 或 Azure Database for PostgreSQL,这些官方工具集成度好,对云服务特有功能(如扩展)支持更佳。
- 商业工具:如 Ispirer、SwisSQL 等,通常提供更友好的图形界面和更复杂的逻辑转换支持,但需要采购成本。
注意:无论使用哪种工具,都必须进行严格的测试。自动化工具转换的代码,尤其是程序逻辑部分,一定要放在测试环境中,用真实数据或模拟数据跑通所有业务流程,验证结果正确性和性能。
2. 搭建并行的测试环境:
- 源库副本:建立一个与生产环境数据结构一致的 Oracle 测试库(数据量可以适当缩减)。
- 目标库环境:搭建一个版本与未来生产环境一致的 PostgreSQL 集群。强烈建议版本 >= PostgreSQL 12,以利用更多现代特性和性能改进。
- 中间验证层:可以考虑使用PG逻辑订阅或Debezium这类基于日志的CDC工具,在迁移后期进行双写对比验证,确保数据一致性。
3. 核心语法差异与迁移策略详解
这是手册的核心部分,我们将深入最常见的语法差异点,并提供具体的迁移策略和代码示例。
3.1 数据定义语言(DDL)的迁移
1. 建表语句的差异:
自增列:
- Oracle:使用序列(Sequence)和触发器(Trigger)模拟,或 12c 以后的
GENERATED AS IDENTITY。
-- Oracle (传统方式) CREATE TABLE users (id NUMBER PRIMARY KEY, ...); CREATE SEQUENCE users_seq; CREATE OR REPLACE TRIGGER users_bir BEFORE INSERT ON users FOR EACH ROW BEGIN SELECT users_seq.NEXTVAL INTO :NEW.id FROM DUAL; END;- PostgreSQL:直接使用
SERIAL或BIGSERIAL类型(本质上是语法糖,自动创建序列),或更标准的GENERATED ALWAYS AS IDENTITY(PG10+)。
-- PostgreSQL (推荐方式) CREATE TABLE users ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, ... ); -- 或传统方式 CREATE TABLE users (id SERIAL PRIMARY KEY, ...);- 迁移要点:迁移时,需要将 Oracle 的序列和触发器组合,转换为 PostgreSQL 的
IDENTITY或SERIAL。注意序列起始值(START WITH)和步长(INCREMENT BY)的对应。
- Oracle:使用序列(Sequence)和触发器(Trigger)模拟,或 12c 以后的
默认值与系统时间:
- Oracle:
SYSDATE
CREATE TABLE orders (order_date DATE DEFAULT SYSDATE);- PostgreSQL:
CURRENT_TIMESTAMP或NOW()
CREATE TABLE orders (order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP);- 迁移要点:
SYSDATE通常映射为CURRENT_TIMESTAMP。注意精度,CURRENT_TIMESTAMP默认带微秒。
- Oracle:
注释:语法类似,但 PostgreSQL 的
COMMENT ON语句更统一。-- Oracle & PostgreSQL COMMENT ON COLUMN users.name IS '用户姓名';
2. 索引与约束:
- 函数索引:两者都支持,但函数语法需调整。
-- Oracle CREATE INDEX idx_upper_name ON users(UPPER(name)); -- PostgreSQL CREATE INDEX idx_upper_name ON users(UPPER(name)); -- 注意:如果name可能为NULL,PostgreSQL中UPPER(NULL)为NULL,索引行为一致。 - 位图索引:Oracle 的位图索引在特定数据仓库场景使用。PostgreSQL 没有原生的位图索引,但可以通过
btree索引或扩展(如bloom)在某些场景替代,通常需要重新评估查询模式。 - 外键约束:语法基本兼容。注意
ON DELETE和ON UPDATE子句的支持情况一致。
3.2 数据操作语言(DML)与查询的迁移
1. 分页查询——最常遇到的差异:
- Oracle (12c 之前):使用
ROWNUM伪列,写法较为繁琐。-- Oracle: 查询第 21 到 30 条记录 SELECT * FROM ( SELECT t.*, ROWNUM rn FROM ( SELECT * FROM employees ORDER BY hire_date ) t WHERE ROWNUM <= 30 ) WHERE rn > 20; - Oracle (12c 之后)与PostgreSQL:使用更标准的
OFFSET/FETCH或LIMIT/OFFSET。-- PostgreSQL (及 Oracle 12c+) SELECT * FROM employees ORDER BY hire_date OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY; -- 或更常见的 SELECT * FROM employees ORDER BY hire_date LIMIT 10 OFFSET 20;- 迁移要点:将
ROWNUM模式重写为LIMIT/OFFSET。性能警告:OFFSET在大数据量时性能很差(它需要先跳过 N 行)。对于深度分页,应考虑使用“基于键值”的分页(WHERE id > last_id ORDER BY id LIMIT N)。
- 迁移要点:将
2. 字符串与日期函数:
- 字符串连接:
- Oracle:
||或CONCAT函数(仅两个参数)。 - PostgreSQL:
||(推荐)或CONCAT函数(多参数)。
-- 两者都支持 SELECT first_name || ' ' || last_name AS full_name FROM users; -- PostgreSQL 的 CONCAT 更灵活 SELECT CONCAT(first_name, ' ', last_name, ' - ', department) FROM users; - Oracle:
- 日期加减与格式化:
- Oracle:
SYSDATE + 1(加一天),ADD_MONTHS(SYSDATE, 1),TO_CHAR(SYSDATE, 'YYYY-MM-DD')。 - PostgreSQL:
CURRENT_DATE + INTERVAL '1 day',CURRENT_DATE + INTERVAL '1 month',TO_CHAR(CURRENT_DATE, 'YYYY-MM-DD')。 - 迁移要点:将日期算术运算改为
INTERVAL表达式。格式化函数TO_CHAR和TO_DATE在两者中功能相似,但格式符可能略有不同(如 PostgreSQL 的DD和D),需测试验证。
- Oracle:
3. 空值处理与条件表达式:
- NVL/NVL2 -> COALESCE/NULLIF:
-- Oracle SELECT NVL(commission_pct, 0) FROM employees; SELECT NVL2(commission_pct, '有佣金', '无佣金') FROM employees; -- PostgreSQL SELECT COALESCE(commission_pct, 0) FROM employees; SELECT COALESCE(NULLIF(commission_pct::text, ''), '无佣金', '有佣金') FROM employees; -- 注意:NVL2逻辑需用CASE或组合函数模拟 -- 更清晰的写法是用 CASE SELECT CASE WHEN commission_pct IS NOT NULL THEN '有佣金' ELSE '无佣金' END FROM employees; - DECODE -> CASE WHEN: Oracle 的
DECODE函数是CASE的简写,PostgreSQL 只支持标准的CASE表达式,迁移时需要重写。-- Oracle SELECT DECODE(status, 'A', '活跃', 'I', '冻结', '未知') FROM accounts; -- PostgreSQL SELECT CASE status WHEN 'A' THEN '活跃' WHEN 'I' THEN '冻结' ELSE '未知' END FROM accounts;
3.3 过程化语言(PL/SQL 到 PL/pgSQL)的迁移
这是最具挑战性的部分,因为两者虽然都是基于 Ada 语言风格,但细节差异巨大。
1. 程序结构差异:
- Oracle PL/SQL:程序以
CREATE OR REPLACE PROCEDURE/FUNCTION ... AS BEGIN ... END;定义。使用DBMS_OUTPUT.PUT_LINE调试输出。 - PostgreSQL PL/pgSQL:程序以
CREATE OR REPLACE FUNCTION ... RETURNS ... AS $$ BEGIN ... END; $$ LANGUAGE plpgsql;定义。使用RAISE NOTICE ‘%’, variable;输出信息。- 迁移要点:需要重写整个程序头尾。特别注意 PostgreSQL 函数必须声明返回值类型,即使是存储过程(
PROCEDURE, PG11+)也略有不同。$$是美元引号,用于包裹函数体。
- 迁移要点:需要重写整个程序头尾。特别注意 PostgreSQL 函数必须声明返回值类型,即使是存储过程(
2. 变量声明与赋值:
- Oracle:声明在
IS或AS之后,BEGIN之前。赋值用:=。DECLARE v_name VARCHAR2(100); v_count NUMBER := 0; BEGIN SELECT name INTO v_name FROM users WHERE id = 1; v_count := v_count + 1; END; - PostgreSQL:声明在
DECLARE部分(对于函数/存储过程)。赋值用:=或=(在SELECT INTO或UPDATE/INSERT ... RETURNING INTO时)。CREATE OR REPLACE FUNCTION get_user_info(user_id INT) RETURNS TEXT AS $$ DECLARE v_name TEXT; v_count INT := 0; BEGIN SELECT name INTO v_name FROM users WHERE id = user_id; v_count := v_count + 1; RETURN v_name || ' processed ' || v_count || ' times.'; END; $$ LANGUAGE plpgsql;
3. 游标处理:
- Oracle 隐式游标:
SQL%ROWCOUNT,SQL%FOUND。在 PostgreSQL 中需用GET DIAGNOSTICS获取。-- Oracle UPDATE accounts SET balance = balance - 100 WHERE user_id = 123; IF SQL%ROWCOUNT = 0 THEN RAISE_APPLICATION_ERROR(-20001, '账户未找到'); END IF;-- PostgreSQL UPDATE accounts SET balance = balance - 100 WHERE user_id = 123; GET DIAGNOSTICS row_count = ROW_COUNT; IF row_count = 0 THEN RAISE EXCEPTION '账户未找到'; END IF; - 显式游标:语法相似,但
FOR record IN cursor_name LOOP在 PostgreSQL 中更常用,且无需显式打开、获取、关闭。
4. 异常处理:
- Oracle:
EXCEPTION WHEN ... THEN ... - PostgreSQL:
EXCEPTION WHEN ... THEN ...块结构类似,但异常名称不同。例如NO_DATA_FOUND在 PostgreSQL 中是NO_DATA_FOUND(但通常用NOT FOUND配合GET DIAGNOSTICS检查),TOO_MANY_ROWS需要自己用逻辑判断或捕获unique_violation等具体异常。- 重要区别:在 PostgreSQL 的 PL/pgSQL 函数中,一旦进入
EXCEPTION块,会形成一个子事务,对性能有影响。应尽量避免在频繁执行的循环中使用异常块进行流程控制。
- 重要区别:在 PostgreSQL 的 PL/pgSQL 函数中,一旦进入
5. 包的迁移:Oracle 的包(Package)是一种将相关函数、过程、变量、游标封装在一起的机制。PostgreSQL 没有直接等价物。常见的迁移策略有:
- 策略一:拆分为独立的函数/存储过程:将包规格(Package Specification)中声明的所有公共对象,在 PostgreSQL 中创建为独立的函数。私有对象(包体内私有)可以转换为不公开的辅助函数,或将其逻辑内联。
- 策略二:使用模式(Schema)和搜索路径模拟:将包名作为模式名,包内的函数放在该模式下。通过设置
search_path,可以模拟包的命名空间。但这无法模拟包变量(Package-level Variables)。 - 策略三:使用临时表或会话级变量模拟包变量:对于需要在会话期间保持状态的包变量,可以使用 PostgreSQL 的临时表、
SET/RESET命令操作自定义配置参数(SET myapp.var = ‘value’),但这种方法有局限性且需谨慎设计。
4. 高级特性与性能考量迁移
迁移不仅仅是语法的对等替换,更要考虑目标数据库的特性,以实现最佳性能。
4.1 并发控制与事务
- 锁机制:两者都支持行级锁和表级锁。Oracle 的
SELECT ... FOR UPDATE在 PostgreSQL 中完全兼容。但需要注意 PostgreSQL 的MVCC(多版本并发控制)实现与 Oracle 略有不同,在长时间运行的事务中,可能会遇到“快照过旧”的错误(可调整old_snapshot_threshold等参数)。 - 序列并发性:Oracle 序列的
CACHE选项在提高性能的同时,可能导致序列值在实例重启后出现“断层”。PostgreSQL 的序列也有CACHE,行为类似。在要求绝对连续无间断的场景(如作为订单号),需要设置为CACHE 1或使用SERIAL/IDENTITY列,但这会影响性能。
4.2 分区表
- Oracle:传统上使用分区表(Partitioned Table),11g 后支持间隔分区等。
- PostgreSQL:10.x 版本引入了声明式分区,12.x 版本后功能趋于成熟,支持范围分区、列表分区、哈希分区,以及分区索引等。迁移时,需要将 Oracle 的分区逻辑用 PostgreSQL 的
CREATE TABLE ... PARTITION BY RANGE/LIST/HASH ...语法重写。- 实操心得:PostgreSQL 的声明式分区在查询优化上比之前的继承表分区有巨大提升。迁移时,应利用
pg_dump导出 Oracle 表结构后,手动编写 PostgreSQL 的分区 DDL。特别注意分区键的数据类型和边界条件定义。
- 实操心得:PostgreSQL 的声明式分区在查询优化上比之前的继承表分区有巨大提升。迁移时,应利用
4.3 物化视图与查询优化
- 物化视图:两者概念相同,用于存储预计算的结果集。语法类似:
CREATE MATERIALIZED VIEW ... AS SELECT ...。刷新命令不同:Oracle 是DBMS_MVIEW.REFRESH,PostgreSQL 是REFRESH MATERIALIZED VIEW [CONCURRENTLY]。CONCURRENTLY选项允许在刷新时不阻塞读取,是 PostgreSQL 的一个实用特性。 - 查询优化器提示(Hints):这是一个重大差异。Oracle 广泛使用优化器提示(如
/*+ INDEX(table_name index_name) */)来干预执行计划。PostgreSQL没有官方支持的查询提示。它的优化器更倾向于基于统计信息自主选择计划。- 迁移策略:
- 首先信任优化器:确保 PostgreSQL 的
ANALYZE已定期运行,统计信息准确。很多时候,无需提示也能得到良好计划。 - 调整配置参数:通过设置会话级参数如
enable_seqscan,enable_indexscan,enable_nestloop等,可以临时禁用某些扫描或连接方式,间接影响优化器。 - 重写查询:有时通过改变 SQL 写法(如使用 CTE、调整子查询为 JOIN、改变条件顺序)可以达到优化目的。
- 使用扩展:
pg_hint_plan是一个流行的第三方扩展,可以在 PostgreSQL 中使用类似 Oracle 的注释提示语法。但这是最后的手段,需谨慎使用并充分测试,因为它绑定了特定的执行计划,可能随数据分布变化而失效。
- 首先信任优化器:确保 PostgreSQL 的
- 迁移策略:
5. 迁移后的验证、优化与踩坑实录
迁移完成并成功运行,只算成功了前半程。后半程是确保系统在生产环境下稳定、高效。
5.1 数据一致性与功能验证
- 行数校验:对每个表进行
COUNT(*)比对,这是最基本的检查。 - 抽样内容校验:编写脚本,随机抽取若干行数据,对比关键字段的值。可以计算字段的哈希值(如 MD5)进行批量比对。
- 业务逻辑验证:这是核心。必须运行完整的业务测试套件,包括:
- 单元测试:针对每个迁移后的函数、存储过程。
- 集成测试:模拟用户端到端的业务流程。
- 报表验证:对比迁移前后关键业务报表的输出结果,确保数据汇总、计算逻辑一致。
- 性能基准测试:使用相同的测试数据和测试用例,在 Oracle 和 PostgreSQL 上运行核心业务查询和事务,对比响应时间、吞吐量(TPS/QPS)和资源使用率(CPU、内存、IO)。工具可以使用
pgbench(模拟)或真实的应用负载回放。
5.2 常见性能问题排查
迁移后性能下降是常见问题,通常源于以下几点:
- 统计信息缺失或过时:PostgreSQL 依赖
ANALYZE收集的统计信息来生成执行计划。迁移后或大数据量变更后,一定要手动执行ANALYZE table_name;或ANALYZE;(整个数据库)。考虑配置autovacuum相关参数,使其更积极地工作。 - 索引缺失或无效:检查慢查询的执行计划(
EXPLAIN ANALYZE)。确认是否在关键的过滤条件列、连接条件列、排序分组列上建立了合适的索引。注意,某些 Oracle 上的函数索引,在 PostgreSQL 中可能需要创建表达式索引(CREATE INDEX ON table (expression(column));)。 - 查询计划器选择不佳:
- 参数化查询与计划缓存:PostgreSQL 对参数化查询(如 JDBC PreparedStatement)会缓存执行计划。如果数据分布不均匀,一个为“典型值”生成的计划可能对“特殊值”非常糟糕。可以使用
PREPARE和EXECUTE测试不同参数,必要时使用DISCARD PLANS清除缓存或尝试用SET LOCAL plan_cache_mode = force_generic_plan;(如果版本支持)强制通用计划。 - 连接顺序与方式:使用
EXPLAIN查看多表连接时选择的连接顺序(NestLoop, HashJoin, MergeJoin)是否合理。不当的连接顺序可能导致笛卡尔积或中间结果集巨大。
- 参数化查询与计划缓存:PostgreSQL 对参数化查询(如 JDBC PreparedStatement)会缓存执行计划。如果数据分布不均匀,一个为“典型值”生成的计划可能对“特殊值”非常糟糕。可以使用
- 配置参数不当:
shared_buffers(相当于 Oracle 的 SGA)、work_mem(排序哈希等操作内存)、maintenance_work_mem(维护操作内存)等核心参数对性能影响极大。需要根据新服务器的硬件资源(内存、CPU、磁盘类型)重新调优,不能直接沿用默认值或 Oracle 的配置经验。
5.3 我踩过的那些“坑”
- 隐式类型转换的陷阱:Oracle 的隐式类型转换非常“宽松”,比如
WHERE char_column = 123可能会工作。PostgreSQL 则非常严格,这样的语句会报错“操作符不存在”。迁移后,所有应用层的 SQL 都必须确保数据类型匹配,或者在数据库层使用显式的CAST。 - 空字符串与 NULL:在 Oracle 中,
VARCHAR2字段的空字符串(‘’)被视为NULL。在 PostgreSQL 中,空字符串和NULL是严格区分的。这可能导致应用逻辑错误,特别是WHERE column != ‘value’这样的查询,在 Oracle 会排除NULL,在 PostgreSQL 则不会(除非显示加IS NOT NULL)。迁移时需要对相关逻辑进行审查和修正。 - 提交行为的差异:在 Oracle 的 PL/SQL 中,DDL 语句(如
CREATE TABLE)会隐式提交事务。在 PostgreSQL 的 PL/pgSQL 中,DDL 语句不会导致隐式提交,除非你显式地COMMIT或使用了AUTOCOMMIT。这可能导致在事务块中的 DDL 在 PostgreSQL 中无法立即对其他会话可见,需要特别注意。 - 序列的“断层”:如前所述,使用
CACHE > 1的序列,在数据库重启后,缓存中未使用的序列值会丢失,导致序列号不连续。如果业务严格要求连续(如发票号),这是一个风险点。我们的解决方案是,对于这类关键序列,要么使用CACHE 1,要么在应用层实现一个更复杂的、基于表的序列生成器,并处理好并发。 - 时区处理的混乱:如果应用涉及多时区,务必统一使用
TIMESTAMPTZ(带时区的时间戳)来存储时间。并在应用连接数据库时,明确设置会话的时区(SET TIME ZONE ‘Asia/Shanghai’;)。避免混用TIMESTAMP(无时区)和TIMESTAMPTZ,否则在时间转换和比较时极易出错。
迁移是一个系统工程,这本手册提供了从评估到上线的完整路线图和关键细节。但每个系统都是独特的,最宝贵的经验往往来自于你自己在测试环境中的反复验证和压测。记住,没有百分之百的自动化转换,深入理解业务逻辑和数据库原理,才是成功迁移的最终保障。当你看到原本运行在 Oracle 上的核心系统,在 PostgreSQL 上平稳高效地运转起来时,那种成就感,是对所有辛苦付出的最好回报。
