2017-06-14 13 views
2

不同我有包含以下列定義Oracle表:時區結果與SYSTIMESTAMP

COLUMN_NAME : RESERVATIONDATE 
    DATA_TYPE : TIMESTAMP(6) 
    NULLABLE : Yes 
    DATA_DEFAULT: null 

然後,從內的Java I執行以下命令:

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 

注意,返回的值bc基本上是一個

因此,我也從同一個應用程序運行以下命令小時提前reservationdate

上運行SQL開發人員在同一臺機器上運行的Java應用程序提供了正確的價值觀相同的請求:

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 

甲骨文是一個虛擬機,其中的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

當你運行ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT ='YYYY-MM-DD HH24:MI:SS TZH:TZM'時,你會得到什麼? –

+0

對不起,之前呢?在Java應用程序的命令中,還是在SQL Developer中測試之前? – Rich

+0

在兩種情況下都可以使用 –

回答

2

嘗試TIMESTAMP WITH TIMEZONETIMESTAMP WITH LOCAL TIMEZONE insted的簡單TIMEZONE ...

2

不是一個完全合格的答卷,但這裏有一些信息:

數據類型TIMESTAMP任何時區信息不保存。當您在時區中插入時間戳(例如SYSTIMESTAMP)時,時區信息將被切斷。 (在你的情況UTC)在數據庫服務器的操作系統時區的時區

SYSTIMESTAMP回報TIMESTAMP WITH TIME ZONE數據類型

每當你轉換/蒙上了TIMESTAMP(無時區),以TIMESTAMP WITH TIME ZONE那麼Oracle需要SESSIONTIMEZONE除非你明確地設置時區爲FROM_TZ或類似的。

1

我認爲你看到了兩件事情;當您按照自己的方式進行調試時,格式化並顯示bc值會導致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 - 但即便如此,請注意e列中兩個值之間的差異。在我看來,它會發現太多的數據,而不是太少 - 因爲e >= g是真的。就你而言,我相信如果你從SQL Developer和Java中運行它們,你會看到另一種方式的差異,因爲來自Java應用程序的會話時區(基於它的語言環境)正在引起另一種方式的轉變。