今天遇到一个问题,某表中的某字段原始类型为varchar2,存的数据为日期,但是是字符串的形式,例:20100101.
    
    由于业务需要,需要把该字段改成date类型,但是表中的数据又不能删,很明显的删除会报错.    于是我尝试了一种方法:
    
    (1)用游标取出所有的数据,放到内存中.    (2)使用动态sql删除表中的所有数据,接着使用动态的sql执行
    alter table table_name modify column_name date;     (3)使用loop fetch into end loop close 等语句把原数据插入了表中.    在尝试之前我觉得这个是会失败的,原因是我之前尝试过使用动态sql创建一张表,然后接着使用非动态sql往表中插入一条 
    数据,会报table or view has not exists,根据错误我判断oracle服务器是先编译sql语句,再编译动态sql的.
    所以这次我认为也会先执行loop end loop中的insert 语句,然后才执行alter table的动态sql,这样就会报错,因为此时类
    型是没有改成date的,而插入的是date类型的值.不料却成功了.    代码如下:
    
declare
    cursor cur_test is
        select * from t_test_date;
    str_dynamic_sql  varchar2(4000);
    vstr_date01      varchar2(32);
    i_total          number;
    str_column_name  varchar2(32);
    str_data_type    varchar2(32);
begin
    open cur_test;
    
    str_dynamic_sql := 'delete from t_test_date';
    execute immediate str_dynamic_sql;
    
    str_dynamic_sql := 'alter table t_test_date modify str_date01 date';
    execute immediate str_dynamic_sql;
    
    loop
        fetch cur_test into vstr_date01;
        exit when cur_test%notfound;
        insert into t_test_date values(to_date(vstr_date01,'yyyymmdd'));
    end loop;
    commit;
end;
        在写这个讨论贴的时候,我突然领悟了,但是既然写的差不多了,那么就贴出来吧,下面是我的领悟.
    oracle服务器是先编译sql再编译动态sql的,上述代码成功的原因是编译sql时,字段、权限、表结构、用户等都是存在的,此时它没有运行整个程序块,而是接着编译动态的sql,发现相关对象也是存在的,于是整个块按顺序执行.
    而使用动态sql创建表,然后使用sql插入这种方式,在编译sql时就发现表不存在,所以就直接报错了,没有继续去编译动态sql.错误代码如下:
    
    begin
    execute immediate 'create table test_001 (arg01 varchar2(32))';
    insert into test_001 values('123');
    commit;
    end;
        之前的结论时:程序块或函数或过程是先执行sql再执行动态sql.
    现在的结论是:顺序执行,但是编译是先编译sql,再编译动态sql.    不知道对不对呢?请高手解答.
    

解决方案 »

  1.   

    第一段代码忘记close了. -_-!!
      

  2.   


    何必要这样呢?
    1、直接新建一个表,和你原来表结构一样,只是要将你那个varchar2的日期字段改成date类型
    CREATE TABLE temp(......, rq DATE ,....) NOLOGGING;
    2、将你原表数据insert到新表temp
      INSERT /*+append*/ into temp SELECT ....,To_Date(rq,'yyyymmdd'),....) FROM oldtable  --执行插入
    3、删除原表
    DROP TABLE oldtable ;
    4、修改别名
    ALTER TABLE temp RENAME TO oldtable
    5、重建index (如果原来表有索引)
    create index index_name on  。
    6、恢复日志记录
    alter table oldtable logging
    --补充:
    当然,上面这6步你可以采用过程来执行
      

  3.   

    首先你的错误纠正SQL> ed
    已写入 file afiedt.buf  1  begin
      2      execute immediate 'create table test_001 (arg01 varchar2(32))';
      3      execute immediate 'insert into test_001 values(''123'')';
      4      commit;
      5*     end;
    SQL> /PL/SQL 过程已成功完成。SQL> select * from test_001
      2  /ARG01
    --------------------------------
    123
      

  4.   

    然后想修改字段类型是很危险的 谨慎 设计表的时候就应该想清楚其次可以想替代法先建个表(nologing) 
    把数据处理插(to_date处理)以appedn 方式插入表 
    提交 
    建索引
    --改表名
    rename oldtb to oldtb1
    rename newtb to oldtb
      

  5.   

    谢谢1楼的回答,你这个方法也蛮好的,不过我讨论的是动态sql执行顺序问题.谢谢2楼的回答,我知道你这个方式可以,但是我想要做的是动态create table,然后非动态执行insert会出现错误.
    但是动态执行alter table ,非动态执行insert则不会出现错误的原因.同样是DDL语句嘛.最后我得出的那个结论我想知道根据俩位的理解是不是对的.
      

  6.   


    恩,是的,不过我最想讨论的是我从那个修改方式中测试出了一些动态SQL与普通SQL执行的一个顺序.