通过 CMD 命令的 Oracle 假脱机文件提供比预期更多的数据

Oracle Spool file via CMD command deliver more Data than expected

我有一个 Oracle Table "Sales",其中包含 ID、Sales、TIMESTAMP 列。数据如下所示:

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 作业以在 CMD 模式下执行 SQL 假脱机文件以将查询导出到 csv。假脱机文件如下所示:

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 服务器上手动执行 SQL 查询时,它会向“2018-08-25 00:00:00”提供正确的数据

==> Talend 上的 CMD 查询给出了比预期多 1 小时的数据。

我真的不明白为什么会出现这个问题。 我的假设是查询“'2018-08-25 00:00:00.0000000'”中的问题时间戳。此时间戳没有时区。但我不确定。

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

手动查询和Talend查询好像是运行 sessions 不同时区

尽管格式模型中有 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

当您在与 table 列(带时区的时间戳)的比较中使用该纯时间戳时,会隐式转换为 session 时区。您可以通过手动设置看到效果:

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

因此,要从您的两个 session 获取不同的数据,该比较使用不同的值,因此 session 时区必须不同。

简单的解决方法是在您的固定值中明确指定时区,但您需要一个不同的函数来避免前面看到的错误;并且最好使用区域而不是偏移量以允许夏令时(假设 table 中的值也是 region-based):

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;

得到相同的值。


i haved tried to format the time zone in the Query with to_timestamp_tz(substr('2018-08-25 00:00:00.0000000'),1,25), 'YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM' at time zone 'berlin/europe') as input_timestamp but it stills gives me more data than expected.

忽略奇数 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;

你会得到(我的 session 仍在纽约时间以获得更好的效果)

TIMESTAMP_WITH_WRONG_TIME        
---------------------------------
2018-08-25 06:00:00.000000 +02:00

时区现在是您所期望的,但是时间是错误的。你有很多和以前一样的问题。您仍在将没有提供时区的固定值转换为带时区的时间戳,因此它隐含地使用 session 时区:

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 .这是同一时间点,只是从不同的 places/time 区域观看。

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