Oracle kennt das Konzept der externen Tabellen schon seit vielen Jahren. Seit Oracle 18c kann man dies auch ohne explizites anlegen einer externen Tabelle über die Funktion EXTERNAL()
nutzen.
Als Test soll der Inhalt von t1.dat geladen werden:
"SYS","OBJ$","2024-10-11:15:19:56","VALID"
"SYS","I_OBJ3","2024-10-11:15:19:56","VALID"
In dem Beispiel wird die Funktion EXTERNAL()
genutzt um Testdaten in eine Tabelle zu laden.
SELECT * FROM EXTERNAL (
(
OWNER VARCHAR2(22),
OBJECT_NAME VARCHAR2(121),
TIMESTAMP VARCHAR2(19),
STATUS VARCHAR2(5)
)
TYPE ORACLE_LOADER
DEFAULT DIRECTORY ARCH$_TMP_LS1M4DG
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
BADFILE 't1.bad'
LOGFILE 't1.log'
fields terminated by "," optionally enclosed by '"'
missing field values are null
(
owner CHAR(22),
object_name CHAR(121),
timestamp CHAR(19),
status CHAR(5)
)
)
LOCATION ('t1.dat') REJECT LIMIT UNLIMITED)
result
WHERE rownum <= 1
/
Dieser Versuch wird mit folgendem Fehler quittiert:
ERROR at line 94:
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-00554: error encountered while parsing access parameters
KUP-01005: syntax error: found "timestamp": expecting one of: "double-quoted-string, identifier, single-quoted-string"
KUP-01007: at line 16 column 4
Oracle beschwert sich, dass „timestamp“ einen Syntax error hervorgerufen hat. Wäre es ein echtes reserviertes Wort, müsste man dieses mittels doppelten Anführungszeichen quoten. Prüfen wir das inmal in V$RESERVED_WORDS
.
select keyword, reserved
from v$reserved_words
where keyword like 'TIMESTAMP%'
KEYWORD RESERVED
------------------------------ ----------
TIMESTAMP N
TIMESTAMP_TO_NUMBER N
TIMESTAMP
wird zwar tatsächlich aufgeführt, aber mit RESERVED = ‚N‘ als kein echtes reserviertes Keyword behandelt.
Schauen wir doch mal im Data Dictionary nach, wie oft es eine Spalte mit dem Namen TIMESTAMP
gibt:
select owner, count(*)
from dba_tab_columns
where column_name='TIMESTAMP'
group by owner;
OWNER COUNT(*)
---------- ----------
SYS 166
SYSTEM 5
APPQOSSYS 4
OJVMSYS 1
DVSYS 6
OUTLN 1
Anscheinend ist der Name TIMESTAMP
als Spalte recht beliebt. Das war schon vor vielen Jahren, als es den Datentype TIMESTAMP
bei Oracle noch nicht gegeben hat, so. Mit der Einführung vom Datentyp TIMESTAMP
– in Wirklichkeit kennt Oracle intern viele verschiedene Versionen des Datentyps! Es gibt auch noch TIMESTAMP(0), TIMESTAMP(1), … Jeder der 10 möglichen Skalierungen ist ein eigener Datentyp (mal 3, weil es die Kombinationen mit WITH TIME ZONE und mit WITH LOCAL TIME ZONE auch noch gibt). Die Vermutung liegt nahe, dass dies nur deswegen so kompliziert (gereicht hätten ja 3 Datentypen: ohne TIME ZONE, WITH TIME ZONE und WITH LOCAL TIME ZONE), damit es eben keine Probleme mit den schon vorhandenen Spaltennamen Timestamp im Data Dictionary gibt. Änderungen am Data Dictionary – mit Ausnahme von zusätzlichen Spalten – mache Oracle extrem selten, damit bestehende Abfragen mit den neuen Oracle Datenbank Versionen auch weiterhin funktionieren.
Die Lösung / der Workaround
Einfach das Keyword TIMESTAMP
großschreiben und mit Quotes umschließen.
SELECT * FROM EXTERNAL (
(
OWNER VARCHAR2(22),
OBJECT_NAME VARCHAR2(121),
TIMESTAMP VARCHAR2(19),
STATUS VARCHAR2(5)
)
TYPE ORACLE_LOADER
DEFAULT DIRECTORY ARCH$_TMP_LS1M4DG
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
BADFILE 't1.bad'
LOGFILE 't1.log'
fields terminated by "," optionally enclosed by '"'
missing field values are null
(
owner CHAR(22),
object_name CHAR(121),
"TIMESTAMP" CHAR(19),
status CHAR(5)
)
)
LOCATION ('t1.dat') REJECT LIMIT UNLIMITED)
result
WHERE rownum <= 1
/
Schöner wäre es natürlich, wenn Oracle diese Ungereimtheit in der Data Cartridge beheben würde.