Oracle Spool文件通过CMD命令提供的数据量超出预期

时间:2019-02-26 17:24:03

标签: sql oracle cmd talend

我有一个带有列ID,Sales,TIMESTAMP的Oracle表“ Sales”。数据如下:

serverImpl
我创建了一个Talend Job,以CMD模式执行SQL Spool文件,以将查询导出到csv。 Spoolfile看起来像这样:

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

当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'”中的问题时间戳。此时间戳没有时区。但我不确定。

您能帮我解决这个问题吗? 谢谢。

1 个答案:

答案 0 :(得分:1)

手动查询和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”(位于时区“柏林/欧洲”)作为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'给出了与世界标准时间完全相同的点-纽约的午夜(UTC时间04:00),而柏林的当地时间为06:00。是同一时间点,只是从不同的位置/时区查看。

同样,您只需指定用于比较的固定时间的时区-timestamp '2018-08-25 00:00:00.0 Europe/Berlin'