简介Oracle 19c数据库时区版本由32升级至42时数据泵导入导出遇到TSTZ带时区时间戳字段报错是常见问题。这份资料面向Oracle DBA、数据迁移工程师以及负责跨时区业务的运维人员提供从问题定位到解决的完整方案。压缩包共10个文件、总大小约377KB包含6个dat时区数据文件、2个xml配置映射信息、2个txt说明文档可用于替换或补充时区数据并指导调整数据泵参数。内容详细讲解时区版本差异、TSTZ兼容性处理、导入前预处理与升级后验证等关键环节特别给出备份恢复、参数配置和最佳实践建议同时涵盖升级前影响评估、业务影响分析与时间相关功能测试等注意事项。对处理跨时区数据、执行数据库升级或数据泵导入导出的运维与开发人员有直接参考价值。目前已有1367人学习下载。1. 时区版本 32 与数据泵 TSTZ 报错不是升级补丁包就能绕过去Oracle 19c 数据泵导入时报 TSTZ 版本冲突十有八九卡在这源库时区版本已经到了 42目标库还是 32impdp 一看到 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 再查 PDB19c 的时区版本按容器拆开看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时区版本 422024 年以后部分地区的 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-01882ORA-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/密码源库服务名 \ directoryDATA_PUMP_DIR \ dumpfileSRC_42.DMP \ logfileEXP_SRC_42.log \ schemasAPPUSER \ parallel4导出参数里没有专门指定 TSTZ 版本的选项。expdp 会自动把源库当前的时区版本号写进 dump 文件元数据也就是 42。schemasAPPUSER指定导出哪个用户的对象parallel4是并行度导出大 schema 时可以提高速度但要注意源库的 CPU 和 I/O 负载。目标库侧导入impdp system/密码目标库服务名 \ directoryDATA_PUMP_DIR \ dumpfileSRC_42.DMP \ logfileIMP_SRC_42.log \ schemasAPPUSER \ transformsegment_attributes:n \ parallel4 \ table_exists_actionskip导入端关键差异是table_exists_actionskip如果上次导入失败时已经创建了部分表跳过已存在的表避免重复建表报错。transformsegment_attributes:n是让导入时忽略源库表空间的段属性统一落到目标库默认表空间适合跨环境迁移。特别注意不要试图在 impdp 里通过VERSION参数去糊弄 TSTZ 检查。数据泵检查的是 dump 里记录的时区版本号不是对象版本设置VERSION19.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 不升 PDBimpdp 到 PDB 依旧 ORA-39405现象CDB 根容器两个视图都显示 42但进入某个 PDB 查询还是 32往这个 PDB 导入数据照样报 ORA-39405。原因19c 的 PDB 各自维护 TZ_VERSIONCDB 根升级成功不代表 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 日志里出现正常完成的字样这件事才算翻篇。希望帮到你。本文还有配套的精品资源点击获取
