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

问题描述 投票:0回答:1

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

你可以帮我解决这个问题吗?谢谢。

sql oracle cmd talend
1个回答
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'在时区'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'

© www.soinside.com 2019 - 2024. All rights reserved.