Heute habe ich ein besonderes Leckerli für die Oracle Freunde:
create table tmp_number
(
val_n number,
val_n8 number(8),
val_int integer,
val_f float
);
Insert into TMP_NUMBER
(VAL_N, VAL_N8, VAL_INT, VAL_F)
Values
(0, 0, 0, 0);
Insert into TMP_NUMBER
(VAL_N, VAL_N8, VAL_INT, VAL_F)
Values
(-1, -1, -1, -1);
Insert into TMP_NUMBER
(VAL_N, VAL_N8, VAL_INT, VAL_F)
Values
(1, 1, 1, 1);
Commit;
(Beispieltabelle, weil ich zunächst vermutete, es hängt davon ab, in welchem Datenformat die Werte gespeichert werden. Das scheint aber egal zu sein.)
Nun haben wir eine Abfrage, die alle Werte selektiert, die größer sind als das aktuelle Datum.
SQL*Plus: Release 10.2.0.3.0 - Production on Thu Mar 22 15:20:58 2012
Copyright (c) 1982, 2006, Oracle. All Rights Reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit
Production
With the Partitioning, OLAP, Data Mining and Real Application Testing
options
SQL> select *
from tmp_number
where val_n >= to_number(to_char(sysdate, 'YYYYMMDD'));
no rows selected
In diesem Fall gibt es keinen Output, das spielt für den aktuellen Fall aber keine Rolle. Also ergänzen wir die Query um die Bedinung or val_n = 0:
SQL> select *
from tmp_number
where val_n >= to_number(to_char(sysdate, 'YYYYMMDD'))
or val_n = 0;
no rows selected
WTF?
Vereinfachen wir die Query mal kurz etwas:
SQL> select *
from tmp_number
where val_n >= 20120322
or val_n = 0;
VAL_N VAL_N8 VAL_INT VAL_F
---------- ---------- ---------- ----------
0 0 0 0
Irgendwas läuft hier also mit dem Test auf 0 falsch. Probieren wir es
anders:
SQL> select *
from tmp_number
where val_n >= to_number(to_char(sysdate, 'YYYYMMDD'))
or val_n <= 0;
VAL_N VAL_N8 VAL_INT VAL_F
---------- ---------- ---------- ----------
0 0 0 0
-1 -1 -1 -1
1 1 1 1
Das wird ja immer verrückter! Jetzt selektiert Oracle schon Werte, für die die Bedingung gar nicht wahr ist.
Ich vermute, hier liegt irgendein seltsamer Bug in Verbindung mit dem Konvertieren des Datums zu einer Zahl zugrunde, die dafür sorgt, dass Oracle die Werte in val_n und 0 intern in andere Werte konvertiert, die wiederum nicht gleich sind. Die Lösung zu dem Problem besteht übrigens in einem expliziten Konvertieren der Spalte val_n zu Number:
SQL> select *
from tmp_number
where val_n >= to_number(to_char(sysdate, 'YYYYMMDD'))
or to_number(val_n) <= 0;
VAL_N VAL_N8 VAL_INT VAL_F
----- ---------- ---------- ----------
0 0 0 0
-1 -1 -1 -1
Also es kann sein, das ein explizites Konvertieren der Werte notwendig ist, auch wenn das auf den ersten Blick nicht so aussieht. Wie bereits erwähnt, spielt es in diesem Fall keine Rolle, ob die Spalte als Number, Number(8), Float oder Integer definiert wird. Das Problem tritt bei allen Datentypen auf. Ich habe bisher keinen Bug dazu gefunden.
Hier ist noch der Dump der Daten:
select
dump(val_n), dump(val_f), val_n
from tmp_number;
VAL_N VAL_F VAL_N
---------------------------------------------------------
Typ=2 Len=1: 128 Typ=2 Len=1: 128 0
Typ=2 Len=2: 193,2 Typ=2 Len=2: 193,2 1
Typ=2 Len=3: 62,100,102 Typ=2 Len=3: 62,100,102 -1
Die Daten werden also intern alle im gleichen Format abgelegt (auch für die anderen Spalten).
Update:Anpassung der Oracle Ausgabe.