Oracle DST时区升级与DBMS_DST实操指南

发布时间:2026/10/27 13:36:31
Oracle DST时区升级与DBMS_DST实操指南 简介面向 Oracle DBA 与数据库运维人员的时区版本升级脚本用于配合官方时区补丁把数据库 DST 时区版本调整到最新以缓解旧时区引发的跨时区时间换算偏差、定时任务与业务数据对不齐等问题适合需要按规范流程完成时区升级、又希望降低误操作风险的中高级维护场景。压缩包共 4 个文件全部为 sql 脚本整体约 16KB体积轻巧按用途分为检查类与应用类脚本另含时区字段统计脚本可辅助核对带时区与不带时区数据在升级前后的处理情况。资源已有 1693 人学习下载脚本内置说明与执行提示便于读者先确认当前时区状态再评估升级影响并保留校验与排错思路可作为升级前的预检查和升级后的结果比对工具减少反复试错成本。1. 时区版本对不上先别急着改数据某次跨年之后一张订单表的TIMESTAMP WITH TIME ZONE列按本地时区查出来整体偏了一小时。应用侧先怀疑 NTP再怀疑连接串参数最后落到v$timezone_file上——数据库内置的时区规则还停在旧版本新规则根本没进来。这类问题不报错只静默算错等你发现时已经是一批对不上的报表。DBMS_DST_scriptsV1.9.zip就是冲这个场景来的它把 Oracle 的DBMS_DST包调用按阶段拆成可执行的 SQL 脚本覆盖版本核查、准备模式、正式升级、结果校验。适合三类人要过版本合规检查的 DBA、做大版本升级前要做预检的从业者、以及被 TSTZ 数据坑过一次、想留一份可复现操作记录的人。2. DBMS_DST 的版本账v$timezone_file 与三张定位视图2.1 先确认「现在是哪一版」时区版本不是一个可以拍脑袋填的数字。Oracle 把时区规则编译成一份内部文件数据库启动时加载版本号就记在v$timezone_file里。这个视图通常只有一行version列是当前生效的版本filename是底层文件名。真正做升级时目标版本必须来自v$timezone_version那是当前数据库版本所能支持到的版本清单不是网上随便抄一个数字就能用。-- 当前数据库正在使用的时区文件版本 SELECT version, filename FROM v$timezone_file; -- 当前数据库版本支持/可切换到的 DST 版本清单 SELECT * FROM v$timezone_version ORDER BY 1;第一段查询的作用是拿到基线。升级前后各跑一次把version值记下来这是判断升级有没有真正生效的唯一硬指标——脚本跑完不报错、但v$timezone_file没变的情况我在现场见过不止一次。第二段查询给的是可选版本范围DBMS_DST.BEGIN_PREPARE和BEGIN_UPGRADE传进去的那个数字就应当来自这里。参数上要注意v$timezone_version在不同小版本上返回的列不完全一致有的版本只有版本号有的会带状态说明。写脚本时不要SELECT *后按列位置取值一律按列名取否则换一个库就翻车。2.2 TSTZ、TSLTZ 与 DATE 三种列型到底谁受影响很多人以为时区升级是「数据库层面的事」跟表没关系。实际上受影响的只有带时区语义的列而且影响方式各不相同。TIMESTAMP WITH TIME ZONETSTZ存的是绝对时间加时区偏移规则变化后同一个绝对时刻在不同偏移下的呈现可能改变历史数据在按新规则换算时可能出现偏差。TIMESTAMP WITH LOCAL TIME ZONETSLTZ底层存 UTC展示时按会话时区换算规则表一变换算结果就变它是最容易被忽略的一类。DATE不带时区本身不受影响但如果应用层拿着DATE去做FROM_TZ转换问题会从应用侧冒出来。所以升级前必须先定位对象。DBA_TSTZ_TABLES汇总了含 TSTZ 列的表DBA_TSTZ_TAB_COLS精确到列。这两张视图是官方给的定位入口比手工去DBA_TAB_COLUMNS里筛DATA_TYPE靠谱因为后者会把 TSLTZ 也混进来而两者的处理策略并不相同。-- 哪些表含 TSTZ 列按 owner 聚合先看规模 SELECT owner, COUNT(*) AS tstz_table_cnt FROM dba_tstz_tables WHERE owner NOT IN (SYS,SYSTEM,SYSAUX) GROUP BY owner ORDER BY tstz_table_cnt DESC; -- 精确到列确认是否还有 TSLTZ 列混在里面 SELECT owner, table_name, column_name, data_type FROM dba_tstz_tab_cols WHERE owner NOT IN (SYS,SYSTEM,SYSAUX) ORDER BY owner, table_name, column_name;第一段解决「有多少张表要过一遍」的规模问题第二段解决「具体是哪一列」的定位问题。参数上排除系统 schema 是为了避免把数据字典表也卷进来SYS下的对象由数据库自己处理脚本去碰反而容易出问题。如果业务 schema 数量多建议把这两段结果落到一张临时表里后续按 owner 分批处理别一次性UPGRADE_ALL_TABLES。2.3 脚本包为什么按「准备 / 升级」两段拆DBMS_DST的流程设计有一个很关键的分界准备阶段只做「预演」把受影响的行写进一张错误表不改业务表数据正式升级阶段才真正改写 TSTZ/TSLTZ 的存储值。这个设计给了操作者一个缓冲窗口——准备阶段跑完发现错误行太多、或者某张核心表转换失败可以停下来处理不必硬着头皮往下走。DBMS_DST_scriptsV1.9.zip里的脚本就是按这个分界组织的一组负责前置检查和错误表创建一组负责BEGIN_PREPARE到END_PREPARE一组负责BEGIN_UPGRADE到END_UPGRADE最后一组负责校验和索引状态复查。常见做法是先把前两组在预发库上完整跑一遍记录耗时和错误行分布再决定生产窗口给多长。注意准备阶段会在数据库内创建一批DST$前缀的内部对象属于正常现象不要手工清理清理会导致后续升级状态不一致。这里有个容易误解的点准备阶段可以重复执行升级阶段不能。BEGIN_PREPARE之后即使中断再跑一次也能重新开始BEGIN_UPGRADE一旦提交数据库就进入升级模式中途退出只能靠恢复手段。脚本把这两段物理分开本质上是在提醒操作者——前半段可以试错后半段只能一次做对。3. 准备阶段跑通CREATE_ERROR_TABLE 到 END_PREPARE 的四步3.1 前置检查权限、打开模式、空间三件事动手之前有三件事必须先确认任何一件没到位后面都会以奇怪的方式失败。第一是权限。整套操作必须以SYSDBA身份执行普通 DBA 角色不够DBMS_DST的部分过程会触碰内部对象。第二是打开模式数据库必须处于READ WRITE且非受限状态RESTRICTED模式下BEGIN_PREPARE会直接拒绝。第三是空间准备阶段要在系统表空间或指定表空间里写错误行受影响行数多的时候增长很快临时表空间和 UNDO 也要留够。-- 确认打开模式与受限状态 SELECT name, open_mode, restricted FROM v$database; -- 确认当前会话权限 SELECT * FROM session_privs WHERE privilege LIKE %SYSDBA%; -- 预估受影响行规模以一张表为例替换成实际表名 SELECT COUNT(*) FROM app_user.order_tbl WHERE created_tz IS NOT NULL;第一段判断数据库是否处于可操作状态open_mode必须是READ WRITErestricted必须是NO。第二段确认会话权限返回空说明当前不是 SYSDBA后面所有步骤都会失败。第三段是经验性的规模预估COUNT(*)在大表上代价不低可以先用DBA_TAB_PARTITIONS的NUM_ROWS做粗估再挑一两张大表精确验证。注意准备阶段写入的错误行会占空间脚本里建议把错误表单独放到一个有空闲配额的表空间不要留在SYSTEM。另一个前置项是补丁版本。11.2.0.1 上DBMS_DST.UPGRADE_ALL_TABLES不可用只能逐表调用UPGRADE_TABLE11.2.0.2 之后才提供批量接口。如果你的库还停在早期小版本脚本里的批量调用会编译不过需要改成循环逐表处理。这一点脚本包的说明里通常会写但实际执行的人经常跳过说明直接跑。3.2 建错误表与 BEGIN_PREPARE错误表是准备阶段唯一的输出载体所有转换失败的行都会记录在这里。它只需要建一次重复执行会报ORA-00955对象名已存在属于可以忽略的提示但更稳妥的做法是先判断再建。-- 建错误表owner 和表名两个参数建议固定用 SYS 下的统一命名 BEGIN DBMS_DST.CREATE_ERROR_TABLE(SYS, DST_ERROR_TABLE); EXCEPTION WHEN OTHERS THEN IF SQLCODE -955 THEN DBMS_OUTPUT.PUT_LINE(error table already exists, skip); ELSE RAISE; END IF; END; /这段的写法比脚本里裸调CREATE_ERROR_TABLE更稳WHEN OTHERS只吞掉-955其它异常照常抛出。参数上第一个是 owner第二个是表名两者都建议写死不用变量避免脚本在不同库上生成不同名字、后续查询对不上。接下来进入准备模式。传进去的版本号必须来自第 2 章查到的目标版本示例里的32只是占位换成你实际查到的值。-- 进入准备模式参数为新 DST 版本号 BEGIN DBMS_DST.BEGIN_PREPARE(32); END; / -- 确认准备模式已生效 SELECT version, status FROM v$timezone_file;BEGIN_PREPARE没有返回值执行成功后数据库内部状态切换到准备态此时对 TSTZ 列的某些操作会被限制。第二段查询用来确认状态如果status没有变化说明调用没生效通常是版本号传错或权限不足。这里有个细节BEGIN_PREPARE之后DBMS_DST会在后台准备内部工作表大库上这一步本身就要花几分钟别以为卡死了就去杀会话。3.3 UPGRADE_ALL_TABLES 与错误表怎么读准备模式生效后调用批量接口把所有受影响表过一遍。这个调用会返回一个错误计数但计数只是汇总真正的细节在错误表里。-- 批量处理所有受影响表OUT 参数返回错误行数 DECLARE v_err NUMBER : 0; BEGIN DBMS_DST.UPGRADE_ALL_TABLES(v_err); DBMS_OUTPUT.PUT_LINE(error rows: || v_err); END; /参数只有一个 OUT 型的error_count返回的是写入错误表的总行数不是错误表条数。这个区别很重要一行数据转换失败可能产生多条记录计数和实际记录数对不上是正常的不要拿它去做精确核对。跑完之后查错误表重点看三列表名、列名、错误信息。常见的错误信息集中在时间值超出可表示范围、偏移量非法两类前者通常是历史脏数据后者多半是应用写入了不规范的时区字符串。-- 错误表结构因版本略有差异先看列再取数 SELECT * FROM sys.dst_error_table WHERE ROWNUM 20; -- 按表聚合找出问题最集中的对象 SELECT table_name, COUNT(*) AS err_cnt FROM sys.dst_error_table GROUP BY table_name ORDER BY err_cnt DESC;第一段先小样本看结构避免直接写死列名导致报错。第二段按表聚合目的是排优先级——错误集中在某一张表说明那张表的数据质量问题最严重值得单独处理错误分散在几十张表、每张几行通常是边缘数据可以在升级后逐条修。错误行的处理策略只有两条能修的先修修不了的在准备阶段结束前决定是清理还是隔离。带着大量错误行进入正式升级升级过程本身不会失败但升级后查询这些行会持续报错。3.4 END_PREPARE 之后看什么准备阶段收尾调用END_PREPARE它会清理准备态、释放内部资源。BEGIN DBMS_DST.END_PREPARE; END; / -- 确认状态回落同时保留错误表供后续比对 SELECT version, status FROM v$timezone_file; SELECT COUNT(*) FROM sys.dst_error_table;END_PREPARE无参数。执行后数据库回到正常状态但错误表不会自动清空这是有意设计的——正式升级前你还需要它做对照。第二段查询里v$timezone_file的版本号此时仍是旧值这是正常的真正变更发生在升级阶段。准备阶段结束到正式升级之间建议留一个观察窗口。这段时间业务正常运行但要把错误表结果和业务方对齐哪些表的历史数据需要修、哪些可以接受、哪些要单独走数据订正流程。跳过这个窗口直接升级等于把所有问题压到升级后暴露那时候回退成本高得多。4. 正式升级BEGIN_UPGRADE 之后的窗口操作与索引重建4.1 升级窗口的边界与不可回退点正式升级和准备阶段最大的区别是不可逆。BEGIN_UPGRADE一执行数据库进入升级模式此后的 TSTZ 数据改写是实打实的没有「取消」按钮。所以窗口安排要围绕三个动作来定升级调用、索引重建、重启验证。升级期间只有 SYSDBA 会话能连其它会话会被阻塞或拒绝。这不是故障是设计如此。窗口长度要覆盖「处理全部表 重建失效索引 重启 抽样校验」四段大库上处理全部表这一步本身可能就要几十分钟。-- 正式升级前最后一次确认版本、打开模式、活动会话 SELECT version FROM v$timezone_file; SELECT open_mode FROM v$database WHERE name YOURDB; -- 杀干净非 SYSDBA 会话避免升级过程中被阻塞 SELECT sid, serial#, username, program FROM v$session WHERE username IS NOT NULL AND username NOT IN (SYS,SYSTEM);前两段是例行确认第三段是列出待清理会话实际执行时按需ALTER SYSTEM KILL SESSION。这里不建议在脚本里自动杀会话容易误伤后台作业人工确认一遍更稳。不可回退点就在BEGIN_UPGRADE成功返回的那一刻。此后的时间点如果发现数据异常只能靠备份恢复或者逐表订正没有「撤销升级」这个过程。血泪经验是升级前做一次全库备份并且验证备份可恢复不是只看备份任务成功。4.2 BEGIN_UPGRADE 到 END_UPGRADE 的调用顺序调用顺序和准备阶段几乎一样只是入口过程换成BEGIN_UPGRADE。-- 进入升级模式 BEGIN DBMS_DST.BEGIN_UPGRADE(32); END; / -- 批量处理全部受影响表 DECLARE v_err NUMBER : 0; BEGIN DBMS_DST.UPGRADE_ALL_TABLES(v_err); DBMS_OUTPUT.PUT_LINE(error rows: || v_err); END; / -- 结束升级 BEGIN DBMS_DST.END_UPGRADE; END; /三段调用的参数逻辑和准备阶段一致BEGIN_UPGRADE传目标版本号UPGRADE_ALL_TABLES只返回错误计数END_UPGRADE无参数。区别在于错误处理升级阶段产生的错误行会直接落在业务表上查询时才会报错不会像准备阶段那样只写进错误表。所以升级阶段的第二段调用完成后不要急着END_UPGRADE先做一次抽样查询挑几张核心表按 TSTZ 列过滤一遍。-- 抽样按时间范围查一张核心表的 TSTZ 列 SELECT COUNT(*) FROM app_user.order_tbl WHERE created_tz TIMESTAMP 2024-01-01 00:00:00 08:00;这条查询能过说明该表的 TSTZ 数据转换基本正常报ORA-01843之类的错误说明仍有行没转换成功需要回到错误定位流程。TIMESTAMP ...字面量里的偏移量要和业务实际时区一致写错偏移量会得到一个「看起来能跑但结果不对」的假阳性。4.3 索引失效与重建TSTZ 列上如果建了索引升级过程可能让索引变为UNUSABLE。这一点在官方文档里提得不多但现场很常见——升级完业务查询突然变慢一查执行计划走了全表扫描。-- 找出升级后失效的索引 SELECT index_owner, index_name, table_name, status FROM dba_indexes WHERE status UNUSABLE AND owner NOT IN (SYS,SYSTEM); -- 重建示例按实际索引名替换 ALTER INDEX app_user.idx_order_created REBUILD ONLINE;第一段是全局扫描status UNUSABLE是判定条件排除系统 schema 是为了避免动到不该动的对象。第二段重建时加ONLINE减少对业务的影响代价是重建时间更长、UNDO 占用更多窗口紧张时可以改成离线重建。重建顺序建议按表大小倒序先处理大表避免窗口末尾剩一堆大索引来不及做。同时要检查约束和物化视图TSTZ 列如果参与函数索引或物化视图定义也需要一并验证。4.4 重启与校验v$timezone_file 是唯一硬指标END_UPGRADE之后必须重启数据库时区文件在启动时重新加载不重启版本号不会变。# 重启并确认版本 sqlplus -S / as sysdba EOF SHUTDOWN IMMEDIATE; STARTUP; SELECT version, filename FROM v$timezone_file; EOF这段用 heredoc 的方式在 shell 里跑适合放进运维脚本。SHUTDOWN IMMEDIATE会回滚未提交事务升级窗口内本来就不该有长事务所以是安全的。重启后v$timezone_file的version必须等于你传入的目标版本这是判断升级成功的唯一硬指标其它视图都只是辅助。如果版本号没变先查告警日志里有没有DST相关的错误再看BEGIN_UPGRADE是否真的执行成功。常见的失败原因是版本号传了一个v$timezone_version里不存在的值过程静默返回但实际没生效。5. 避坑与排查五条从现场带回来的记录5.1 五条现象、原因与处置第一条准备阶段报 ORA-30079 或 ORA-01843卡在中途不动。现象是UPGRADE_ALL_TABLES跑到一半抛出时区相关错误进程挂住。原因是表里存在偏移量非法的历史数据比如应用写入了25:00这种不存在的偏移。解决方式是在准备阶段先把这些行查出来用错误表定位到具体主键逐条订正或隔离再重新执行BEGIN_PREPARE。带着脏数据硬跑升级阶段会把这些错误固化到业务表里。第二条多租户环境只在根容器跑了一遍PDB 没升。现象是CDB$ROOT的v$timezone_file版本已经变了切到某个 PDB 里查还是旧版本。原因是DBMS_DST的升级动作作用于当前容器不会自动下推到 PDB。解决方式是切到每个 PDB 内单独执行完整流程脚本包里如果只有一套 SQL需要按容器循环调用。这一点在 12c 之后的环境里尤其容易漏。第三条错误表建在了业务表空间把空间撑爆。现象是准备阶段跑到后半段报ORA-01653无法扩展表空间。原因是CREATE_ERROR_TABLE默认建在调用者的默认表空间如果调用者是业务 schema错误表就落到业务表空间上。解决方式是固定用SYS建表或者建表前显式指定表空间并预留出受影响行数二到三倍的空间。第四条升级后 TSTZ 列查询变慢执行计划走了全表扫描。现象是升级前毫秒级的查询升级后变成秒级。原因是 TSTZ 列上的索引在升级过程中失效状态变成UNUSABLE。解决方式是升级后立刻跑一遍DBA_INDEXES状态检查见 4.3 节的 SQL发现失效索引及时REBUILD ONLINE不要等业务报警才回头查。第五条主备环境只升了主库切换后版本不一致。现象是主库升级后运行正常做一次切换演练后备库的v$timezone_file还是旧值。原因是备库的时区文件不会自动跟随主库更新需要单独处理。解决方式是在主库升级完成后对备库执行同样的重启流程确认版本号对齐后再恢复日志应用。这一步在演练里经常被跳过直到真切换时才发现。5.2 哪些能退、哪些退不了准备阶段的绝大多数操作可以重来。BEGIN_PREPARE中断了再跑一次错误表清了重建都不影响业务数据。这一段可以放心试错也是脚本把准备和升级拆开的价值所在。升级阶段就完全不同。BEGIN_UPGRADE执行后没有官方的回退过程错误行会固化索引会失效版本号会变更。唯一的后悔药是升级前的全库备份并且这个备份必须验证过可恢复。我见过只做了备份任务、没做过恢复演练的案例真出事时发现备份文件不完整那种场面没什么技术手段能救。所以脚本包的正确用法不是「从头到尾一键跑完」而是把准备阶段当成一次完整的演练把错误表结果当成升级前的决策依据。跳过这一步直接冲升级阶段等于把可控的预演变成了不可控的赌博。6. 把 DST 巡检固化成三条 SQL一个 DBA 的收尾习惯6.1 三条常驻巡检 SQL升级做完不代表事情结束。时区版本是会随着数据库大版本升级、补丁应用而变化的下一次可能又对不上。我一般会在监控里挂三条 SQL按周跑一次结果有变化就人工看一眼。-- 巡检一当前时区版本与支持版本的上限是否一致 SELECT (SELECT version FROM v$timezone_file) AS cur_ver, (SELECT MAX(version) FROM v$timezone_version) AS max_ver FROM dual; -- 巡检二含 TSTZ 列的业务表规模数量突变说明有新表接入 SELECT COUNT(DISTINCT owner || . || table_name) AS tstz_tables FROM dba_tstz_tables WHERE owner NOT IN (SYS,SYSTEM,SYSAUX); -- 巡检三失效索引总数升级后这个数字应该归零 SELECT COUNT(*) AS unusable_idx FROM dba_indexes WHERE status UNUSABLE AND owner NOT IN (SYS,SYSTEM);巡检一判断是否需要规划升级cur_ver低于max_ver不一定马上就升但至少要知道差距。巡检二盯的是增量业务新接一张带 TSTZ 的表下一次升级的受影响面就大一分数量突变时值得回头问一句是谁加的。巡检三是个兜底指标正常情况下应该长期为 0非 0 就说明上一次升级遗留了问题没处理干净。参数上没什么可调的三条都是只读查询代价很低唯一要注意的是DBA_TSTZ_TABLES在部分小版本上对分区表的统计口径有差异跨版本对比数字时别太较真绝对值看趋势即可。6.2 一个习惯升级清单先写脚本后跑脚本包最大的价值不是省掉手敲 SQL而是提供了一个固定的执行顺序。我现在的习惯是拿到任何一个带DBMS_DST的操作包先不看脚本内容先照着自己的清单把「版本核查 → 受影响表定位 → 空间确认 → 备份验证 → 准备阶段 → 错误行处置 → 升级窗口 → 索引重建 → 重启校验」九步写一遍再拿脚本去对。对不上的地方就是脚本可能遗漏的地方也是这次操作最可能翻车的地方。这个习惯是被一次准备阶段跳过错误表直接升级的经历逼出来的。当时错误行不多升级本身跑完了版本号也变了看起来一切正常。三个月后业务查一批历史订单TSTZ 列报错追下去发现就是那几行没处理的数据。修是能修但要在大表上定位、停业务、逐条改代价比升级前处理高出十几倍。从那以后我每次做时区升级都强制先跑完准备阶段、把错误表清零或明确隔离才允许执行BEGIN_UPGRADE。DBMS_DST_scriptsV1.9.zip这套脚本解决的是「顺序不乱、调用不错」的问题剩下的判断还是得靠人。把它当成一份可执行的检查表而不是一个一键按钮用起来会顺很多。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询