oracle数据库迁移sql server2008

    头儿让我将现在的项目数据库从oracle10g迁移到sql server2008,为了以后的需要吧。数据库大概是370张表,70个视图,30多个函数,十几个存储过程,我搞了将近两周,效率比较低,下面就把这两周所遇到的问题总结一下。

    因为以前没做过,所以就上网找找有什么工具之类的没,结果只找到两个方法,一个是微软的工具SSMA,一个是用sql server管理工具自带的导入功能。

    关于SSMA(后边的确是用上了这个工具,使用起来挺简单,具体方法自己查),上来就遇到一个头疼的问题,就是连接oracle,如图

其中server name我弄了很长时间,试了很多也没试出来是什么东东,以为跟登录oracle时的Datebase有关,如图

后来在网上查到了,在oracle中查询select * from v$instance,结果中的HOST_NAME就是这个server name,sid(数据库的唯一标示符)在安装oracle时默认是orcl,如果你在安装的时候修改了,在oracle的安装目录下,如D:\oracle\product\10.2.0\db_1\dbs,看文件名如SPFILEORCL.ORA,那么就是orcl,也就是说文件名字是 "SPFILE******.ORA ",其中的 "**** "就是SID,剩下的几项就好填了。然后就是SSMA连接sql server如图

这里边的server name就是在登录sql server管理工具时候的服务器名称。这是SSMA的第一个问题。第二个问题就是转换出了很多问题,oracle表里边number类型的数据,它都给转为了float(53),三百多张表,太扯淡了,因为我们数据库中number大部分都是int型的(也不能怪SSMA,float(53)是最保险的类型),还是老老实实自己转吧,其他的视图,存储过程,函数都有错,看来微软的这个东西还不是很智能。

    还是不甘心,再找其他方法,发现有人用sql server管理工具自带的导入功能,右键数据库->任务->导入数据,这个东西也不好使,具体不多说了,最后终于做了一个艰难的决定,自己动手,丰衣足食。

    一、转表

    先用pl/sql Developer查看该表的建表sql,然后把建表语句复制出来,我把所有表的sql都搞到一个文件中去,方便一次执行就把表都生成了。如果这个表有外键,索引,先不要管,把创建外键索引的语句复制到另一个文件,等你把表都创建好了,再把这些创建外键索引的语句执行一下就行了。转表的问题不大,就是一些数据类型的转换(需要注意number类型),如下。

Oracle 数据类型                    SQL Server 数据类型                  备用
BFILE                                VARBINARY(MAX)                      是
BLOB                                 VARBINARY(MAX)                      是
CHAR([1-2000])                  CHAR([1-2000])                        是
CLOB                                 VARCHAR(MAX)                        是
DATE                                DATETIME                                是
FLOAT                              FLOAT                                     否
FLOAT([1-53])                   FLOAT([1-53])                          否
FLOAT([54-126])                FLOAT                                     否
INT                                  NUMERIC(38)                            是
INTERVAL                          DATETIME                                是
LONG                                VARCHAR(MAX)                         是
LONG RAW                         IMAGE                                     是
NCHAR([1-1000])                NCHAR([1-1000])                      否
NCLOB                              NVARCHAR(MAX)                       是
NUMBER                            FLOAT                                     是
NUMBER([1-38])                 NUMERIC([1-38])                       否
NUMBER([0-38],[1-38])       NUMERIC([0-38],[1-38])              是
NVARCHAR2([1-2000])        NVARCHAR([1-2000])                  否
RAW([1-2000])                  VARBINARY([1-2000])                 否
REAL                                 FLOAT                                    否
ROWID                              CHAR(18)                                否
TIMESTAMP                        DATETIME                              是
UROWID                            CHAR(18)                                否
VARCHAR2([1-4000])         VARCHAR([1-4000])                    是

    二、我第二个转的是视图,后来证明,视图是第二个好转的东东,下面是视图中遇到的问题和需要注意的地方。

    1.oracle中如果有select ... from dual的语句,sql server中把from dual去掉即可。

    2.关于二者的函数,我基本是从http://www.cnblogs.com/wuchaocug/archive/2011/05/27/2059490.html来对比转换的。

    3.sql server没有wm_concat函数(连接一个字段中的字符串),需要自己写一个函数来替代了,给一个参考:

SELECT  USUID ,
        RESUME = STUFF(( SELECT   ',' + (StartDate+'-'+EndDate+' ' +CropName+''+Duty)
                       FROM     tbWorkResume t
                       WHERE    t.USUID = tbWorkResume.USUID
                     FOR
                       XML PATH('')
                     ), 1, 1, '')
FROM    tbWorkResume
GROUP BY USUID

select userid,STUFF((select ','+JOBNAME from tbjoborder j,TBUSERJOB u
							  where  j.joid= u.JOID and u.USERID=uj.USERID
							  for xml path('')
							  ),1,1,'') jobname from tbuserjob uj
				group by userid

    4.错误:除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效。在select 语句后加top 100 percent 即可。

    5.oracle的start with... connect by树查询语法sql server是没有的,这里用with as替代(http://blog.csdn.net/key_the_one/article/details/7222128写的更清楚),如下:

with node AS ( select organid  FROM tborgan 
				 WHERE organid IN (SELECT FOREIGNKEYID as organid FROM tbTPUser a WHERE SELECTMODE=1)
				 UNION ALL SELECT bb.organid
				 FROM tborgan bb inner join node no ON no.organid = bb.organfatherid )
				 --此后跟的插入或查询语句
				 select * from node 
这里需要注意,如果查询出来的结果是要作为其他查询的子条件的话,上边的sql语句是不能用小括号包起来的,解决办法可以讲其再写入一个视图(假设起名conn),然后用SELECT ORGANID from conn调用即可。

    6.其中可能会调用一些自定义的函数,我先不管他,等把函数转完后,再回头生成这些视图。

    7.SSMA转的视图还凑合,你想省事可以从它那复制,不过必须注意,你复制之前必须看一下,有一些东西也是必须再手动转的,可能你复制过来执行的话,不会报错,但有些东西一看明显是不对的,比如SSMA把

Trunc(months_between(SysDate,f.born)/12) UserAge
转为
sysdb.ssma_oracle.trunc(sysdb.ssma_oracle.months_between(sysdatetime(), f.BORN) / 12, DEFAULT) AS UserAge
执行时候是不报错的。

    三、自定义函数的转换,这个不太好转(当然如果函数简单的话,还是挺好转的),但感觉可说的,可注意的地方也不太多。

    1.自定义函数里边不支持begin try和begin catch,可以用if @@error<>0(如果上边的语句执行不正确)替代。

    2.if和else、else if里边的内容最好用begin end包裹。

    3.oracle不支持返回table类型,一般用管道类型pipelined返回,转换的时候sql server可直接返回table类型,而在定义的时候,虽然我看网上有人直接这样写:

create function test()
returns table
as
begin
	return select * from userTable
end
go
但是执行的时候会报'BEGIN' 附近有语法错误。这样定义一个table就对了:

create function test()
returns @Table table(userid int,username varchar(10))
as
begin
	insert into @Table select userid,username from userTable
	return
end
go
  4.sql server返回游标的问题,这个还没解决,弄好了再更新在这里吧。
  5.oracle的charIndex()方法里可以有第四个参数,意思是第几次出现,sql server该方法没有第四个参数,所以必须再写一个方法替换,下面是我写的,可能不是很完美

create function dbo.myCharIndex
(
	@partString varchar(max),
	@fullString varchar(max),
	@startIndex int,
	@time int
)
returns int
as
begin
	declare 
		@tempIndex int,
		@tempString varchar(max)
	set @tempIndex = 0
	if(@time < 1)
		return 0
	if(@time = 1)
		return charindex(@partString,@fullString,@startIndex)
	if(@time > 1)
	begin
		set @tempString = subString(@fullString,@startIndex,len(@fullString))
		if(len(@tempString)=0)
		return 0
		while @time >= 1
			begin
				if(@tempIndex+1 > len(@fullString))
						return 0
				else
					begin
						set @tempIndex = charindex(@partString,@tempString,@tempIndex+1)
						if(@tempIndex = 0)
							return 0
						else
							set @time = @time - 1
					end
			end
		return charindex(@partString,@fullString,@startIndex+@tempIndex-1)
	end
	return 0
end
go
    四、存储过程的转换

    1.oracle支持视图式的更新(使用/*+*/)

update (
select  /*+ BYPASS_UJVC */  a.tpid,nvl(a.cid,0) cid,a.cexamgrade,c.exscore,a.egid tpegid,c.egid
from tbtpcourseresult a 
inner join tbtestpaper b on a.tpid=b.relakey and a.cid=nvl(b.relakey2,0) and b.papermode=16
inner join tbexamineegrade c on b.tpid=c.tpid and a.userid=c.userid ) 
set cexamgrade=exscore,tpegid=egid;
sql server只能这样(不知道这样转正确不?哪位大侠给个答案)

update tbtpcourseresult set cexamgrade=c.exscore,egid = c.egid from
tbtpcourseresult a 
inner join tbtestpaper b on a.tpid=b.relakey and a.cid=isnull(b.relakey2,0) and b.papermode=16
inner join tbexamineegrade c on b.tpid=c.tpid and a.userid=c.userid

    2.关于rownum,可以用下面代替(sql server必须是2005版本以上)。还有好多方法,可以上网搜。
select * from (select Row_Number() over     
(Order by UserId) as RowId ,* from UserInfo) U 
where U.RowId between 10 and 20
    3.sql server用下面方法控制事务
BEGIN
IF @@TRANCOUNT > 0
     COMMIT WORK 
IF @@TRANCOUNT > 0
     ROLLBACK WORK 
END
    oracle用下面方法控制事务
COMMIT;
EXCEPTION
  WHEN OTHERS THEN
  ROLLBACK;

五、其他的待续吧



你可能感兴趣的:(oracle数据库迁移sql server2008)