我有一个带有列ID,Sales,TIMESTAMP的Oracle表“Sales”。数据如下所示:
ID Sales TimeStamp
1 30 2018-08-20 00:00:00.989900 +02:00
1 35 2018-08-21 05:00:00.989900 +02:00
...
1 35 2018-08-27 05:00:00.989900 +02:00
我创建了一个Talend Job来在CMD模式下执行SQL Spool文件,将Query导出到csv中。 Spoolfile看起来像这样:
alter session set NLS_TIMESTAMP_TZ_FORMAT ='YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM';
alter session set NLS_TIMESTAMP_FORMAT ='YYYY-MM-DD HH24:mi:ss.ff6';
alter session set NLS_DATE_FORMAT ='YYYY-MM-DD';
alter session set NLS_NUMERIC_CHARACTERS ='.,';
spool C:/test.csv
SET ECHO OFF
SET ...
SELECT * FROM Sales where timestamp< to_timestamp('2018-08-25 00:00:00.0000000','YYYY-MM-DD HH24:mi:ss:ff66 TZH:TZM')
当TalendJob在CMD模式下运行查询时,它为数据提供了比预期更多的数据到'2018-08-25 01:00:00'。
当我手动在Oracle Server上执行SQL查询时,它会将正确的数据提供给'2018-08-25 00:00:00'
==>在Talend上查询CMD会比预期提供1小时的数据。
我真的不明白为什么会出现这个问题。我的假设是查询“'2018-08-25 00:00:00.0000000'”中的问题时间戳。此时间戳没有时区。但我不确定。
你可以帮我解决这个问题吗?谢谢。
手动查询和Talend查询似乎在具有不同时区的会话中运行。
尽管格式模型中有TZH:TZM
,但您没有在固定值中指定时区;事实上,你不能与to_timestamp()
:
select to_timestamp('2018-08-25 00:00:00.0000000 +02:00','YYYY-MM-DD HH24:mi:ss:ff6 TZH:TZM')
from dual;
ORA-01821: date format not recognized
因为该函数为您提供了一个简单的时间戳:
alter session set NLS_TIMESTAMP_FORMAT ='YYYY-MM-DD HH24:mi:ss.ff6';
alter session set NLS_TIMESTAMP_TZ_FORMAT ='YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM';
select to_timestamp('2018-08-25 00:00:00.0000000','YYYY-MM-DD HH24:mi:ss:ff6 TZH:TZM')
as plain_timestamp
from dual;
PLAIN_TIMESTAMP
--------------------------
2018-08-25 00:00:00.000000
在与表列(与时区的时间戳)进行比较时使用该普通时间戳时,会隐式转换为会话时区。您可以通过手动设置它来查看效果:
alter session set time_zone = 'Europe/London';
select cast(
to_timestamp('2018-08-25 00:00:00.0000000','YYYY-MM-DD HH24:mi:ss:ff6 TZH:TZM')
as timestamp with time zone
) as timestamp_with_session_zone
from dual;
TIMESTAMP_WITH_SESSION_ZONE
---------------------------------
2018-08-25 00:00:00.000000 +01:00
alter session set time_zone = 'America/New_York';
select cast(
to_timestamp('2018-08-25 00:00:00.0000000','YYYY-MM-DD HH24:mi:ss:ff6 TZH:TZM')
as timestamp with time zone
) as timestamp_with_session_zone
from dual;
TIMESTAMP_WITH_SESSION_ZONE
---------------------------------
2018-08-25 00:00:00.000000 -04:00
因此,要从两个会话中获取不同的数据,该比较使用不同的值,因此会话时区必须不同。
简单的解决方法是在固定值中明确指定时区,但是您需要一个不同的函数来避免前面看到的错误;并且最好使用区域而不是偏移以允许夏令时(假设表中的值也是基于区域的):
select to_timestamp_tz('2018-08-25 00:00:00.0000000 Europe/Berlin','YYYY-MM-DD HH24:mi:ss:ff6 TZR')
as timestamp_with_berlin_zone
from dual;
TIMESTAMP_WITH_BERLIN_ZONE
---------------------------------
2018-08-25 00:00:00.000000 +02:00
或者你可以使用时间戳文字:
select timestamp '2018-08-25 00:00:00.0 Europe/Berlin' as timestamp_with_berlin_zone
from dual;
它获得相同的价值。
我试图用to_timestamp_tz格式化查询中的时区(substr('2018-08-25 00:00:00.0000000'),1,25),'YYYY-MM-DD HH24:mi:ss.ff6 TZH: TZM'在时区'berlin / europe')作为input_timestamp,但它仍然给我提供了比预期更多的数据。
如果你这样做,忽略奇怪的substr()
,它只是从已经固定的字符串中删除最后两个零,
select to_timestamp_tz('2018-08-25 00:00:00.0000000', 'YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM')
at time zone 'Europe/Berlin' as timestamp_with_wrong_time
from dual;
你得到了(我的会议仍在纽约时间取得更大的效果)
TIMESTAMP_WITH_WRONG_TIME
---------------------------------
2018-08-25 06:00:00.000000 +02:00
时区现在是你所期望的,但时间是错误的。你和以前有很多相同的问题。您仍然将没有提供时区的固定值转换为带时区的时间戳,因此它隐式使用会话时区:
select to_timestamp_tz('2018-08-25 00:00:00.0000000', 'YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM')
as timestamp_with_wrong_time
from dual;
TIMESTAMP_WITH_WRONG_TIME
---------------------------------
2018-08-25 00:00:00.000000 -04:00
然后at timezone 'Europe/Berlin'
在世界时间中给出了完全相同的点 - 在纽约的午夜,即04:00 UTC - 但在柏林当地时间,即06:00。这是相同的时间点,只是从不同的地方/时区观看。
同样,您只需要指定用于比较的固定时间的时区 - 如timestamp '2018-08-25 00:00:00.0 Europe/Berlin'
。