在Oracle中,如果需要从其他数据库获取数据,我们一般建立的是DB_Link; 在SQL Server 2012中, 我们称之为“Linked Server”。
由于新的平台是以SQL Server为主,因此下面的讨论都是以SQL SERVER中如何写SQL来连接两个数据库。
详细配置可以参考这篇:点击打开链接
这里假设:
Linked Server名字:Linkerserver001
远端Oracle的数据库用户:user
远端Oracle的数据库表名:table
我们可以这样获取Oracle的数据库的数据 (记住是两个点..)
SELECT * FROM [Linkerserver001]..user.table where column1 =??? and column2 = ???
但是,但是,但是, 上面的这种写法性能及其的差!!!
主要原因是用这种方式Linked Server会先把所有的数据从Remote server (Oracle)抓取过来,纳尼?全部抓取过来? 也就是说如果远端的表有一千万条记录,你只要获取其中的某一条(或者几条), 你得把这个表先搬过来,然后再做条件筛选...
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)
第二步,修改程序,从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
时隔多年,还是觉得要写点东西给以后留个念...