☰
Oracle 19c 数据泵导入 TSTZ 报错?时区版本 32 升 42 全流程
2026/9/26 22:45:19 网站建设 项目流程

简介:Oracle 19c数据库时区版本由32升级至42时,数据泵导入导出遇到TSTZ(带时区时间戳)字段报错是常见问题。这份资料面向Oracle DBA、数据迁移工程师以及负责跨时区业务的运维人员,提供从问题定位到解决的完整方案。压缩包共10个文件、总大小约377KB,包含6个dat时区数据文件、2个xml配置映射信息、2个txt说明文档,可用于替换或补充时区数据,并指导调整数据泵参数。内容详细讲解时区版本差异、TSTZ兼容性处理、导入前预处理与升级后验证等关键环节,特别给出备份恢复、参数配置和最佳实践建议;同时涵盖升级前影响评估、业务影响分析与时间相关功能测试等注意事项。对处理跨时区数据、执行数据库升级或数据泵导入导出的运维与开发人员有直接参考价值。目前已有1367人学习下载。

1. 时区版本 32 与数据泵 TSTZ 报错:不是升级补丁包就能绕过去

Oracle 19c 数据泵导入时报 TSTZ 版本冲突,十有八九卡在这:源库时区版本已经到了 42,目标库还是 32,impdp 一看到 TIMESTAMP WITH TIME ZONE 类型就直接甩出 ORA-39405,整张表不进。oracle19c升级时区版本 32->42 就是为这类报错准备的标准动作:把目标库时区文件升到和源库一致,让 dump 里的 TSTZ 数据按新规则落库。这篇文章从查版本到跑完 ALTER DATABASE UPDATE TIMEZONE FILE,再回到 impdp 重新导数,给你一条能直接抄的完整路径。适合正在做数据迁移、异构导入或者 19c 补丁集升级的 DBA 和维护人。

2. 升级前先盘一遍现场:三个视角确认时区版本现状

时区升级最怕不看现状直接上手。曾经有人在 19c 单实例上直接把 42 版时区文件丢进$ORACLE_HOME/oracore/zoneinfo/,然后重启数据库,结果数据库能起,应用一查 TSTZ 数据就报ORA-01882: timezone region not found。原因是文件换了,但数据库字典里记录的版本还是 32,内存和数据字典对不上。所以在跑任何升级命令前,先把下面三件事查清楚。

2.1 用 V$TIMEZONE_FILE 和 REGISTRY$DATABASE 交叉确认版本

先连进目标库,用两条 SQL 看当前的时区版本状态:

-- 查看当前容器正在使用的时区文件版本,以及该版本的生效时间 SELECT VERSION, UPDATED FROM V$TIMEZONE_FILE; -- 查看数据字典里持久化的时区版本 SELECT TZ_VERSION FROM REGISTRY$DATABASE;

两条 SQL 的区别很关键:V$TIMEZONE_FILE反映的是当前会话所在容器加载进内存的时区文件版本,数据库启动时从ORACLE_HOME/oracore/zoneinfo/下读取;REGISTRY$DATABASE.TZ_VERSION是数据字典表里固化的版本号,用来标记这个库当前认哪个版本的时区语义。

正常情况下两处结果一致。如果出现V$TIMEZONE_FILE显示 42、REGISTRY$DATABASE还是 32,说明升级只做了一半——这正是很多人说的玄学状态:数据库能跑,TSTZ 数据一碰就炸。遇到这种情况不要继续导数据,按第 3 章的完整流程重走一遍,把状态拉齐。

2.2 再查 PDB:19c 的时区版本按容器拆开看

19c 默认是 CDB 架构,每个 PDB 也有自己的时区版本记录,这和 11g 时代的单库逻辑不一样。常见误区是只升级 CDB 根容器,然后数据泵导入 PDB 时照样报 ORA-39405。所以在 CDB 环境下要一个容器一个容器地查:

-- 在 CDB$ROOT 下执行,看所有 PDB 的打开状态 SELECT CON_ID, NAME, OPEN_MODE FROM V$PDBS; -- 进入具体 PDB,查它自己的时区文件版本 ALTER SESSION SET CONTAINER = PDB1; SELECT VERSION, UPDATED FROM V$TIMEZONE_FILE; SELECT TZ_VERSION FROM REGISTRY$DATABASE;

逐个 PDB 执行后,把结果列成一张对照表:CDB 根、PDB1、PDB2 各是多少。升级目标是把所有容器的版本都升到 42,不是只升某一个。如果现场很多 PDB,建议先写个循环脚本把结果一次性收集出来,省得漏掉哪个,后面导数据又翻车。

2.3 32 和 42 之间到底差了什么:DST 规则与 TIMESTAMP WITH TIME ZONE 语义

时区版本号背后的实质内容是夏令时规则表。Oracle 每发布一个新时区版本,通常是因为某些国家或地区调整了夏令时起止时间、时区偏移或者永久停用夏令时。版本 32 是 19c 数据库刚开始发布时自带的,里面没有后来几年的规则修正;版本 42 补齐了后续的变更。

TSTZ 类型全称是 TIMESTAMP WITH TIME ZONE,它存的不只是一个时间点,还包括时区区域名,比如2024-03-10 02:30:00 US/Eastern。这个时间在东八区的人看来就是一个普通时间,但数据库需要根据时区版本里的 DST 规则判断它到底是不是合法时间、转换成 UTC 应该是几点。旧版本 32 不认识新规则里调整过的时间段,硬算出来的结果可能和实际差一小时,甚至直接报错。

对比项时区版本 32时区版本 42
2024 年以后部分地区的 DST 规则缺失或按旧规则已更新
TSTZ 字面量中新增的时区区域名部分识别不了,报 ORA-01882正常解析
数据泵 TSTZ 导入兼容性目标库低于源库版本时报 ORA-39405与 42 版源库匹配
常见 SHOW TSTZ 版本检查19c 初始版本常见值补丁后常见值

这就是数据泵导数据时为什么死磕版本号:dump 文件里写死了源库的时区版本,目标库版本不够,数据泵宁可拒绝执行也不愿意把语义错误的数据导进去。它不是 bug,是保护机制。

2.4 升级影响面评估:停机窗口、RAC 节点与应用连接池

时区版本升级不是一条命令就能静默完成的,需要确认以下影响面:

  • 停机窗口:升级过程中数据库至少重启两次,一次进 UPGRADE 模式,一次回正常模式。单实例建议预留 30 到 60 分钟窗口。
  • RAC 环境:所有节点的$ORACLE_HOME/oracore/zoneinfo/下的时区文件都要替换,并且要规划滚动顺序,不能一边节点在跑业务一边把文件换掉。
  • 应用连接池:升级完成后,连接池里的老会话可能还持有旧的时区上下文,需要重启应用或刷新连接池。
  • TSTZ 相关对象:TSTZ 列上的函数索引、基于 AT TIME ZONE 的物化视图,升级后要检查执行计划和结果。

把这些写进变更单,再去执行升级,后面才不会被业务方追问"为什么导完的数据时间对不上"。

3. 把 19c 时区版本从 32 升到 42:一条能直接抄的升级命令序列

这一章是全文的核心操作,按顺序执行。场景默认是 19c CDB 架构,单实例;RAC 的差异点会在步骤里单独说明。

3.1 准备时区数据文件并同步到所有节点

先从 My Oracle Support 下载与 19c 匹配的时区文件补丁,或者从另一个已经是 42 版本的 19c 库的$ORACLE_HOME/oracore/zoneinfo/目录复制。补丁包解压后通常包含timezrg_42.dat和timezlrg_42.dat,分别是常规时区文件和大时区文件。数据库启动时会根据内部配置读取对应的文件,两个文件最好都放到位。

# 先看目标目录里现有的时区文件,确认命名和版本 ls -l $ORACLE_HOME/oracore/zoneinfo/ | grep -E "timezr" # 备份旧文件,升级失败时还能回滚 cp $ORACLE_HOME/oracore/zoneinfo/timezrg_42.dat \ $ORACLE_HOME/oracore/zoneinfo/timezrg_42.dat.bak cp $ORACLE_HOME/oracore/zoneinfo/timezlrg_42.dat \ $ORACLE_HOME/oracore/zoneinfo/timezlrg_42.dat.bak # 把新文件放到指定目录 cp /tmp/tzpatch/timezrg_42.dat $ORACLE_HOME/oracore/zoneinfo/ cp /tmp/tzpatch/timezlrg_42.dat $ORACLE_HOME/oracore/zoneinfo/

RAC 环境要注意,这条命令必须在每个节点都执行一遍,而且建议把新文件先放到所有节点,再开始下一步,避免出现节点 A 已经换文件、节点 B 还是旧文件的中间状态。节点不一致导致的问题比不升级还难查,后面避坑章会展开。

这里补充一个血泪经验:文件换完之后不要急着重启数据库,先确认文件权限和属主跟原来一致,通常是oracle:oinstall。权限不对,数据库启动时读时区文件会失败,报错日志又藏在 alert log 里,排查起来很绕。

3.2 建错误表并进入升级中间态

时区文件就位后,连到 CDB 根容器,先创建错误表。这张表的作用是记录升级过程中无法转换的 TSTZ 数据行,排查问题时它是第一手证据。

-- 在 CDB$ROOT 下执行 ALTER SESSION SET CONTAINER = CDB$ROOT; -- 建错误表,指定存放到 SYSTEM 表空间 BEGIN DBMS_DST.CREATE_ERROR_TABLE( error_table_name => 'SYS.TSTZ_ERROR_TAB', tablespace_name => 'SYSTEM'); END; / -- 开始升级,把数据库标记为向 42 版本迁移 BEGIN DBMS_DST.BEGIN_UPGRADE(upgrade_version => '42'); END; /

CREATE_ERROR_TABLE不是强制步骤,但强烈建议建。如果不建,升级过程中遇到无法转换的数据行,Oracle 只在日志里给提示,后续很难定位具体是哪些行有问题。BEGIN_UPGRADE执行后,REGISTRY$DATABASE里的TZ_VERSION会从 32 变成 42,但内存里真正生效的时区数据还是旧的,这是设计中的中间态,不是出错。

执行完后不要停留太久,尽快进入下一步。中间态下数据库对外表现是正常的,但 TSTZ 数据写入已经被保护起来,业务在这个窗口继续写 TSTZ 列会累积风险。

3.3 重启到 UPGRADE 模式,更新 CDB 与全部 PDB

关键一步来了。关闭数据库,以 UPGRADE 模式启动,然后执行ALTER DATABASE UPDATE TIMEZONE FILE。这个命令是 12.2 之后才有的,它直接读取$ORACLE_HOME/oracore/zoneinfo/下的时区文件并更新内存中的数据,省去了老版本那种先把 TSTZ 相关数据移出来再重建数据库的复杂流程。

-- 退出 SQL*Plus 后,在命令行执行 sqlplus / as sysdba -- 关闭数据库 SHUTDOWN IMMEDIATE; -- 以 UPGRADE 模式启动 STARTUP UPGRADE; -- 更新 CDB 根容器的时区数据 ALTER DATABASE UPDATE TIMEZONE FILE; -- 19c 的 PDB 也要进入 UPGRADE 模式才能继续 ALTER PLUGGABLE DATABASE ALL OPEN UPGRADE; -- 更新所有 PDB 的时区数据 ALTER PLUGGABLE DATABASE ALL UPDATE TIMEZONE FILE;

ALTER DATABASE UPDATE TIMEZONE FILE会在执行时读取磁盘上的timezrg_42.dat和timezlrg_42.dat,把它们加载进 SGA 并更新内部时区字典。命令本身不要求数据库处于 UPGRADE 模式也能跑,但在 UPGRADE 模式下执行最稳妥,避免被正常业务会话干扰。

ALTER PLUGGABLE DATABASE ALL OPEN UPGRADE这条命令容易漏。STARTUP UPGRADE 只打开了 CDB 根容器,PDB 默认还是 MOUNT 状态,不先 OPEN UPGRADE 就直接执行ALTER PLUGGABLE DATABASE ALL UPDATE TIMEZONE FILE,命令不会生效,后面查 PDB 版本还是 32。

3.4 结束升级:END_UPGRADE 与三处版本核对

时区数据加载完成后,先把数据库切回正常模式,再执行END_UPGRADE。顺序不能反:如果在 UPGRADE 模式下直接调DBMS_DST.END_UPGRADE(),有些版本会报会话状态不对。

-- 关闭并正常启动数据库 SHUTDOWN IMMEDIATE; STARTUP; -- 回到 CDB 根容器,检查版本是否已经变成 42 ALTER SESSION SET CONTAINER = CDB$ROOT; SELECT VERSION FROM V$TIMEZONE_FILE; SELECT TZ_VERSION FROM REGISTRY$DATABASE; -- 逐个 PDB 检查 ALTER SESSION SET CONTAINER = PDB1; SELECT VERSION FROM V$TIMEZONE_FILE; SELECT TZ_VERSION FROM REGISTRY$DATABASE; -- 确认版本无误后,在 CDB 根容器结束升级 ALTER SESSION SET CONTAINER = CDB$ROOT; BEGIN DBMS_DST.END_UPGRADE(); END; / -- 每个 PDB 也要各自结束升级 ALTER SESSION SET CONTAINER = PDB1; BEGIN DBMS_DST.END_UPGRADE(); END; /

验证版本时如果发现某个容器还是 32,不要执行END_UPGRADE,停下来排查为什么UPDATE TIMEZONE FILE没覆盖到它。END_UPGRADE一执行,整个升级就定性了,之后想回退到 32 非常麻烦,Oracle 官方不支持时区版本降级,等于没有后悔药。

END_UPGRADE过程中如果检测到错误表里有记录,会报出相关信息,这时候去查SYS.TSTZ_ERROR_TAB里的数据,逐条判断是哪些 TSTZ 行转换失败。大多数情况下是历史数据里用了已经废弃的时区区域名,把那些行单独修正就好。

4. 数据泵 TSTZ 报错的正解:日志定位、版本对齐、重新导数

时区版本升到 42 后,回到最初的问题:数据泵导数据报 TSTZ 错误。这一章讲怎么从日志里确认问题,再重新跑通 expdp 和 impdp。

4.1 先认准 ORA-39405 和它身边的 ORA-01882

数据泵导 TSTZ 数据常见的报错有两种,报错信息不同,处理方式也不同。

第一种是 ORA-39405,文本类似:

ORA-39405: Oracle Data Pump: Value of TSTZ version in source database (42) is newer than in destination database (32)

这条报错出现在 impdp 侧,含义很直白:源库导出 dump 时记录的时区版本是 42,目标库当前只有 32。数据泵检查到版本不匹配,拒绝继续导入。解法就是把目标库按第 3 章的流程升到 42,而不是想着绕过检查。

第二种是 ORA-01882:

ORA-01882: timezone region not found

这条报错如果发生在数据泵导入过程中,通常是 dump 文件里的某些 TSTZ 数据引用了新时区版本才有的时区区域名,而目标库版本太老,词表里没有这个区域。比如某个城市在 42 版本里才新增了时区条目,32 版本里找不到。

所以在升级前,先在目标库执行一段测试 SQL,确认它认识源库 dump 里可能出现的时区区域名,能提前暴露问题:

-- 在目标库测试常见的新时区区域名是否能识别 SELECT FROM_TZ(TIMESTAMP '2024-03-10 12:00:00', 'US/Eastern') FROM DUAL;

这条 SQL 在 32 版本下如果正常返回,说明该区域名在旧版本里也存在;如果报 ORA-01882,基本可以断定目标库时区版本太旧。

4.2 目标库升到 42 后,重新跑一遍 expdp 与 impdp

版本对齐后,重新执行导出和导入。这里给出一个完整的命令组合,实际使用时替换连接串、目录对象和 schema 名。

源库侧导出:

expdp system/密码@源库服务名 \ directory=DATA_PUMP_DIR \ dumpfile=SRC_42.DMP \ logfile=EXP_SRC_42.log \ schemas=APPUSER \ parallel=4

导出参数里没有专门指定 TSTZ 版本的选项。expdp 会自动把源库当前的时区版本号写进 dump 文件元数据,也就是 42。schemas=APPUSER指定导出哪个用户的对象,parallel=4是并行度,导出大 schema 时可以提高速度,但要注意源库的 CPU 和 I/O 负载。

目标库侧导入:

impdp system/密码@目标库服务名 \ directory=DATA_PUMP_DIR \ dumpfile=SRC_42.DMP \ logfile=IMP_SRC_42.log \ schemas=APPUSER \ transform=segment_attributes:n \ parallel=4 \ table_exists_action=skip

导入端关键差异是table_exists_action=skip:如果上次导入失败时已经创建了部分表,跳过已存在的表,避免重复建表报错。transform=segment_attributes:n是让导入时忽略源库表空间的段属性,统一落到目标库默认表空间,适合跨环境迁移。

特别注意:不要试图在 impdp 里通过VERSION参数去糊弄 TSTZ 检查。数据泵检查的是 dump 里记录的时区版本号,不是对象版本,设置VERSION=19.3这类参数不影响 TSTZ 版本判定。唯一正解就是让目标库时区版本大于等于源库。

4.3 导完不等于完:抽查 TSTZ 列与 DST 边界时间

导入完成后,抽几条 TSTZ 数据核对。重点查两类:一类是极端时区偏移的数据,另一类是 DST 切换时间点的数据。

-- 抽查导入后的 TSTZ 数据是否保留区域名和偏移 SELECT ORDER_ID, ORDER_TS, EXTRACT(TIMEZONE_REGION FROM ORDER_TS) AS TZ_REGION, EXTRACT(TIMEZONE_ABBR FROM ORDER_TS) AS TZ_ABBR FROM APPUSER.ORDERS WHERE ROWNUM <= 10; -- 验证 DST 切换当天的数据转换结果 SELECT COUNT(*) FROM APPUSER.ORDERS WHERE ORDER_TS AT TIME ZONE 'US/Eastern' >= TIMESTAMP '2024-03-10 00:00:00 US/Eastern';

第二条 SQL 如果返回结果和源库对不上,先查是不是应用连接池没刷新、会话里还带着旧的时区上下文,而不是急着怀疑数据泵。把连接池重启一遍再跑。

5. 时区升级避坑:五个翻车现场和补救顺序

按第 3 章流程走能顺利升级,但实际操作中总有几个固定翻车点。这一章把这些坑的现场、原因和解决步骤写清楚。

5.1 BEGIN_UPGRADE 后长期停在中间态,应用报 ORA-01882

现象:执行完DBMS_DST.BEGIN_UPGRADE后没有继续,几小时后应用查询 TSTZ 数据报 ORA-01882,业务方急着找 DBA。

原因:数据库字典里时区版本已经标成 42,但内存里真正生效的时区数据还是 32。这时候数据库对外提供的是一个矛盾的状态:它认为自己是 42,但解析时区区域名用的还是老词表,新的区域名自然认不出来。

解决:不要在这个状态上做任何数据修复,直接按第 3.2 到 3.4 的流程走完。如果已经拖了很久,先确认没有业务在写 TSTZ 列,再继续。

5.2 RAC 只升一个节点,节点切换后应用报错找不到节点还带 ORA-01882

现象:RAC 两节点,节点 1 完成了时区文件替换和数据库重启,节点 2 没做。应用连节点 1 正常,负载均衡切到节点 2 后,连接报错找不到节点,或者报 ORA-01882。

原因:节点 2 的$ORACLE_HOME/oracore/zoneinfo/下还是旧时区文件,整个集群的时区文件版本不一致。Oracle 在 RAC 里对时区文件有校验,节点间版本不一致时,实例之间传递 TSTZ 数据可能直接失败。

解决:升级前把新时区文件同步到所有节点,统一版本后再开始升级。如果已经出现节点不一致,把所有节点的文件补齐,然后逐个实例重启,不要在只升了一半的状态下继续导数据。

5.3 只升 CDB 不升 PDB,impdp 到 PDB 依旧 ORA-39405

现象:CDB 根容器两个视图都显示 42,但进入某个 PDB 查询还是 32,往这个 PDB 导入数据照样报 ORA-39405。

原因:19c 的 PDB 各自维护 TZ_VERSION,CDB 根升级成功不代表 PDB 也升级成功。漏掉ALTER PLUGGABLE DATABASE ALL UPDATE TIMEZONE FILE,或者执行时 PDB 还是 MOUNT 状态,命令没真正生效。

解决:回到 CDB 根容器,确认 PDB 是 OPEN 状态,执行ALTER PLUGGABLE DATABASE ALL OPEN UPGRADE,再执行ALTER PLUGGABLE DATABASE ALL UPDATE TIMEZONE FILE,最后逐个进入 PDB 验证版本并各自跑一次DBMS_DST.END_UPGRADE()。

5.4 错误表建在 SYSTEM 表空间导致 ORA-01650

现象:执行CREATE_ERROR_TABLE或END_UPGRADE时报ORA-01650: unable to extend segment,错误表建到一半写不进去。

原因:默认建在 SYSTEM 表空间,而 SYSTEM 剩余空间不足。常见于建库时没给 SYSTEM 分足够空间,或者升级前没有清理过 SYSTEM 里的历史碎片。

解决:建错误表时把表空间指定到业务使用的表空间,比如应用默认表空间TBS_APP。如果错误表已经建了一半,先DBMS_DST.DROP_ERROR_TABLE删掉重建。升级前看一眼表空间剩余量,留足 100MB 以上比较保险。

5.5 升级到 42 后,业务报表时间差一小时,做 DST 回归

现象:升级完成后,某张报表按本地时间统计,发现 2024 年春季的部分数据比预期早一小时或晚一小时,业务质疑数据导错了。

原因:时区版本从 32 到 42 修正了部分地区的夏令时规则,旧数据在旧规则下计算出一个结果,在新规则下计算结果不同。这是预期的行为变化,不是升级失败。

解决:升级前挑几个关键业务时间点做快照,比如2024-03-10 02:30:00 US/Eastern,记录转换结果;升级后跑同样的 SQL 对比。如果差异在 DST 规则变化范围内,向业务方说明这是新版本规则修正的结果。同时提醒应用侧清理连接池,避免新旧会话混用。

6. 三张视图一次重导,十分钟验证 32->42 没有白做

升级完别急着宣布完成,花十分钟做一轮收尾验证。我的习惯是固定查三张视图,再做一次真实导入测试,全部通过才算落地。

第一组验证是版本一致性:

SELECT VERSION FROM V$TIMEZONE_FILE; SELECT TZ_VERSION FROM REGISTRY$DATABASE;

在 CDB 根容器和每个 PDB 里各执行一次,结果必须全部是 42,且两边一致。任何一处显示 32,都要回到第 3 章重走。

第二组验证是 DST 边界时间转换。拿一个升级前容易出错的时区,比如US/Eastern的 2024-03-10 凌晨 2 点,执行转换:

SELECT FROM_TZ(TIMESTAMP '2024-03-10 02:30:00', 'US/Eastern') AS EASTERN_TS FROM DUAL;

如果这条 SQL 能正常返回时间戳而没有报错,说明新时区文件对 2024 年的 DST 规则已经生效。升级前在 32 版本上执行同样语句,要么报 ORA-01882,要么转换结果和 42 不同。

第三组验证是数据泵连通性测试。建一张只含一列 TSTZ 的小表,导出再导入,整个过程不超过两分钟:

CREATE TABLE TSTZ_REGRESSION (ID NUMBER, TS TIMESTAMP WITH TIME ZONE); INSERT INTO TSTZ_REGRESSION VALUES (1, TIMESTAMP '2024-03-10 02:30:00 US/Eastern'); COMMIT;

然后分别执行 expdp 和 impdp,导入后查一下数据是否保留区域名和正确的 UTC 偏移。这组测试能提前暴露连接串、目录对象、权限等环境问题,避免真正导业务数据时才发现。

我个人的习惯是,升级完成后把这次变更的所有输出存一份:三个版本的查询结果、错误表里是否有记录、导入日志最后 50 行,一起放到变更记录里。以后业务方说"时间好像不对",直接翻当时的快照对照,省去很多扯皮。如果这次升级是为了解决数据泵 TSTZ 报错,那最后一步一定是用真实的 dump 重导一次业务表,让 impdp 日志里出现正常完成的字样,这件事才算翻篇。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询