我有一个Oracle表,其中包含以下列定义:
COLUMN_NAME : RESERVATIONDATE
DATA_TYPE : TIMESTAMP(6)
NULLABLE : Yes
DATA_DEFAULT: null
然后,从Java中执行以下命令:
insert into my_table (col1, col2, reservationdate) values (:np1, :np2, systimestamp)
然后在其他地方执行以下命令,目的是返回少于X秒前添加的行。
select * from my_table where reservationDate >= systimestamp - NUMTODSINTERVAL( :seconds, 'SECOND' )
但是,尽管预订日期晚于确定的点,但不会返回任何行。
因此,我也从同一个应用程序运行以下命令:
select col1,
col2,
reservationdate,
systimestamp as b,
systimestamp - NUMTODSINTERVAL( 5, 'SECOND' ) as c
from my_table
其中给出了以下输出:
col1: value1
col2: value2
reservationdate:2017-06-14 14:31:00.746173
b :2017-06-14 15:31:00.905617
c :2017-06-14 15:30:55.905617
请注意,b
和c
返回的值基本上比reservationdate
提前一小时。
在与Java应用程序在同一台机器上运行的SQL Developer上运行相同的请求会得到正确的值:
reservationdate:14-JUN-17 02.31.00.746173000 PM
b :14-JUN-17 02.58.32.863300000 PM +00:00
c :14-JUN-17 02.58.27.863300000 PM +00:00
Oracle正在一台虚拟机上运行,其中Unix日期的输出为:
Wed Jun 14 14:18:11 UTC 2017
在运行Java的机器上:
Wed Jun 14 15:21:24 BST 2017
显然这是一个时区问题,但我不确切地知道它来自哪里。毕竟我一直在使用systimestamp
,目的是在数据库服务器上进行所有时间戳计算。
我的请求通过Spring的NamedParameterJdbcTemplate
,我正在使用Oracle Database 11g Express Edition 11.2.0.2.0
。
答案 0 :(得分:3)
不是完全合格的答案,但这里有一些信息:
数据类型SYSTIMESTAMP
不存储任何时区信息。当您插入带时区的时间戳(例如SYSTIMESTAMP
)时,时区信息将被切断。
TIMESTAMP WITH TIME ZONE
在数据库服务器的时区中返回UTC
数据类型'操作系统时区(在您的情况下为TIMESTAMP
)
每当您将TIMESTAMP WITH TIME ZONE
(没有时区)转换/转换为SESSIONTIMEZONE
时,Oracle会考虑a,1,5
a,2,6
a,3,7
- 除非您使用FROM_TZ或{{3}}明确设置时区类似。
答案 1 :(得分:2)
尝试TIMESTAMP WITH TIMEZONE
或TIMESTAMP WITH LOCAL TIMEZONE
简单TIMEZONE
...
答案 2 :(得分:1)
我认为你看到了两件事;当您按照提取方式执行调试查询时,格式化和显示b
和c
值会导致Java转换时区。但我认为这是一个红色的鲱鱼,因为它不会直接影响你的原始查询来查找最近更改的行。它可能与您的Java语言环境有关,因此与主要查询问题有关。
如果您跟踪最初运行的查询,您应该看到它实际使用的过滤器是:
SYS_EXTRACT_UTC(INTERNAL_FUNCTION(RESERVATIONDATE))
>=SYS_EXTRACT_UTC(SYSTIMESTAMP(6)-NUMTODSINTERVAL(TO_NUMBER(:SECONDS),'SECOND'))
函数调用是因为"During SELECT FROM operations, Oracle converts the data from the column to the type of the target variable.";所以将列值转换为带有时区的时间戳,以便与systimestamp
进行比较,然后将两个时间戳与区域值进行比较,将它们转换为UTC。
然后这又回到了@WernfriedDomscheit所说的话。 sys_extract_utc()
function将datetime_with_timezone
作为参数,因此当传入普通timestamp
时,会使用SESSIONTIMEZONE
隐式转换它。您看到的查询结果不同,因为Java代码和SQL Developer之间的会话时区不同。
您可以select sessiontimezone from dual
同时查看这些内容,或查看时间戳的实际转换方式。
您可以通过将系统时间强制转换回无时区的时间戳来避免此问题:
... where reservationDate >= cast(systimestamp as timestamp) - NUMTODSINTERVAL( :seconds, 'SECOND' );
跟踪显示更简单:
RESERVATIONDATE
>=CAST(SYSTIMESTAMP(6) AS timestamp)-NUMTODSINTERVAL(TO_NUMBER(:SECONDS),'SECOND')
将列定义从timestamp
更改为timestamp with [local] time zone
也会有效;根据我的追踪,使用local
转换为UTC仍然会发生,而没有本地的转换。 This is an interesting read too
如果您查看如何使用不同的会话时区解析sys_extract_utc()
来电,您可以看到差异,使用新插入的行并省略日期元素以简化:
insert into my_table (col1, col2, reservationdate) values (:np1, :np2, systimestamp);
alter session set nls_timestamp_format = 'HH24:MI:SS.FF1';
alter session set nls_timestamp_tz_format = 'HH24:MI:SS.FF1 TZR';
alter session set time_zone = 'Europe/London';
select reservationdate a,
systimestamp b,
systimestamp(6)-numtodsinterval(to_number(:seconds),'SECOND') c,
cast(reservationdate as timestamp with time zone) d,
sys_extract_utc(reservationdate) e,
sys_extract_utc(systimestamp) f,
sys_extract_utc(systimestamp(6)-numtodsinterval(to_number(:seconds),'SECOND')) g
from my_table;
A B C D E F G
----------- ----------------- ----------------- ------------------------ ----------- ----------- -----------
17:12:13.9 17:12:15.0 +01:00 17:12:10.0 +01:00 17:12:13.9 EUROPE/LONDON 16:12:13.9 16:12:15.0 16:12:10.0
alter session set time_zone = 'UTC';
select reservationdate a,
systimestamp(6) b,
systimestamp(6)-numtodsinterval(to_number(:seconds),'SECOND') c,
cast(reservationdate as timestamp with time zone) d,
sys_extract_utc(reservationdate) e,
sys_extract_utc(systimestamp) f,
sys_extract_utc(systimestamp(6)-numtodsinterval(to_number(:seconds),'SECOND')) g
from my_table;
A B C D E F G
----------- ----------------- ----------------- -------------- ----------- ----------- -----------
17:12:13.9 17:12:15.4 +01:00 17:12:10.4 +01:00 17:12:13.9 UTC 17:12:13.9 16:12:15.4 16:12:10.4
我的设置似乎与您的设置不同 - 我的数据库服务器在英国时间,但DBTIMEZONE
是+00:00 - 但即便如此,请注意{{1}中的两个值之间的差异列。在我的情况下,它看起来会发现太多数据而不是太少 - 因为e
是真的。在您的情况下,我相信如果您从SQL Developer和Java运行那些,您将看到相反的差异,因为Java应用程序的会话时区(基于其语言环境)导致了另一种方式的转换。 / p>