Oracle 12c+ 升级时区文件前必须先执行 DBMS_DST.BEGIN_PREPARE 声明准备,否则 UPGRADE_TIMEZONE 将报 ORA-30078 错误;该步骤不锁表但要求目标版本合法且无未处理时区敏感表,失败后仅能在 BEGIN_PREPARE 至 END_PREPARE 间软回退。
Oracle 12c+ 升级时区文件前必须调用 DBMS_DST.BEGIN_PREPARE
不执行这一步就直接 upgrade_timezone,会报错 ora-30078: time zone file not in upgrade mode。这是 oracle 强制的流程校验——它要求你先“声明准备升级”,系统才会把时区文件切换到可写状态。
常见错误是跳过准备阶段,直接跑升级脚本;或者在 BEGIN_PREPARE 后没验证就继续,结果发现目标版本不支持当前数据库版本(比如 DB 是 19c,却想升到 tzfile v45,而官方只允许升到 v44)。
-
DBMS_DST.BEGIN_PREPARE只接受一个参数:version_number,必须是SELECT * FROM V$TIMEZONE_FILE中列出的合法版本号 - 执行后查
DBA_TSTZ_TABLES,确认没有未处理的时区敏感表(否则后续END_PREPARE会失败) - 准备阶段不锁表、不阻塞 DML,但不能同时启动另一个准备任务
升级失败后如何回退到原时区文件版本
Oracle 不提供自动回滚 UPGRADE_TIMEZONE 的命令。一旦执行成功,V$TIMEZONE_FILE.VERSION 就永久变更,回退只能靠还原备份或重建数据库——所以升级前必须有完整 RMAN 备份,且备份里包含旧版 timezlrg.dat 文件。
真正能“软回退”的窗口只在 BEGIN_PREPARE 到 END_PREPARE 之间:此时还没改数据字典,执行 DBMS_DST.END_PREPARE 就能退出准备态,相当于什么都没发生。
- 如果已执行
UPGRADE_TIMEZONE但尚未重启实例,可以尝试ALTER SYSTEM SET TIME_ZONE = 'OLD_VALUE' SCOPE=SPFILE,但这只影响新会话的SYSTIMESTAMP显示,不改变已存储的TIMESTAMP WITH TIME ZONE值 - 检查
DBA_TSTZ_TAB_COLUMNS中所有TIMESTAMP WITH TIME ZONE列是否已完成调整(STATUS = 'SUCCESS'),失败项会导致部分数据时区偏移异常 - 升级后首次重启时,Oracle 会自动运行时区调整作业(
DBMS_DST.FIND_AFFECTED_TABLES触发),这个过程可能持续数小时,期间不能停库
为什么 DBMS_DST.FIND_AFFECTED_TABLES 返回空结果但升级仍报错
这个函数只扫描当前用户下含 TIMESTAMP WITH TIME ZONE 的表,但 Oracle 内部组件(如审计表 AUD$、调度日志 SCHEDULER$_JOB_RUN_DETAILS)也可能含时区列——它们不归普通用户管,但升级时照样要处理。
典型现象是:自己建的表全 OK,FIND_AFFECTED_TABLES 返回空,可 UPGRADE_TIMEZONE 还是卡住并报 ORA-30088: datetime/interval precision is out of range。
- 必须用
SELECT OWNER, TABLE_NAME FROM DBA_TAB_COLUMNS WHERE DATA_TYPE = 'TIMESTAMP WITH TIME ZONE'全库扫描,别只信FIND_AFFECTED_TABLES - 某些 Oracle 组件表(如
WRI$_ADV_SQLT_PLANS)在升级过程中被隐式访问,若其所在表空间 offline 或段损坏,也会中断升级流程 - 升级前建议临时禁用审计(
AUDIT_TRAIL=NONE)和调度器(EXEC DBMS_SCHEDULER.DISABLE('SYS.SCHEDULER$_EVENT_QUEUE')),减少干扰
19c 数据库升级时区文件后 SYSTIMESTAMP 显示不对
不是时区文件问题,而是 SYSTIMESTAMP 永远返回 OS 系统时间 + 主机时区偏移,和数据库时区(DBTIMEZONE)无关。很多人误以为升级后 SYSTIMESTAMP 应该跟着 DBTIMEZONE 变,其实不会。
真正受时区文件升级影响的是:TIMESTAMP WITH TIME ZONE 字段的解析逻辑、FROM_TZ 函数对缩写(如 PST/PDT)的识别、以及跨 DST 边界的时间计算精度。
- 查当前数据库时区用
SELECT DBTIMEZONE FROM DUAL,它默认是 ‘+00:00’,除非你显式执行过ALTER DATABASE SET TIME ZONE - 若需让应用看到统一时区时间,应使用
CAST(SYSTIMESTAMP AS TIMESTAMP WITH TIME ZONE) AT TIME ZONE 'Asia/Shanghai',而不是依赖DBTIMEZONE - 升级后第一次查询
V$TIMEZONE_NAMES可能变慢,因为 Oracle 要重新加载新版时区规则树,属正常现象
事情说清了就结束。最麻烦的从来不是命令怎么敲,而是升级前漏掉某个隐藏的时区列,或者以为备份了数据就等于备份了时区上下文。