关于ORACLE时区问题 ----------为什么我的JOB总不是在我想执行的时候跑

本文介绍了Oracle数据库中关于时区的问题,包括数据库时区和session时区的区别,如何查看和修改时区,以及不同时间类型在时区转换中的表现。通过示例展示了时区设置如何影响SYSDATE和SYSTIMESTAMP等函数的返回结果。

 我们的成功总是站在巨人的肩膀上我,先转了别人经验,最后再总结自己的实验结果,学习才能有进步!

Oracle 9i 开始多了 3 个关于时间的数据类型:TIMESTAMP [(precision)] TIMESTAMP [(precision)] WITH TIME ZONE TIMESTAMP [(precision)] WITH LOCAL TIME ZONE,其中 TIMESTAMP [(precision)] WITH TIME ZONE 保存了时区信息。

1. Oracle 的时区设置

 

    Oracle 的时区可以分为两种,一种是数据库的时区,一种是 session 时区,也就是客户端连接时的时区(经过实验,连接以后再修改客户端的时区,session 的时区不会更改)。

 

    数据库的时区在创建数据库时可以通过在 create database 语句中加上 SET TIME_ZONE = ' { { + | - } hh : mi | time_zone_region } ' 来指定,如果,不指定,默认是按照数据库所在的操作系统时区来设定的。创建之后,可以通过 alter database 来修改。其中 time_zone_region 参数可以通过查询 V$TIMEZONE_NAMES 动态视图来获得所有支持的值。修改之后,需要重启数据库才能生效。经常有人会碰到无法修改的情况:

 

SQL> alter database set time_zone='+06:00';
alter database set time_zone='+06:00'
*
ERROR at line 1:
ORA-02231: missing or invalid option to ALTER DATABASE

 

    TOM 对此问题有过解释TIME_ZONE 的设定主要是为了 WITH LOCAL TIME ZONE,当 session 的时区和数据库的时区不同时,oracle 根据时区的差距转换到数据库的时间,再保存到数据库的 WITH LOCAL TIME ZONE 类型中,他是不保存时区的,所以需要 TIME_ZONE 来进行各种时区之间时间的转换(WITH TIME ZONE 类型保存了原始的时区,所以不需要 TIME_ZONE 的设置也可以进行各种时区之间的转换)。但数据库中一旦有了该类型,就不能通过 alter database 修改时区了,会得到上面的错误,可以通过下面的语句获得所有包含该类型的表,将他们删除之后,再修改。

 

select u.name || '.' || o.name || '.' || c.name TSLTZcolumn
  from sys.obj$ o, sys.col$ c, sys.user$ u
where c.type# = 231
   and o.obj# = c.obj#
   and u.user# = o.owner#;

(一般查询后的结果为:OE.ORDERS.ORDER_DATE,指的是OE用户下的ORDERS表的ORDER_DATE字段使用了时区的信息:WITH LOCAL TIME ZONE,将此信息去掉就可以再修改了,修改好了之后需要重启数据库才能生效)

    Session 的时区是根据客户端的时区来决定的,当然连接以后也可以通过 alter session 来改变。WITH LOCAL TIME ZONE 类型会根据 TIME_ZONE 的设置,自动把时间转换为 session 所在时区的时间显示出来,而 WITH TIME ZONE 因为保存了时区,不需要根据 TIME_ZONE 的设置来转换。

 

2. 查看时区

 

    可以分别使用 SESSIONTIMEZONE / DBTIMEZONE 内建函数查看 session 和数据库时区:

 

 

SYS@SKYDB> select dbtimezone from dual;

 

DBTIME
------
+08:00

 

SYS@SKYDB> select sessiontimezone from dual;

 

SESSIONTIMEZONE
---------------------------------------------
+09:00

 


   
另外可以用 TZ_OFFSET 查询某时区和 UTC 之间的差值。

 

 

TZ_OFFSET ( { 'time_zone_name'
                        | '{ + | - } hh : mi'
                        | SESSIONTIMEZONE
                        | DBTMEZONE  }
                      )

 

SELECT TZ_OFFSET('US/Eastern') FROM DUAL;

 

TZ_OFFS
-------
-04:00

SELECT TZ_OFFSET(DBTIMEZONE) FROM DUAL;

TZ_OFFSET(DBTI
--------------
+08:00

 

    其中 time_zone_name 也可以从 V$TIMEZONE_NAMES 获得。

 

3. 几个内建时间函数的比较

 

    sysdate/systimestamp 都是返回数据库的时间并且使用数据库的时区,他们返回的是操作系统的时间。sysdate 返回的是 date 类型,没有时区信息,操作系统上是什么时间就返回什么时间;systimestamp 返回 TIMESTAMP WITH TIME ZONE 类新,有时区信息:

 

 

SYS@SKYDB> select sysdate from dual;

 

SYSDATE
-------------------
2006-08-03 10:01:31

SYS@SKYDB> select systimestamp from dual;

 

SYSTIMESTAMP
-----------------------------------------------
03-AUG-06 10.02.21.093000 AM +08:00

SYS@SKYDB> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.

修改操作系统时区为 +02:00

SYS@SKYDB> startup
ORACLE instance started.

 

Total System Global Area   89202456 bytes
Fixed Size                   454424 bytes
Variable Size              62914560 bytes
Database Buffers           25165824 bytes
Redo Buffers                 667648 bytes
Database mounted.
Database opened.

SYS@SKYDB> select sysdate from dual;

 

SYSDATE
-------------------
2006-08-03 04:03:37

 

SYS@SKYDB> select systimestamp from dual;

 

SYSTIMESTAMP
----------------------------------------------
03-AUG-06 04.04.15.687000 AM +02:00

    注:这是我单位机子上实验的结果,由于建了多个数据库,不知道为什么不能通过 ipc 来连接本地数据了,登陆时使用 sqlplus "/@skydb as sysdba",也就是使用了监听器来连接,但在家里做相同的实验,通过 ipc 连接 sqlplus "/as sysdba",修改时区后,sysdate 依然显示修改前的时间,而 systimestamp 却正确,不知道是什么原因:

SQL> select sysdate from dual;

SYSDATE
-------------------
2006-02-08 22:21:40

SQL> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 10.22.38.578000 PM +08:00

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
修改时区为 +09:00
SQL> startup
ORACLE instance started.

Total System Global Area  131145064
bytes

Fixed Size                   453992
bytes

Variable Size             109051904
bytes

Database Buffers           20971520
bytes

Redo Buffers                 667648

bytes

Database mounted.
Database opened.
SQL> select sysdate from dual;

SYSDATE
---------
02-AUG-06

SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';

Session altered.

SQL> select sysdate from dual;

SYSDATE
-------------------
2006-08-02 22:32:59             
<- 还是之前的时间

SQL> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 11.35.05.171000 PM +09:00         
<- 时间正确

 

    另外,有个初始化参数 fixed_date,可以设置 sysdate 返回指定的时间:

 

 

alter system set fixed_date='2005-04-04-11-00-00'

 

this fixed_date is normally used, in oracle, for dubugging purpose.

 

once finishing it, you can set it back:

 

alter system set fixed_date=none

 

    Eygle 的关于这个参数的相关文章:Why sysdate is fixed

    current_timestamp/current_date
也会返回数据库的时间,但转换为 session 的时区进行显示,可以使用 alter session set time_zone 改变 session 时区。

 

4. 四个日期时间类型的实验

 

SQL> select dbtimezone from dual;

DBTIME
------
+06:00

SQL> select sessiontimezone from dual;

SESSIONTIMEZONE
---------------------------------------------------------------------------
+08:00

SQL> ed
Wrote file afiedt.buf

  1  create table tztest(a date,
  2  b timestamp(0),
  3  c timestamp(0) with time zone,
  4* d timestamp(0) with local time zone)
SQL> /

Table created.

SQL> alter session set nls_date_format ='yyyy-dd-mm hh24:mi:ss';

Session altered.

SQL> select sysdate from dual;

SYSDATE
-------------------
2006-02-08 22:21:40

SQL> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 10.22.38.578000 PM +08:00

SQL> select current_date from dual;

CURRENT_DATE
-------------------
2006-02-08 22:23:50

SQL> select current_timestamp from dual;

CURRENT_TIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 10.24.04.031000 PM +08:00

SQL> insert into tztest
  2  values(sysdate,systimestamp,systimestamp,systimestamp);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from tztest;
2006-02-08 22:25:59
02-AUG-06 10.25.59 PM
02-AUG-06 10.25.59 PM +08:00
02-AUG-06 10.25.59 PM

SQL> exit
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.3.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.3.0 - Production

修改了客户端操作系统的时区

C:/Documents and Settings/Administrator>sqlplus sky/xxxx

SQL*Plus: Release 9.2.0.3.0 - Production on Wed Aug 2 23:28:01 2006

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.3.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.3.0 - Production

SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';

Session altered.

SQL> select sysdate from dual;

SYSDATE
-------------------
2006-08-02 22:28:49       
<-数据库没有重启,时间依然是修改前的

SQL> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 11.29.33.609000 PM +09:00 
<- 这里却已经改变了,有时区信息,自动转换了?
SQL> select * from tztest;
2006-08-02 22:25:59                      
<- 没变
02-AUG-06 10.25.59 PM                  <- 没变
02-AUG-06 10.25.59 PM +08:00      <- 保存时区信息
02-AUG-06 11.25.59 PM                  <-自动转换为 session 的时区

总结:

可以通过这个SQL来查看系统时区与系统时间的差异,以systimestamp里的时区为准

select sysdate from dual

union all

select current_date from dual

union all

select systimestamp from dual;

 

<think> 我们聚焦于分析给定的SQL查询语句: SELECT JOB_NAME, STATE, (END_DATE - START_DATE)*86400 duration FROM USER_SCHEDULER_JOB_RUN_DETAILS WHERE JOB_NAME = 'FULL_BACKUP_JOB' ORDER BY LOG_DATE DESC FETCH FIRST 1 ROW ONLY 这个查询的目的是获取名为'FULL_BACKUP_JOB'的Oracle调度任务的最新一次运行详情,包括任务名称、状态和持续时间(以秒为单位)。 下面我们将详细解析这个查询的各个部分及其实现效果: 1. **字段选择**: - `JOB_NAME`:作业的名称,这里我们指定了'FULL_BACKUP_JOB'。 - `STATE`:作业运行的状态(例如:'SUCCEEDED'、'FAILED'等)。 - `(END_DATE - START_DATE)*86400 duration`:计算作业运行持续时间的表达式。因为Oracle中日期相减得到的是天数,所以乘以86400(一天的秒数)将其转换为秒。 2. **数据来源**: - `USER_SCHEDULER_JOB_RUN_DETAILS`:这是Oracle提供的一个视图,它记录了当前用户可访问的调度作业的详细信息,包括每次运行的开始时间、结束时间、状态等。 3. **过滤条件**: - `WHERE JOB_NAME = 'FULL_BACKUP_JOB'`:只选择名为'FULL_BACKUP_JOB'的作业记录。 4. **排序和限制**: - `ORDER BY LOG_DATE DESC`:按日志日期(即作业运行的记录时间)降序排列,这样最近运行的记录会排在最前面。 - `FETCH FIRST 1 ROW ONLY`:只取排序后的第一行,即最近一次的运行记录。 实现效果结: 该查询会返回'FULL_BACKUP_JOB'作业最近一次运行的以下信息: - 作业名称(JOB_NAME):固定为'FULL_BACKUP_JOB' - 运行状态(STATE):最近一次运行的状态(成功、失败等) - 持续时间(duration):最近一次运行实际消耗的时间(秒) 典型返回结果示例: - 如果最近一次运行成功:`('FULL_BACKUP_JOB', 'SUCCEEDED', 3600)` 表示运行了3600秒(1小时)。 - 如果最近一次运行失败:`('FULL_BACKUP_JOB', 'FAILED', 120)` 表示运行了120秒后失败。 - 如果该作业从未运行过,则查询返回空结果(无行)。 注意事项: - 如果作业正在运行中,那么`END_DATE`可能为NULL,此时计算出来的持续时间也会是NULL(因为NULL参与运算结果为NULL)。因此,对于正在运行的作业,此查询返回的持续时间是NULL。 在监控脚本中的应用: 在之前的监控脚本中,我们使用了类似的查询来获取全量备份任务和增量备份任务的状态和持续时间。通过这个查询,我们可以快速判断备份任务是否成功以及耗时情况,进而推送至监控系统并触发告警。 §§相关问题§§ 1. 如何监控Oracle备份任务的日志轮转和归档状态? 2. 当增量备份频繁失败时,应该检查哪些关键指标? 3. 如何优化Oracle备份监控脚本以支持大规模数据库实例? 4. 在备份任务超时时,如何自动获取相关的Oracle等待事件信息? 5. 如何将Oracle备份监控与企业微信机器人通知深度集成? </think> ### SQL查询语句解析:`SELECT JOB_NAME, STATE, (END_DATE - START_DATE)*86400 duration` #### 一、查询语句功能解析 ```sql SELECT JOB_NAME, STATE, (END_DATE - START_DATE)*86400 duration FROM USER_SCHEDULER_JOB_RUN_DETAILS WHERE JOB_NAME = 'FULL_BACKUP_JOB' ORDER BY LOG_DATE DESC FETCH FIRST 1 ROW ONLY ``` #### 二、实现效果详解 1. **核心数据获取**: - **`JOB_NAME`**:精确提取指定备份任务名称(`'FULL_BACKUP_JOB'`) - **`STATE`**:返回任务最近一次执行状态(`SUCCEEDED`/`FAILED`) - **`(END_DATE - START_DATE)*86400 duration`**:计算任务执行耗时(秒) 2. **时间计算逻辑**: $$ \text{duration} = (\text{END\_DATE} - \text{START\_DATE}) \times 86400 $$ - Oracle日期相减**返回天数差值** - 乘以86400(24×60×60)**转换为秒** - 示例:若运行1.5小时(0.0625天),则返回 `0.0625 × 86400 = 5400` 秒 3. **结果集控制**: - `ORDER BY LOG_DATE DESC`:按日志时间**降序排列** - `FETCH FIRST 1 ROW ONLY`:仅返回**最近一次执行记录** #### 三、典型返回结果示例 | 场景 | 返回结果 | 说明 | |--------------------|-----------------------------------|------| | 成功执行 | `('FULL_BACKUP_JOB', 'SUCCEEDED', 3825)` | 耗时3825秒 | | 执行失败 | `('FULL_BACKUP_JOB', 'FAILED', 120)` | 运行120秒后失败 | | 任务未完成 | `('FULL_BACKUP_JOB', 'RUNNING', NULL)` | `END_DATE`为空导致duration为NULL | | 无历史执行记录 | **空结果集** | 查询返回0行 | #### 四、关键注意事项 1. **时区影响**: -数据库与应用时区不一致,需使用`AT TIME ZONE`转换: ```sql (CAST(END_DATE AT TIME ZONE 'UTC' AS DATE) - CAST(START_DATE AT TIME ZONE 'UTC' AS DATE)) * 86400 ``` 2. **空值处理**: - 使用`NVL`避免NULL值中断监控: ```sql NVL((END_DATE - START_DATE)*86400, 0) duration ``` 3. **高精度计时**: - 需要毫秒级精度时改用时间戳: ```sql EXTRACT(SECOND FROM (END_TIMESTAMP - START_TIMESTAMP)) duration_ms ``` #### 五、实际应用场景 ```python # Python处理查询结果示例 cursor.execute(query) row = cursor.fetchone() if row: job_name, status, duration = row if status == 'SUCCEEDED': print(f"✅ 备份成功 | 耗时: {duration//3600}h{(duration%3600)//60}m") else: print(f"❌ 备份失败 | 运行: {duration}秒") else: print("⚠️ 未找到执行记录") ``` #### 六、性能优化建议 1. **索引优化**: ```sql CREATE INDEX IDX_JOB_LOG ON USER_SCHEDULER_JOB_RUN_DETAILS (JOB_NAME, LOG_DATE DESC); ``` 2. **查询改进**: - 添加状态过滤减少扫描: ```sql WHERE JOB_NAME = 'FULL_BACKUP_JOB' AND LOG_DATE > SYSDATE - 30 /* 仅查30天内记录 */ ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值