Untuk menghindari kesalahan ini, pertimbangkan untuk menggunakan ekspresi eksplisit dalam klausa where ke jenis stempel waktu (stempel waktu tanpa zona waktu), dengan cara ini:
select *
from MY_TABLE T
where T.MY_TIMESTAMP >= cast(CURRENT_TIMESTAMP - interval '1' hour As timestamp );
Atau Anda dapat secara eksplisit mengatur zona waktu sesi, misalnya '-05:00' - untuk waktu standar New York (musim dingin),
menggunakan ALTER SESSION time_zone = '-05:00'
, atau dengan menyetel variabel lingkungan ORA_SDTZ di semua lingkungan klien,
lihat tautan ini untuk detailnya:http://docs.Oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263
Tapi itu juga tergantung pada apa yang sebenarnya disimpan di kolom stempel waktu di tabel, misalnya stempel waktu apa 2014-07-01 15:00:00
sebenarnya mewakili, apakah ini "waktu musim dingin" atau "waktu musim panas"?
CURRENT_TIMESTAMP
fungsi mengembalikan nilai tipe data TIMESTAMP WITH TIME ZONE
lihat tautan ini:http://docs.Oracle.com/cd/B19306_01/server.102/b14200/functions037.htm
Sementara membandingkan cap waktu dan tanggal, Oracle secara implisit mengonversi data ke tipe data yang lebih tepat menggunakan zona waktu sesi !
Lihat tautan ini --> http://docs.Oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG251
Dalam kasus khusus kami, Oracle mengeluarkan timestamp
kolom ke timestamp with time zone
type.
Oracle menentukan zona waktu sesi dari lingkungan klien.
Anda dapat menentukan zona waktu sesi saat ini menggunakan kueri ini:
select sessiontimezone from dual;
Misalnya di PC saya (Menang 7), ketika opsi ""Sesuaikan jam secara otomatis untuk Waktu Musim Panas" dicentang, kueri ini kembali (di bawah SQLDeveloper):
SESSIONTIMEZONE
---------------
Europe/Belgrade
Ketika saya menghapus centang opsi ini di Windows dan kemudian restart SQLDeveloper, itu memberikan:
SESSIONTIMEZONE
---------------
+01:00
Zona waktu sesi sebelumnya adalah zona waktu dengan nama wilayah, di mana Oracle menggunakan aturan Waktu Musim Panas untuk wilayah ini dalam penghitungan tanggal:
alter session set time_zone = 'Europe/Belgrade';
select cast( timestamp '2014-01-29 01:30:00' as timestamp with time zone ) As x,
cast( timestamp '2014-05-29 01:30:00' as timestamp with time zone ) As y
from dual;
session SET altered.
X Y
---------------------------- ----------------------------
2014-01-29 01:30:00 EUROPE/B 2014-05-29 01:30:00 EUROPE/B
ELGRADE ELGRADE
Zona waktu terakhir menggunakan offset tetap "+01:00" (selalu "Winter time"), dan Oracle tidak menerapkan aturan DST apa pun untuk itu, ia hanya menambahkan offset tetap.
alter session set time_zone = '+01:00';
select cast( timestamp '2014-01-29 01:30:00' as timestamp with time zone ) As x,
cast( timestamp '2014-05-29 01:30:00' as timestamp with time zone ) As y
from dual;
session SET altered.
X Y
---------------------------- ----------------------------
2014-01-29 01:30:00 +01:00 2014-05-29 01:30:00 +01:00
Harap dicatat, demi rasa ingin tahu, bahwa Y
hasil di atas mewakili dua waktu yang berbeda !!!014-05-29 01:30:00 EUROPE/BELGRADE
tidak sama dengan:2014-05-29 01:30:00 +01:00
tetapi sebenarnya ini:014-05-29 01:30:00 EUROPE/BELGRADE
sama dengan:2014-05-29 01:30:00 +02:00
Hal di atas hanya untuk membuat Anda menyadari betapa sederhananya "penghapusan centang kotak" dapat memengaruhi kueri Anda, dan di mana harus menggali alasan ketika pengguna mengeluh "kueri ini berfungsi dengan baik di bulan Januari, tetapi memberi hasil yang salah pada bulan Juli".
Dan masih pada topik ORA-01878 - katakanlah sesi saya adalah EUROPE/Warsaw
dan tabel saya berisi stempel waktu ini (tanpa zona waktu)
'TIMESTAMP'2014-03-30 2:30:00'
Perhatikan bahwa di wilayah saya perubahan DST, pada tahun 2014, terjadi pada tanggal 30 Maret pukul 2:00 pagi
Ini berarti bahwa pada tanggal 30 Maret, pukul 2:00 malam, saya harus bangun dan mengalihkan jam tangan saya. maju dari 2:00 ke 3:00;)
alter session set time_zone = 'Europe/Warsaw';
select cast( TIMESTAMP'2014-03-30 2:30:00' as timestamp with time zone ) As x
from dual;
SQL Error: ORA-01878: podane pole nie zostało znalezione w dacie-godzinie ani w interwale
01878. 00000 - "specified field not found in datetime or interval"
*Cause: The specified field was not found in the datetime or interval.
*Action: Make sure that the specified field is in the datetime or interval.
Oracle tahu, bahwa stempel waktu ini tidak valid di wilayah saya menurut aturan DST, karena tidak ada waktu 2:30 pada 30 Maret - jam 2:00 jam dipindahkan ke 3:00, dan tidak ada waktu 2:30. Oleh karena itu Oracle melempar kesalahan ORA-01878.
Namun kueri ini berfungsi dengan baik:
alter session set time_zone = '+01:00';
select cast( TIMESTAMP'2014-03-30 2:30:00' as timestamp with time zone ) As x
from dual;
session SET altered.
X
----------------------------
2014-03-30 02:30:00 +01:00
Dan inilah alasan kesalahan ini - tabel Anda berisi stempel waktu seperti 2014-03-09 2:30
atau lebih (untuk New York, di mana pergeseran DST terjadi pada 9 Maret dan 2 November), dan Oracle tidak tahu cara mengonversinya dari stempel waktu (tanpa TZ) ke stempel waktu dengan TZ.
Pertanyaan terakhir - mengapa kueri dengan >=
tidak berfungsi, tetapi kueri dengan <=
berfungsi dengan baik ?
Mereka berfungsi/tidak berfungsi, karena SQLDeveloper hanya mengembalikan 50 baris pertama (mungkin 100 ? Tergantung pada pengaturan). Kueri tidak membaca seluruh tabel, kueri berhenti saat 50(100) baris pertama diambil.
Ubah kueri "berfungsi", misalnya:
select sum( EXTRACT(HOUR from MY_TIMESTAMP) ) from MY_TABLE
where MY_TIMESTAMP <= (CURRENT_TIMESTAMP - interval '1' hour );
Ini memaksa kueri untuk membaca semua baris dalam tabel, dan kesalahan akan muncul, saya yakin 100%.