# [Oracle] Alternative zu Varchar2 - CLOB?



## forced (3. Juni 2008)

Hi

Ich habe in meiner "Oracle-Tabelle" ein Spalte vom Typ Varchar2. Dies funktionierte bisher sehr gut, doch nun muss eine Datensatz eingefügt werden, welcher mehr als 4000 Zeichen in dieser Spalte benötigt. 

Also muss ich den Spaltentyp ändern. Meine Wahl ist nun CLOB. Mit "Alter table.." erhalte ich folgende Fehlermeldung "ORA-22858: invalid alteration of datatype".

Laut Oracle soll ich die Spalte erst entfernen und anschließend neu anlegen. 
1. Gibt es da keine einfachere Vorgehensweise?
2. Gibt es eine alternativ zu meinem Vorgehen? ( BEIM Datentyp "LONG" erhalte ich irgendeine "constrain" Meldung, mit der ich nichts anfangen kann .-( )

Vielen Dank!


----------



## Albiorix (4. Juni 2008)

Hi,

Oracle kann ab 10g auch VARCHAR2-Spalten bis zu einer Länge von 32767, also einfach mal versuchen, die Spalte entsprechend zu erweitern.

Grundsätzlich gilt: Wenn man einen Spalteninhalt mit SELECT aufrufen bzw. entsprechend mit DML-Ops verändern will, ist VARCHAR2 immer noch die beste Wahl. (C)LOBs in den Oracle-DB lassen sich idR nur über DBMS_LOB modifizieren und das für jemanden, der das nicht ständig macht, etwas "unübersichtlich".

Und was LONG betrifft: Lt. Oracle soll der Datentyp irgendwann mal "obsolet" sein. Allerdings höre ich die Aussage schon seit ca. 2000 immer mal wieder und in der 11g ist der Spalten-Typ immer noch drin 

Ich hoffe geholfen zu haben

Sascha


----------



## dbwizard (4. Juni 2008)

Albiorix hat gesagt.:


> Hi,
> 
> Oracle kann ab 10g auch VARCHAR2-Spalten bis zu einer Länge von 32767, also einfach mal versuchen, die Spalte entsprechend zu erweitern.
> 
> Sascha



- ? Wie kommst du darauf ?

SQL*Plus: Release 10.2.0.1.0 - Production on Wed Jun 4 15:26:36 2008

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
With the Partitioning, OLAP and Data Mining options

m_hoko@THLTST12> 
m_hoko@THLTST12> CREATE TABLE TESTVARCHAR  (string VARCHAR2 (100))
/  

Table created.

m_hoko@THLTST12> ALTER TABLE TESTVARCHAR  MODIFY (STRING VARCHAR2 (8000))
/
ALTER TABLE TESTVARCHAR  MODIFY (STRING VARCHAR2 (8000))
                                                  *
ERROR at line 1:
ORA-00910: specified length too long for its datatype


m_hoko@THLTST12>


- Du verwechselst das mit den PL/SQL Variablen

Gruss


----------



## Albiorix (4. Juni 2008)

dbwizard hat gesagt.:


> - Du verwechselst das mit den PL/SQL Variablen
> 
> Gruss



ok, Asche auf mein Haupt.

Zum Thema LONG (Auszug aus der Oracle-Doku):


> Do not create tables with LONG columns. Use LOB columns (CLOB, NCLOB, BLOB) instead. LONG columns are supported only for backward compatibility.



Dann bleibt eigentlich nur noch CLOB.


----------



## forced (6. Juni 2008)

ok vielen Dank für die Hilfe.. Habs nun auf CLOB umgestellt (Tabelle gelöscht, neue erstellt, mit Daten befüllt) und nun protokolliert der Trigger keine Änderungen der CLOB-Spalte mehr. Das ist echt zum verzweifeln 

Nach einer Änderung in einer der CLOB Spalten ist in OLD:Spaltenname noch der alte Inhalt zu finden, in der New:spaltenname steht nichts drin.. Mit Varchar2 funktionierte es problemlos.. 

Vielen Dank für die Hilfe!


----------



## Nico Graichen (6. Juni 2008)

Das hättest du auch einfacher haben können, vorallem wenn schon Daten vorhanden waren.

Neue Spalte in der Tabelle vom Typ CLOB anlegen --> Daten von der VARCHAR2-Spalte in die CLOB-Spalte kopieren --> VARCHAR2-Spalte löschen --> CLOB-Spalte umbenennen

Bzgl des Trigger-Problems:
http://www.stanford.edu/dept/itss/docs/oracle/10g/appdev.101/b10795/adfns_tr.htm
vielleicht hilft das


----------



## Albiorix (6. Juni 2008)

hm, komisch, ich hab gerade versucht, das Problem nachzustellen, und bei mir Loggt der Trigger erfolgreich die Transaktionen mit.

Zum Nachtesten:

```
CREATE TABLE test_tabelle
   (id NUMBER NOT NULL, 
    clob_marker CLOB, 
         CONSTRAINT test_tabelle_pk PRIMARY KEY (id)) ;
 
CREATE OR REPLACE TRIGGER test_tabelle_br_iud BEFORE
   INSERT OR
   UPDATE OR
   DELETE ON 
 test_tabelle FOR EACH ROW
DECLARE
  dml_ops VARCHAR2(3);
  
BEGIN 
IF inserting THEN
  IF :NEW.ID IS NULL THEN
    SELECT TEST_SEQ.nextval
      INTO :NEW.ID
      FROM dual;
  END IF;
END IF;

CASE
  WHEN inserting THEN
    dml_ops := 'INS';
  WHEN updating THEN
    dml_ops := 'UPD';
  WHEN deleting THEN
    dml_ops := 'DEL';
  ELSE
    dml_ops := 'UNK';
END CASE;

-- Protokoll Transaktion
INSERT INTO test_tabelle_jn
  (jn_operation
  ,jn_datetime
  ,id
  ,clob_marker) 
VALUES
  (dml_ops
  ,sysdate
  ,DECODE(dml_ops, 'DEL', :OLD.id, :NEW.id)
  ,DECODE(dml_ops, 'DEL', :OLD.clob_marker, :NEW.clob_marker));
END;
/
ALTER TRIGGER test_tabelle_br_iud ENABLE;
 
-- Logging-Tabelle (Journalisierung der aktuellen Transaktion
CREATE TABLE test_tabelle_jn
   (jn_operation VARCHAR2(3 BYTE) NOT NULL, 
    jn_datetime DATE NOT NULL, 
    id NUMBER NOT NULL, 
    clob_marker CLOB
   );
```

Ansonsten, man kann ein Lob nicht direkt in den PL/SQL-Funkionen anfassen. Dafür gibt es das Paket DBMS_LOB.


----------



## forced (10. Juni 2008)

ok vielen Dank für die Hilfe.. der Trigger loggt nun meistens alles mit, ab und zu scheint der Zugriff auf die CLOB Spalte gestört zu sein..

Dennoch ist das Trigger-Problem nicht beseitigt.

Der Trigger soll änderungen an einer Tabelle protokolieren, aber nur die Änderungen, welche manuell durch Personen durchgeführt werden. Auf diese Tabelle greift aber auch eine Java Software zu, welche ebenfalls Änderungen durchführt, was aber nicht protokolliert werden soll..

Nun habe ich mir überlegt, das die Software praktisch eine Variable setzt und diese später von dem Trigger abgefragt wird. Ist die Variable gleich der gesetzten der Javasoftware, so soll nicht mitgeloggt werden, andernfalls loggen.


```
java.sql.CallableStatement cStmt = db.Oracle.conn.prepareCall("{ call dbms_application_info.set_client_info('JAVASOFWARE')}");
```

im Trigger steht in dieser Variable aber nichts drin- Ich frage den Wert wie folgt ab:


```
SELECT USERENV('CLIENT_INFO') INTO clientinfo FROM dual;
```

Jemand eine Idee? 
Evtl. sollte ich mich hiermit auch ans Java-Forum wenden, aber ich weiß nicht auf welcher Seite das Problem vorliegt. 

Danke


----------



## Albiorix (10. Juni 2008)

Das Problem mit den CLOB ist folgender:

Wenn das CLOB (idR) größer als 4k ist, feuert der Trigger, obwohl noch nciht alle Daten in der DB stehen. Das hat folgenden Hintergrund: ab 4K Größe werden die Date nicht mehr in der Zeile gespeichert. Oracle streamt die Daten im Hintergrund weiter, damit das Statement bei größeren Datenmengen nicht "hängen" bleibt. Das bedeutet aber auch, das das CLOB-Feld beim ersten Durchlauf auch erstmal nicht "voll" ist.
Der Workaround wäre an der Stelle, die Daten erstmal in einem Temporären CLOB zwischenzupuffern und erst nach dem Stream zu speichern.

Zu deiner 2. Frage:
Innerhalb von PL/SQL-Blöcken, dazu zählen auch Trigger, brauchst du die dämliche "SELECT xxx FROM DUAL-Notation nicht, jedenfalls bei den meisten Zuweisungen nicht. Der dbms_application_info.set_client_info-Call ist nur für die Laufzeit einer Session gültig, d.h. Zwischen dem connect und dem disconnect. Wenn die Session beendet ist, also in der Anwendung mittendrin, ist der Wert natürlich auch weg.
Du kannst das auch gerne prüfen, ob der Wert angekommen ist, er sollte in der Spalte CLIENT_INFO in dem Systemview V$SESSION stehen (wenn du die Berechtigung für den Zugriff auf die Ansicht hast).

Ich würde dir empfehlen, wenn du keine dauerhafte Session in deiner Applikation abgebildet hast, folgendes zu tun


```
connect zur DB
dbms_application_info.set_client_info ...
==auszuführende Aktion==
  (hier zieht im Normalfall der Trigger)
disconnect
```

und wenn der Trigger zieht (d.h. zwischen connect und disconnect) im Trigger

```
clientinfo := userenv('CLIENTINFO') -- kann auch in einer IF-Bedingung benutzt werden

bzw. alternativ

dbms_application_info.read_client_info(clientinfo);
```

Ich hoffe das wars

Sascha


----------



## forced (13. Juni 2008)

Ich danke


----------

