Hm, yep, it seems it works only in insert select command (I tried an update with subquery) but didn't succeed).
In this case try following:
 
1) if you got partitioning option and have enough space to store a temporary copy of your table in database, then:
 
SQL> create table t (id number, long_col long);
 
Table created.
 
SQL> insert into t select rownum, name from sys.obj$ where rownum <=5;
 
5 rows created.
 
SQL> commit;
 
Commit complete.
 
SQL> desc t;
 Name                          Null?    Type
 ----------------------------- -------- --------------------
 ID                                     NUMBER
 LONG_COL                               LONG
 
SQL> select * from t;
 
        ID LONG_COL
---------- --------------------
         1 I_COBJ#
         2 C_FILE#_BLOCK#
         3 SEG$
         4 I_UNDO1
         5 I_COL1
 
SQL> create table t_tmp (id, clob_col)
  2  partition by range (id)
  3    (partition t_tmp_p1 values less than (maxvalue))
  4  nologging
  5  as select id, to_lob(long_col) from t;
 
This command creates partitioned t_tmp table with only one partition which holds all rows
Nologging is useful here, you can add PARALLEL as well.
 
Table created.
 
SQL> truncate table t drop storage;
 
We truncate t because we have a copy of the data in t_tmp and that way drop column will be faster
 
Table truncated.
 
SQL> alter table t drop column long_col;
 
Table altered.
 
SQL> alter table t add (lob_col clob);
 
We can only exchange partitions between tables with same number of columns, their datatypes and order (note that column names don't matter, at least in 9.2 they don't).
 
Table altered.
 
SQL>
SQL> alter table t_tmp exchange partition t_tmp_p1 with table t without validation;
 
Using without validation reduces CPU usage during exchange operation, because no check is done whether all rows in segment really belong to specified partition (in this example we have only one partition for all rows anyway).
If we had any indexes on T table, these will be marked unusable (with exception of the LOB index we have now) and have to be rebuilt. If we would have built a matching LOCAL index on t_tmp for all indexes on T for some reason, we could do the exchange using "including indexes" clause, that way index segments get exchanged as well and no rebuild is needed.
 
Table altered.
 
SQL>
SQL> drop table t_tmp;
 
Table dropped.
 
SQL>
SQL> desc t;
 Name                          Null?    Type
 ----------------------------- -------- --------------------
 ID                                     NUMBER
 LOB_COL                                CLOB
 
SQL> select * from t;
 
        ID LOB_COL
---------- --------------------
         1 I_COBJ#
         2 C_FILE#_BLOCK#
         3 SEG$
         4 I_UNDO1
         5 I_COL1
 
SQL> :)
 
So in this example we only use double amount of disk space temporarily. We do only one cycle of IO -> inserting into t_tmp from t. The exhange partition doesn't copy any data, it just updates data dictionary and segment header blocks to reflect new exchanged extent locations.
 
Comments:
* You might want to specify different storage options (tablespaces) for LOB and table segments.
* It's reasonable to create the partitioned table to tablespace where you want your data to remain, that way you avoid additiona IO which would be needed for "alter table move tablespace".
 
2) If you don't have partitioning option or no extra disk space, then we have to do the job in step-by-step in batches. Unfortunately exchange partition doesn't work for exchanging only parts of non-partitioned table, thus we have to use regular direct load insert commands for the job:
 
SQL> drop table t;
 
Table dropped.
 
SQL> create table t (id number, long_col long);
 
Table created.
 
SQL> alter table t nologging;
 
We can't use nologging in hint in 8i (in 9i it works), thus have to alter table in order to save IO.
 
Table altered.
 
SQL> insert /*+ APPEND */ into t select rownum, name from sys.obj$ where rownum <=5;
 
5 rows created.
 
SQL> alter table t logging;
 
Table altered.
 
SQL> desc t;
 Name                          Null?    Type
 ----------------------------- -------- --------------------
 ID                                     NUMBER
 LONG_COL                               LONG
 
SQL> select * from t;
 
        ID LONG_COL
---------- --------------------
         1 FILE$
         2 BOOTSTRAP$
         3 I_COBJ#
         4 I_CON2
         5 SEG$
 
SQL> alter table t add clob_col clob;
 
Table altered.
 
SQL> insert into t (id, clob_col) select id, to_lob(long_col) from t;
 
We insert into clob_col but select from long_col.
 
5 rows created.
 
SQL> select * from t;
 
        ID LONG_COL             CLOB_COL
---------- -------------------- --------------------
         1 FILE$
         2 BOOTSTRAP$
         3 I_COBJ#
         4 I_CON2
         5 SEG$
         1                      FILE$
         2                      BOOTSTRAP$
         3                      I_COBJ#
         4                      I_CON2
         5                      SEG$
 
10 rows selected.
 
Here we have two copies of each row
And we should ger rid of them:
 
SQL> delete from t where long_col is not null;
 
5 rows deleted.
 
SQL> select * from t;
 
        ID LONG_COL             CLOB_COL
---------- -------------------- --------------------
         1                      FILE$
         2                      BOOTSTRAP$
         3                      I_COBJ#
         4                      I_CON2
         5                      SEG$
 
SQL> alter table t drop column long_col;
 
Table altered.
 
SQL> select * from t;
 
        ID CLOB_COL
---------- --------------------
         1 FILE$
         2 BOOTSTRAP$
         3 I_COBJ#
         4 I_CON2
         5 SEG$
SQL>  :)
 
Comments:
* You have to create a foolproof criteria for deleting old copies of records (just checking for not null might erase some records which are null in source as well)
* You can use parallel inserts if your hardware permits
* Insert +append to long column didn't work anymore in 9.2.0.4, in 8.1.7 it works ok (second test is done on 32bit 8.1.7.1 Solaris)
* I personally think, that if you're dealing with one or few tables, it's easier to recreate tables, that way you'll save yourself from significant IO occuring during delete.
* Do the job in relatively small batches, that way you'll save disk space (if you grow your table to 2X it's size, there's no way to reduce it without reorganization).
 
Cheers,
Tanel.
 

----- Original Message -----
From: Siddharth Haldankar
To: Multiple recipients of list ORACLE-L
Sent: Thursday, September 11, 2003 4:19 PM
Subject: RE: converting long to lob - the easiest and fastest way ?


Thanks Tanel,

But to_lob does not work in update clause, it only works in select clause, that is the restriction it has.

With Warm Regards



Siddharth Haldankar
Cisco Systems Inc. ODC
Zensar Technologies Ltd.
#  : 4128374
[EMAIL PROTECTED]
[EMAIL PROTECTED]
-----Original Message-----
From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On Behalf Of Tanel Poder
Sent: Thursday, September 11, 2003 4:59 PM
To: Multiple recipients of list ORACLE-L
Subject: Re: converting long to lob - the easiest and fastest way ?

Sorry, I didn't notice in the end of your post, that you don't want to recreate your table.

Then try this one:

add a new clob type column to your table using alter table add.
make an update statement:
   update tableA set col1=null, newcol=to_lob(col1);

use alter table drop column col1 afterwards and if needed, rename newcol to col1 using alter table rename column (or whatever the syntax was).

You might want to do this in batches and commit meanwhile if your table is big.
Also, if your table is big, then you should test which mode lob segments are faster for your update - nocache nologging or cache logging. Oracle might be able to optimize serial new chunk write requests to batches, but I don't know whether it's true. It depends on the size of your current LONGs as well..

Tanel.

----- Original Message -----
From: Siddharth Haldankar
To: Multiple recipients of list ORACLE-L
Sent: Thursday, September 11, 2003 1:14 PM
Subject: converting long to lob - the easiest and fastest way ?

Hi Gurus,

How do I convert a long datatype to clob datatype.
Say Table A
No                    number
Col1                 long
Would like to convert to
Table A
No                    number
Col1                  clob

Alter table A modify (col1 clob);
Gives an error as inconsistent datatypes.

Could you give me an example of this with lob_storage_clause.

I would like to convert the existing data in clob type also. The to_lob works only for select statements, cannot go through that pain of table creation.

Thanks in advance.

- Siddharth

Reply via email to