SQL Server和Oracle的那些事 - Linked Server 和 DB_link

由于工作的需要,新的平台需要将之前支持Oracle的数据传输改成SQL Server 2012, 同时旧的系统的Oracle数据库仍旧必须保留。 这就涉及到如何在SQL Server和Oracle两个异构的数据库之间进行数据传输,同时还要兼顾到性能的问题。

在Oracle中,如果需要从其他数据库获取数据,我们一般建立的是DB_Link; 在SQL Server 2012中, 我们称之为“Linked Server”。

由于新的平台是以SQL Server为主,因此下面的讨论都是以SQL SERVER中如何写SQL来连接两个数据库。

在SQL SERVER中建立Linked Server连接Oracle数据库

  • 第一步:安装Oracle client software
  • 第二步:安装Oracle ODAC software
  • 第三步:重启 SQL Server Services
  • 第四步:配置OraOLEDB.Oracle provider
  • 第五步:创建Linked Server (右键点击“Server Objects”, 选择“New”, 然后选择“Linked Server”)

详细配置可以参考这篇:点击打开链接

Linked Server在SQL Server中的使用

这里假设:

Linked Server名字:Linkerserver001

远端Oracle的数据库用户:user

远端Oracle的数据库表名:table

我们可以这样获取Oracle的数据库的数据 (记住是两个点..)

SELECT * FROM [Linkerserver001]..user.table where column1 =??? and column2 = ???
但是,但是,但是, 上面的这种写法性能及其的差!!!

主要原因是用这种方式Linked Server会先把所有的数据从Remote server (Oracle)抓取过来,纳尼?全部抓取过来? 也就是说如果远端的表有一千万条记录,你只要获取其中的某一条(或者几条), 你得把这个表先搬过来,然后再做条件筛选...

那还有其他方法吗? 有,就是使用OpenQuery

OpenQuery的几大用法: select,delete, insert, update

以下SQL是从微软的标准指引里Copy过来,不用多讲,都看得明白:

SELECT * FROM OPENQUERY(Linkerserver001,'SELECT name FROM joe.titles WHERE name = ''NewTitle''');
DELETE OPENQUERY (Linkerserver001, 'SELECT name FROM joe.titles WHERE name = ''NewTitle''');
INSERT OPENQUERY (Linkerserver001, 'SELECT name FROM joe.titles')  VALUES ('NewTitle');  
UPDATE OPENQUERY (Linkerserver001, 'SELECT name FROM joe.titles WHERE id = 101')   SET name = 'ADifferentName';  

前面的Select 和 delete比较容易懂,跟Oracle的写法差不多,但是insert和delete比较怪,那个query是先要写select的,适应一下就可以了。然后那个查询条件里面的单引号自己注意一下。

然后,另外一个坑爹的东东又出来了。“OPENQUERY does not accept variables for its arguments”, 啥子?不支持在查询条件中传入参数???那我在存储过程(Store Procedure)中如果要传入条件咋办???-- 你就是在外面构建好了sql ,如果带参数都不能传入,会报错...

下面这段在SP中会报错:

@VAR varchar(2)
@VAR = 'M'
SELECT * FROM OPENQUERY(Linkerserver001, '''SELECT * FROM MyTestDB.dbo.Employee WHERE Gender = ''' + @VAR + '''' + '''') 

但是微软还是比较仁慈的,他提供了另外几种解决方案:

详情见 how-to-pass-a-variable-to-a-linked-server-query

第一种, 只是SQL是带变量的,用EXEC就可以了,以下的例子其实不大好,一般SP (Store Procedure)里面主要是修改数据的比较多(insert, update, delete):

DECLARE @TSQL varchar(8000), @VAR char(2)
SELECT  @VAR = 'CA'
SELECT  @TSQL = 'SELECT * FROM OPENQUERY(MyLinkedServer,''SELECT * FROM pubs.dbo.authors WHERE state = ''''' + @VAR + ''''''')'
EXEC (@TSQL)

第二种,连Linked Server的名字都是传变量进去

DECLARE @OPENQUERY nvarchar(4000), @TSQL nvarchar(4000), @LinkedServer nvarchar(4000)
SET @LinkedServer = 'MyLinkedServer'
SET @OPENQUERY = 'SELECT * FROM OPENQUERY('+ @LinkedServer + ','''
SET @TSQL = 'SELECT au_lname, au_id FROM pubs..authors'')' 
EXEC (@OPENQUERY+@TSQL) 

第三种,用Sp_executesql这个系统自带的store procedure,这种方法没有亲自试验过

DECLARE @VAR char(2)
SELECT  @VAR = 'CA'
EXEC MyLinkedServer.master.dbo.sp_executesql
     N'SELECT * FROM pubs.dbo.authors WHERE state = @state',
     N'@state char(2)',
     @VAR
呼....又解决了一个坑。以上实践,12小时的执行时间缩短到了只需一俩分钟。

但是,又来了一个奇葩的需求(别问我为啥有这种需求,系统多了啥需求都有...), 我们需要在SQL SERVER这边执行Store Procedure,把数据推到Oracle的数据库中,但是数据量很大(每次大概推送千万条记录),用了上面的写法...呵呵呵,一个insert的操作花了12个小时。尽管看到数据一秒钟可以更新几十条,但是经不住数据量大呀...

但是这也难不倒哥, 奇葩的需求有奇葩的做法,以下做法仅供参考,毕竟越复杂的设置会是系统的稳定性下降。

第一步,在Oracle那边建个DB_link (https://community.oracle.com/thread/658751)

  1. Install ODBC
  2. Setup tnsnames.ora for odbc connectivity (authentication=none)
  3. Create db-link

第二步,修改程序,从SQL这边远程调用Oracle的SQL,然后回调回SQL Server,这样就把推的数据(SQL SERVER推到Oracle)变成在Oracle这边从SQL Server拉数据过来,稍稍修改一下exec的语句,变成exec(query) at MyLinkedS, 经过测试,可以缩减几个小时的执行时间到几分钟,性能得到大大的改进。

DECLARE @Sql VARCHAR(1000)
SET @Sql = 'Delete from user.table WHERE STATUS<>''C'''
EXEC (@Sql) at Linkerserver001
SET @Sql = 'Insert /*+APPEND*/ into user.table select distinct * from sqltable@db_link where column1 =111'
exec(@Sql) at Linkerserver001   
时隔多年,还是觉得要写点东西给以后留个念...


你可能感兴趣的:(数据库(SQL),软件开发/架构)