烦人的sqlserver到oracle的表结构转换

2023-02-22 00:00:00 创建 字段 主键 结构 转换成

这几天搞了个sqlserver到oracle的ogg同步,总体来说 ,就是 配置参数,建表,初始化数据,配置进程。额,搞了2 ,3天。总的来说还是技术不过关。工欲善其事必先利其器,花的时间长就要看花哪里了。回忆并总结了一下,原来 花了一天的时间 纠结在表结构的配置上。OGG的initload不会创建表。所以必须自己想办法弄表结构。

心路历程

配置透明网关, CTAS, 以为成功了,But ...

  1. 表名和字段名全部小写了,需要加"";
  2. 源库表太长,没办法通过透明网关查询;
  3. 字段超过30 创建表会报错;

使用oracle的sql developer ,同步数据,基本上成功了,But:

  1. 必须同时连接oracle 和 sqlserver 两个库;
  2. sqlserver 的varchar 大于4000的时候,没有处理,导致建表的时候报错;
  3. varchar转成varchar2的时候变成了VARCHAR2(50 CHAR) ,着实不好看;
  4. 主键名称怪怪的,不好看,字段太长了没有处理,表太长了,没有处理;

还有没有什么工具可以用呀。

网上大神 还是很多的,《SQLServer表结构转换成Oracle表结构》 https://blog.csdn.net/mh942408056/article/details/71330540

试了一下效果挺好的,不过依然存在几个问题:

  1. 1个1个表来搞也是麻烦;
  2. 没有考虑表太长的情况,没有考虑字段太长的情况;
  3. 没有考虑的varchar 大于4000的时候的情况;

因此在基础上修改一下:

  1. varchar 超过4000的转换成clob;
  2. 表名,字段名限制为30个字符;
  3. 改成用中间表ConvOracleList 来实现多表操作;
  4. 修复源存储过程多表无法循环问题; 更多的数据类型的转换,下图是sql developer工具的类型转换,可参考:

实操过程

1 创建中间表

字段说明:源表名,目标表名,目标主键名,ddl文本

create table ConvOracleList( table_name varchar(100) not null ,tar_tab_name varchar(30) not null ,prikeyname varchar(100),ddl_text text);

alter table ConvOracleList add constraint pk_ConvOracleList primary key(table_name);





2、插入需要处理的表

insert into ConvOracleList 
select CONVERT(varchar(100),x.name),substring(x.name,1,30),'PK_'+CONVERT(varchar(100),x.name),NULL
  from sys.tables x where x.name<>'ConvOracleList'
 order by x.name; 







3、处理源库表过长的条目

 select *,LEN(tar_tab_name),LEN(prikeyname) from ConvOracleList where LEN(tar_tab_name)>30 or LEN(prikeyname)>30;




确认表名和主键名没有大于30的,如果有,则先修改:

 update ConvOracleList set prikeyname ='PK_PersonalizationAllUsers' where table_name='AAAAA_PersonalizationAllUsers';




4、创建存储过程(核心内容)

CREATE PROCEDURE [dbo].[Conv2Oracle]
as
begin
/********************   
* created: sqlserver 表结构转换成Oralce 表结构
* modify by chuchu , 2021-05-08:  新增多种字段的转换
* modify by chuchu , 2021-05-08:  表名,字段名限制为个字符
* modify by chuchu , 2021-05-08:  改成用中间表ConvOracleList 来实现多表操作
* modify by chuchu , 2021-05-08:  修复源存储过程多表无法循环问题
**********************/  
--取消影响行数 
set nocount on;  
  
--创建表名游标 
declare table_cursor cursor for  
(select CONVERT(varchar(100),x.name) name,CONVERT(varchar(500),y.value) value ,z.tar_tab_name,z.prikeyname
  from sys.tables x  
  left join (select major_id,value from sys.extended_properties where minor_id=0) y  
    on x.object_id=y.major_id
  inner join ConvOracleList z
    on x.name=z.table_name
);
  
--声明变量 
declare   
  @sql varchar(max)='',  
  @primary varchar(100),  
  @tablename varchar(100),  --表名称 
  @tabledes varchar(500),   --表名称描述
  @tartabName varchar(30),  --目标表名,截取个字符
  @prikeyname varchar(100); --主键名
   
--创建表结构临时表 
create table #table(colname varchar(300),  
    isprimary int,  
    typename varchar(50),  
    intlength int,  
    decimallength int,  
    nullflag int,  
    defaultval varchar(50),  
    commonts varchar(500)   
)  
   
--打开游标 
open table_cursor; 
fetch next from table_cursor into @tableName,@tabledes,@tartabname,@prikeyname; 
while @@FETCH_STATUS = 0  
    begin   
        --初始化变量 
        select @sql='' ; 
        select @tabledes = case when  ISNULL(@tabledes,'')='' then '' else @tabledes end
        truncate table #table;   
        
        --获取表字段的类型信息
        insert into #table (colname,isprimary,typename,intlength,decimallength,nullflag,defaultval,commonts)  
        SELECT CONVERT(varchar(300),replace(a.name,' ','')) [字段名],   
               (case when (SELECT count(*)  
                             FROM sysobjects  
                            WHERE (name in (SELECT name  
                                              FROM sysindexes  
                                             WHERE (id = a.id)  
                                               AND (indid in (SELECT indid  
                                                                FROM sysindexkeys  
                                                               WHERE (id = a.id)  
                                                                 AND (colid in (SELECT colid   
                                                                                  FROM syscolumns    
                                                                                 WHERE (id = a.id)  
                                                                                   AND (name = a.name)))))))  
                              AND (xtype = 'PK')) > 0 then  1  
               end) [主键],  
               b.name [类型],   
               COLUMNPROPERTY(a.id, a.name, 'PRECISION') as [长度],  
               isnull(COLUMNPROPERTY(a.id, a.name, 'Scale'), 0) as [小数位数],  
               (case when a.isnullable = 1 then 1 else 0 end) [允许空],  
               e.text [默认值],  
               CONVERT(varchar(500),g.[value]) AS [说明]  
          FROM syscolumns a  
          left join sys* b  
            on a.xtype = b.xusertype  
         inner join sysobjects d  
            on a.id = d.id  
           and d.xtype = 'U'  
           and d.name <> 'dtproperties'  
          left join syscomments e  
            on a.cdefault = e.id  
          left join sys.extended_properties g  
            on a.id = g.major_id  
           AND a.colid = g.minor_id  
          left join sys.extended_properties f  
            on d.id = f.class  
           and f.minor_id = 0  
         where b.name is not null  
         and d.name=@tableName  
         order by a.id,a.colorder;
  
 
        --构建表结构 
        select @sql+='  
    '+case colname when 'Comment' then 'Comments' else SUBSTRING(colname,1,30) end +' '  
             +case typename   
                when 'image'      then 'BLOB '
                when 'binary'     then 'BLOB '
                when 'varbinary'  then 'BLOB '
                when 'text'       then 'CLOB '
                when 'xml'        then 'CLOB '
                when 'ntext'      then 'NCLOB '
                when 'uniqueidentifier' then 'varchar2(36) '
                when 'time'       then 'varchar2(200) '
                when 'datetimeoffset'       then 'varchar2(200) '
                when 'datatime2'       then 'varchar2(200) '
                when 'date'       then 'varchar2(200) ' 
                when 'sysname'     then 'varchar2(128) '
                when 'timestamp'     then 'number(8,0) '
                when 'varchar'    then 
                   case when intlength>4000  then 'CLOB '   --当varchar>4000的时候,oracle不支持,需要转成clob
                        when intlength<0     then 'CLOB '   --max ,-1  CLOB 
                        else  'varchar2('+CONVERT(varchar(10),intlength)+') '  
                   end
                when 'nvarchar'  then 
                   case when intlength>4000  then 'NCLOB '
                        when intlength<0     then 'NCLOB '   --max ,-1  CLOB 
                        else 'nvarchar2('+CONVERT(varchar(10),intlength)+') '  
                   end
                when 'bit' then 'number(1,0) ' 
                when 'int' then 'number(10,0) '
                when 'integer' then 'number(10,0) '
                when 'bigint'  then 'number(19,0) '
                when 'smallmoney' then 'number(10,4) '
                when 'money' then 'number(19,4) '
                when 'numeric' then 'number('+CONVERT(varchar(10),intlength)+ ( case when decimallength>0 then ','+ CONVERT(varchar(10),decimallength) else '' end)+') '   
                when 'decimal' then 'number('+CONVERT(varchar(10),intlength)+ ( case when decimallength>0 then ','+ CONVERT(varchar(10),decimallength) else '' end)+') '  
                when 'datetime' then 'date '  
                when 'smalldatatime' then 'date '
                else typename  
             end   
             + case when defaultval is not null and len(defaultval)>0 then 'default '+  
                 (case when charindex('getdate',defaultval)>0 then 'sysdate '  
                       when charindex('newid',defaultval)>0 then 'sys_guid() '    
                       else (case when typename='int' or typename='decimal' then REPLACE(REPLACE(defaultval,'(',''),')','') else defaultval end )  
                  end)   
             else '' end   
             + case when nullflag=0 then ' not null,' else ',' end  
          from #table;  

          
        if @sql is not null and len(@sql)>0  
            begin  
                set @sql=left(@sql,len(@sql)-1);  
                  
                --创建表结构 
                set @sql='create table '+ @tartabname +'('+@sql+  
                '  
);  
comment on table '+@tartabname+' is '''+@tabledes+''';
'                 
                --添加字段备注 
                select @sql+= case when commonts is not null and len(commonts)>0 then 'comment on column '+@tartabname+'.'+colname+' is '''+commonts+'''; 
                ' else '' end  
                  from #table;
                   
  
                --添加主键索引  
                if exists(select 1 from #table where isprimary=1 )   
                    begin  
                        set @primary=''  
                        select @primary+= colname+','   
                          from #table  
                         where isprimary=1  
                        set @primary=left(@primary,len(@primary)-1);  
                        set @sql+='alter table '+@tartabname+' add constraint '+@prikeyname+' primary key ('+@primary+') using INDEX;
                        '  
                    end
                --行尾加两个空行
               select @sql+='
        
               ' 
            end
       --更新到中间表
        update ConvOracleList  set ddl_text = @sql  where table_name = @tableName;
       
       
       print CAST(@sql AS TEXT);

        fetch next from table_cursor into @tableName,@tabledes,@tartabname,@prikeyname ; 
    end  
close table_cursor;  
deallocate table_cursor;
--删除临时表 
drop table #table;

end
































































































































































































5、生成ddl 更新到中间表

update ConvOracleList set ddl_text=null;
exec conv2oracle 
select ddl_text from ConvOracleList;





6、导出查询结果

create table ACSI(  
    ACSI_ID varchar2(20)  not null,  
);  
comment on table ACSI is '';
alter table ACSI add constraint PK_ACSI primary key (ACSI_ID) using INDEX;








7、编辑脚本,可以加个表空间,然后执行即可。

至此sqlserver 转换oracle 的表结构完成,除了适用于ogg迁移,普通的数据迁移也是适用的。


本文来源https://www.modb.pro/db/66735

相关文章