1. 字符集转换导致的数据丢失问题最近在帮客户做Oracle 11g升级到19c的项目时遇到了一个让人头疼的问题使用expdp/impdp工具迁移用户数据时系统报错ORA-39346: data loss in character set conversion。这个错误看起来简单但排查起来还真需要点技巧。首先我们需要理解这个错误的本质。当Oracle在导入数据时如果发现源库和目标库的字符集不匹配就会尝试进行字符集转换。在这个过程中某些特殊字符可能会丢失或无法正确转换系统就会抛出这个错误。我遇到的具体情况是一些表注释中的中文字符在导入过程中出现了乱码。排查这个问题的第一步就是检查两边的字符集设置是否一致。你可以用这个SQL查询select property_name,property_value from database_properties where property_name like %CHARA%这个查询会返回数据库的字符集(NLS_CHARACTERSET)和国家字符集(NLS_NCHAR_CHARACTERSET)设置。在我的案例中虽然两边的设置都是AL32UTF8和UTF8看起来完全一致但还是出现了问题。深入分析后发现问题出在源库的某些对象元数据上。这些对象的注释在源库中就已经存在乱码只是11g版本下没有报错。当迁移到19c时新版本对字符集检查更加严格就暴露了这个问题。这种情况特别容易出现在那些历史悠久的系统上可能是在某个历史时期导入数据时就已经埋下了隐患。解决这个问题有几个思路如果乱码只出现在注释等非关键位置可以暂时忽略不影响系统运行对于重要的注释内容可以在迁移前先在源库中修正最彻底的方案是重建这些对象确保元数据干净2. 作业(Job)导入后log_user异常问题在完成数据导入后我发现一个奇怪的现象dba_jobs视图中的log_user字段全部显示为SYS而不是原来的应用用户。这看起来像是权限问题但实际原因要复杂得多。刚开始我以为是使用SYS用户执行导入导致的于是尝试用应用用户重新导入问题依旧。然后又试着手动创建作业结果log_user还是显示为SYS。经过一番排查最终确认这是Oracle 19c的一个已知Bug编号32249704。这个Bug的具体表现是通过impdp导入作业时log_user字段会被错误地设置为SYS用户而priv_user和schema_user字段则保持正常。虽然看起来是个小问题但在某些严格的权限控制环境下可能会导致作业执行失败。解决这个问题有两个方案安装Oracle官方提供的one-off补丁32249704如果暂时无法打补丁可以采取变通方案在导出前记录所有作业的原始log_user导入后手动更新dba_jobs视图中的log_user字段使用DBMS_IJOB包重建这些作业我建议在生产环境中优先考虑打补丁的方案虽然操作稍复杂但能从根本上解决问题。临时方案虽然能应急但后续维护会比较麻烦。3. 物化视图刷新组创建失败问题在导入过程中遇到了一个关于物化视图刷新组的报错ORA-39083: Object type REFRESH_GROUP failed to create和ORA-01858: a non-numeric character was found where a numeric was expected。这个错误信息看起来有点晦涩我来拆解一下。问题的核心在于刷新组的next_date参数。在源库中这些刷新组对应的作业处于禁用状态brokenY导致next_date被设置为一个特殊值4000-01-01。在导入过程中这个日期值被转换成了00-JAN-01的格式而Oracle无法正确解析这个日期字符串。要解决这个问题我们需要修改导入脚本中的日期格式。具体操作如下首先找到报错的刷新组定义将其中的next_date参数从00-JAN-01改为明确的日期格式to_date(4000-01-01 00:00:00,yyyy-mm-dd hh24:mi:ss)手动执行修改后的SQL脚本这个问题的根本原因是Oracle在不同版本间对日期处理的差异。11g对某些特殊日期值比较宽容而19c则要求更严格的格式。这也提醒我们在跨版本迁移时要特别注意日期和时间相关的对象。4. DBA_REFRESH视图中BROKEN字段显示异常迁移完成后在检查物化视图刷新状态时发现DBA_REFRESH视图中的BROKEN字段显示为?而不是预期的Y或N。这个现象看起来很奇怪值得深入分析。通过查看DBA_REFRESH视图的定义我发现BROKEN字段的值是通过解码job$表中的flag字段得到的。问题在于在19c环境中job$表竟然是空的这就导致连接查询时返回null值最终显示为?。这个问题比较棘手因为Oracle官方文档中没有任何关于job$表为空的说明目前没有公开的Bug与此相关直接操作基表job$风险很大经过多次尝试我找到了一个可行的解决方案绕过DBA_REFRESH视图直接通过dba_jobs视图查询作业状态。虽然这不是最完美的方案但至少能获取到需要的信息。对于这个问题的长期解决方案我建议关注Oracle官方的补丁更新考虑使用Oracle Scheduler替代传统作业如果需要严格监控可以创建自定义视图来规避这个问题5. 外键约束验证失败问题在导入过程中遇到了多个外键约束创建失败的报错ORA-02298: cannot validate - parent keys not found。这个错误通常表示子表中存在父表没有的记录导致外键约束无法建立。排查这个问题的方法很明确检查父表和子表的数据完整性确认约束状态是否正常比较两边的数据一致性在我的案例中发现问题出在导出导入的时间差上。由于源库是生产环境在导出父表和子表之间可能有数据变动导致两边数据不一致。解决这个问题的关键在于确保导出时所有相关表的数据处于一致状态。我采用了以下方法-- 首先查询当前SCN号 select to_char(current_scn) from v$database; -- 然后在expdp命令中使用FLASHBACK_SCN参数 expdp ... flashback_scn查询到的SCN这种方法利用了Oracle的闪回查询特性确保所有表都基于同一个时间点的数据快照导出。虽然导出过程会稍微慢一点但能有效避免数据不一致的问题。6. SYS对象权限丢失问题最后一个问题是关于权限的。迁移完成后发现目标库中某些对象的权限比源库少特别是那些SYS用户拥有的对象。这看起来像是导入过程中丢失了部分授权。经过仔细分析发现这不是Bug而是expdp的预期行为。Oracle的数据泵工具在设计时就有意不导出SYS对象的授权主要是出于安全考虑。这个特性在官方文档中有说明但很容易被忽略。解决这个问题需要手动处理在源库生成所有必要的授权语句在目标库导入前预先执行这些授权然后再进行正式的数据导入具体操作步骤如下-- 在源库生成授权脚本 connect / as sysdba spool grants_script.sql select grant || privilege || on ||||table_name |||| to || grantee || ; from dba_tab_privs where owner SYS and privilege not in (READ, WRITE) and grantee in (应用用户) order by 1; spool off这个问题的教训是在规划迁移时一定要全面考虑各种对象和权限特别是那些系统对象的授权。最好在测试环境中先完整演练一遍确保不会遗漏任何细节。