ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Oracle与SQLServer跨库查询:dblink与链接服务器

Oracle与SQLServer跨库查询:dblink与链接服务器 1. 先把跨库查询这件事说明白跨库查询这个词听起来像是一个功能实际上是一类需求的总称。我在实际项目里遇到的场景大多长这样核心业务库跑在 Oracle 上一些外围系统、报表库、历史归档库用的是 SQLServer业务方要一张既包含订单主数据、又包含外围系统处理状态的报表于是问题就来了——数据在两个不同的数据库实例里甚至在两台不同的机器、两套不同的账号体系下怎么在一个 SQL 里把它们查出来这个问题没有唯一答案因为它背后至少有三层不同性质的需求。第一层是同一个数据库实例里跨 schema 查询这个最简单加个 schema 前缀就行第二层是同一类数据库、不同实例之间查询比如 Oracle 到 Oracle、SQLServer 到 SQLServer这时候要用 Database Link 或者链接服务器第三层才是真正的异构跨库Oracle 查 SQLServer、SQLServer 查 Oracle这一层涉及异构服务、驱动、类型转换坑最多。很多新手一上来就想用第三层的方案去解决第一层的问题结果把一个本来五行 SQL 能搞定的事情做成了需要配网关、调驱动的工程,这就是典型的方案选型失误。我把这篇内容定位成一份从选型到落地的实操记录覆盖 Oracle 和 SQLServer 各自的跨库手段也覆盖两者互相查询的异构方案。适合的读者是已经能写基本 SQL、但对跨库这件事还停留在听说过 dblink阶段的开发和运维同学。需要说明的是下面涉及的参数、配置和报错排查一部分来自我自己的项目实践一部分是基于主流版本Oracle 11g/12c/19c、SQLServer 2016 及以上常见实践的合理补充具体到你的环境版本差异还是要以官方文档为准。2. Oracle 侧的跨库能力Database Link 全流程2.1 建链之前先把网络命名这件事理清楚Oracle 的跨库查询核心对象叫Database Link简称 dblink。它的本质是在本地数据库里存一条如何连到远端数据库的记录用的时候写表名dblink名字就能把远端表当本地表查。听起来简单但绝大多数失败案例都卡在建链之前的准备工作上。关键点是Oracle 连远端靠的是连接描述符不是靠 IP 直连。连接描述符通常来自tnsnames.ora形如REMOTE_DB (DESCRIPTION (ADDRESS (PROTOCOL TCP)(HOST 10.0.0.21)(PORT 1521)) (CONNECT_DATA (SERVER DEDICATED) (SERVICE_NAME orcl) ) )这段东西有两个容易踩的地方。一是SERVICE_NAME和SID的区别老库常用 SID新库常用 SERVICE_NAME写错了会直接报连接失败。二是这个文件的位置客户端和服务端的tnsnames.ora可能不是同一个你改了本地客户端的但数据库实例实际用的是它自己$ORACLE_HOME/network/admin下的那一份结果配了半天没生效。我一般的做法是先在数据库服务器上直接用tnsping REMOTE_DB验证连通性通了再建链。GLOBAL_NAMES这个初始化参数值得单独提一句。如果它是 TRUE那么 dblink 的名字必须和远端数据库的全局名一致否则建链会失败报ORA-02085。很多环境默认是 FALSE所以随便命名也能用一旦跨环境迁移就出问题。我的建议是建链前先查一下show parameter global_names;如果是 TRUE老老实实把 dblink 命名成远端库的db_name.db_domain格式省得后面返工。2.2 创建与验证 Database Link准备工作做完建链本身很快。有两种权限级别私有 dblink 只有创建者能用公有 dblink 全库可用。生产环境我倾向于给业务账号建私有 dblink避免所有人都能顺着一根链子摸到远端库。-- 需要 CREATE DATABASE LINK 权限 CREATE DATABASE LINK link_to_remote CONNECT TO remote_user IDENTIFIED BY remote_pwd USING REMOTE_DB;这里有个细节密码里的特殊字符要用双引号包起来因为 Oracle 建 dblink 时密码会被解析成 SQL 标识符里面有、#、$这类字符不处理的话直接语法错误。我踩过一次密码里带了个排查了半天才发现是引号的事。建完立刻验证别等到业务用了才发现问题SELECT sysdate FROM duallink_to_remote;这条语句能跑通说明网络、认证、权限都正常。如果报ORA-12154那是连接描述符找不到报ORA-01017那是账号密码问题报ORA-28547一般是客户端和远端库版本不匹配导致的网络管理错误这种时候要检查本地 Oracle 客户端版本是不是太老。2.3 同义词、视图与权限治理dblink 用起来最别扭的地方是每条 SQL 都要带dblink写长了很难看而且一旦远端库换了名字所有 SQL 都得改。我的处理方式是用同义词SYNONYM封装CREATE SYNONYM remote_orders FOR orderslink_to_remote;之后业务代码直接查remote_orders本地、远端对上层是透明的。如果要更彻底一点就再套一层视图把远端的字段名、类型都对齐成业务方习惯的格式这样后续换实现方案比如改成 ETL 同步时上层一行代码都不用动。这里分享一个实战心得不要把 dblink 当长期数据集成方案用。dblink 更适合临时取个数做一次对账这种轻量场景。如果一条 SQL 里 dblink 表和外网表要 joinOracle 的优化器对远端表的统计信息往往一无所知很容易生成一个灾难性的执行计划——比如把远端几百万行的表整个拉过来再做哈希连接。我一般会在这种 SQL 上先用EXPLAIN PLAN看一眼确认有没有出现REMOTE相关的全表扫描动作必要时用/* driving_site(remote_alias) */提示把计算推到远端执行。3. SQLServer 侧的跨库能力链接服务器实战3.1 sp_addlinkedserver 的核心参数怎么填SQLServer 的对应概念叫链接服务器Linked Server。和 Oracle 的 dblink 不同SQLServer 通过 OLE DB 或者 ODBC 提供程序连接外部数据源所以配置项更多参数填错的面也更大。最基础的创建语句长这样EXEC sp_addlinkedserver server LINK_ORA, srvproduct Oracle, provider OraOLEDB.Oracle, datasrc REMOTE_DB;server是你给这条链起的本地别名后面 SQL 里就用它。provider决定用哪个驱动。Oracle 官方的OraOLEDB.Oracle通常比微软自带的MSDAORA更稳尤其连新版本 Oracle 时。datasrc是远端库的 TNS 名称或者 EZConnect 串如10.0.0.21:1521/orcl。建完服务器还要配登录映射否则查询时用的是 SQLServer 服务账号的身份去连很可能认证失败EXEC sp_addlinkedsrvlogin rmtsrvname LINK_ORA, useself FALSE, rmtuser remote_user, rmtpassword remote_pwd;useself FALSE的意思是不要用本地 Windows 身份透传用我下面指定的账号密码这一点在跨域、跨操作系统的环境里几乎是必须的。我见过太多人只建了 linkedserver 没建 login 映射然后一直报登录失败查半天以为是网络问题。3.2 OPENQUERY 与四部分命名到底用哪个SQLServer 查链接服务器有两种写法。第一种是四部分命名SELECT * FROM LINK_ORA.REMOTE_SCHEMA.ORDERS WHERE STATUS A;第二种是OPENQUERYSELECT * FROM OPENQUERY(LINK_ORA, SELECT * FROM REMOTE_SCHEMA.ORDERS WHERE STATUS A);这两种写法的性能差异巨大是跨库查询里最值得记住的一点。四部分命名时SQLServer 倾向于把远端表整体拉回本地再做过滤和 join数据量大时网络和内存直接爆掉。而 OPENQUERY 是把整条 SQL 原样送到远端执行只把结果集拿回来效率高一个数量级。所以我的经验是能下推的查询一律用 OPENQUERY尤其是带 WHERE、聚合、join 的复杂查询。四部分命名只在查小表或者做元数据探测时用。OPENQUERY 的代价是不可参数化——它只接受一个字符串字面量不能直接带变量需要动态拼 SQL这就又引出了注入风险拼的时候务必对输入做白名单校验或者用QUOTENAME处理。3.3 分布式事务与 MSDTC 的取舍只要一条 SQL 里既更新本地表又更新链接服务器上的表并且放在同一个BEGIN TRAN里SQLServer 就会升级成分布式事务交给 MSDTC分布式事务协调器协调。这里问题就来了MSDTC 默认在很多环境里没开、防火墙没放行、或者远端 OLE DB 提供程序根本不支持分布式事务于是报MSDTC 不可用或者该提供程序不支持分布式事务。我的做法是尽量避免跨库写操作放在同一个显式事务里。如果业务上必须保证一致性宁可拆成两步、加补偿逻辑也不硬上分布式事务。因为 MSDTC 的排查成本极高涉及网络、端口、域认证多个环节一旦出问题会拖垮整个发布节奏。如果确实要配核心是检查三点本地 MSDTC 服务是否启动、两台机器是否放行了相关端口、以及provider对应的AllowInProcess选项是否开启。另外在 SQLServer 链接服务器属性里RPC 和 RPC Out 这两个选项也常被忽略。如果要从本地调用远端的存储过程RPC Out必须是 True否则只能查询不能执行。4. 异构互通Oracle 与 SQLServer 互相查4.1 从 SQLServer 查 Oracle这条路径相对成熟就是上一节讲的链接服务器加 Oracle OLE DB 驱动。需要注意两点一是驱动版本要和远端 Oracle 版本匹配连 19c 用太老的驱动经常报奇怪的协议错误二是datasrc如果用 TNS 名称SQLServer 服务所在的机器必须有对应的tnsnames.ora位置通常在 Oracle 客户端安装目录的network/admin下而不是随便放一个就行。还有一个高频报错是排序规则冲突collation conflict当你把链接服务器上的字符列和本地表做 join 或比较时如果两边排序规则不同会直接报错。解决方式是在比较或 join 的条件上显式加COLLATESELECT * FROM LOCAL_T A JOIN OPENQUERY(LINK_ORA, SELECT CODE FROM T2) B ON A.CODE B.CODE COLLATE Chinese_PRC_CI_AS;4.2 从 Oracle 查 SQLServer反过来要从 Oracle 连 SQLServerOracle 这边靠的是异构服务Heterogeneous Services也就是常说的 HS ODBC 网关方案。它需要额外安装 Oracle Gateway 相关组件配置init.ora、odbc.ini、监听器把 SQLServer 通过 ODBC 数据源暴露成 Oracle 眼里的一个远端库然后就能像建普通 dblink 一样建链查询。这套方案配置复杂度明显更高我个人的态度是除非有强约束要求必须在 Oracle 里查 SQLServer否则优先反向做——也就是在 SQLServer 侧配链接服务器去查 Oracle或者干脆用中间层应用代码分别取数再内存里拼来解耦。原因很简单Oracle 到 SQLServer 的网关方案对版本、位数、驱动依赖很敏感一旦环境升级容易全线崩维护成本高。4.3 数据类型映射与那些看不见的转换异构跨库最隐蔽的坑不在连接而在类型转换。你以为字段是原样传过来的实际上中间经过了一层映射Oracle 类型映射到 SQLServer 常见结果注意事项NUMBERnumeric/decimal精度可能被截断需显式声明VARCHAR2varchar/nvarchar字符集差异可能导致中文乱码DATEdatetimeOracle DATE 含时分秒别当纯日期用CLOBnvarchar(max)大字段跨库查询性能极差TIMESTAMPdatetime2时区信息可能丢失这里专门说一下身份证号这类长数字串的问题。热词里出现过Oracle 数据库 SQL 导出的身份证信息是科学计数法这个现象在跨库场景里同样常见当 18 位身份证号被当成 NUMBER 存储在 Oracle 里导出或者跨库传给 SQLServer 时很容易被转成1.10101E17这种科学计数法。根治办法是从数据源头把身份证号、订单号这类标识字段定义为字符串类型VARCHAR2/nvarchar而不是数字类型。如果历史数据已经这么存了取数时必须用TO_CHAR显式转换SELECT TO_CHAR(ID_CARD) FROM PERSONlink_to_remote;同理SQLServer 侧遇到字符串需要转数字时要用CAST或TRY_CAST后者转换失败返回 NULL 而不是报错更适合脏数据场景别依赖隐式转换因为跨库的隐式转换规则你根本控不住。5. 性能、坑与排查速查表5.1 下推与全量拉取性能差异的关键跨库查询的性能瓶颈八成不在数据库本身而在数据有没有下推。所谓下推就是把这个过滤、聚合、join 的操作尽量放在远端执行只把结果传回来。判断有没有下推最简单的办法是看执行计划里有没有出现Remote Query相关节点以及估算的行数是不是接近全表行数。我处理过的一个真实案例一条 join 本地小表几百行和远端大表千万行的 SQL用四部分命名写执行了十几分钟还在跑改成把本地小表的条件值先查出来拼进 OPENQUERY 的字符串再用IN过滤远端几秒钟出结果。差别就在于前者把千万行拉回了本地。对于那种必须两边数据都参与 join 的场景更务实的方案是把远端数据定期同步到本地临时表用 ETL 或定时任务做增量然后在本地做 join。这也是为什么我一直说跨库查询适合应急不适合长期高频使用。5.2 典型报错排查思路跨库问题的排查有个通用套路分层定位。从网络到底是通的、到驱动对不对、到认证过不过、再到权限有没有、最后到 SQL 能不能下推一层层排除。报错现象可能原因排查动作ORA-12154连接描述符找不到检查 tnsnames.ora 位置和内容ORA-01017账号密码错误确认密码特殊字符是否需引号ORA-28547客户端与远端版本不匹配升级或对齐 Oracle 客户端ORA-02085全局名与 dblink 名不一致检查 global_names 参数登录失败SQLServer缺登录映射补 sp_addlinkedsrvlogin排序规则冲突两边 collation 不同比较条件加 COLLATEMSDTC 不可用分布式事务未配置检查服务、端口、域认证中文乱码字符集不一致统一字符集或用 nvarchar热词里还出现过SQLServer 服务启动不了错误码 17051这个大多和实例授权模式或服务账号权限有关和跨库本身没直接关系但如果你在配链接服务器时刚好后台服务重启失败很容易被误判成链接配置的问题。遇到服务起不来先单独确认服务状态别把两件事混在一起查。另一个排查小技巧用 SQLServer 的sp_testlinkedserver做连通性测试它比直接查询更快暴露底层问题是网络还是认证。EXEC sp_testlinkedserver NLINK_ORA;5.3 关于去重与数据整合的补充跨库取完数往往还要做清洗比如去重。热词里有SQLServer 删除重复数据只保留一条 无 id这个需求在跨库汇总时特别常见——从多个来源拉到数据后发现同一个业务主键有多条记录。没有自增 id 时标准做法是用窗口函数WITH T AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY biz_key ORDER BY update_time DESC) AS rn FROM merged_data ) DELETE FROM T WHERE rn 1;思路是先按业务主键分组再按某个谁更新就留谁的字段排序编号大于 1 的就是要删的重复项。这套逻辑放在跨库汇总之后做清洗非常顺手。至于热词里提到的Oracle 按逗号拆分列为多行这在跨库后的数据展开里也常用。核心思路是用CONNECT BY或REGEXP_SUBSTR递归拆开SELECT id, REGEXP_SUBSTR(codes, [^,], 1, LEVEL) AS code FROM src CONNECT BY REGEXP_SUBSTR(codes, [^,], 1, LEVEL) IS NOT NULL AND id PRIOR id AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL;拆完之后再去和远端库做比对能把多值字段这种非规范数据处理好。我个人在实际项目里的体会是跨库查询永远不要作为最终的数据架构方案它更像是一把救急的钥匙。真要长期打通两个库老老实实做数据同步、做服务接口把耦合度降下来才是省心的路。dblink 和链接服务器该会还是会但用之前先想清楚这是一次性的取数还是要长期跑的流程想清楚这一点选型基本就不会错。
返回列表