大数跨境
0
0

PG与Oracle的桥梁:手把手教你配置oracle_fdw实现跨库访问

PG与Oracle的桥梁:手把手教你配置oracle_fdw实现跨库访问 老苏畅谈运维
2025-04-25
4
导读:前一篇文章说到了pg dblink的使用方法(PostgreSQL的dblink扩展模块使用方法),这种是用在
前一篇文章说到了pg dblink的使用方法(PostgreSQL的dblink扩展模块使用方法,这种是用在pg到pg之间。那如果需要PG数据库连接oracle数据库,要怎么办?这就需要用到oracle_fdw这个插件。

oracle_fdw简介

oracle_fdw是PostgreSQL外部表插件,可以读取Oracle数据库的数据,可以使PostgreSQL轻松跨库操作Oracle。

oracle_fdw使用主要有以下三个步骤:

1,安装和创建对应插件(create extension)2,创建远程数据库链接(create server),指定对端数据库的物理信息3,创建用户映射关系(create user mapping),指定对端数据库的认证信息

加载扩展

postgres=# CREATE EXTENSION oracle_fdw;
postgres=# \dx
                                                 已安装扩展列表
        名称        | 版本 |  架构模式  |                                  描述
--------------------+------+------------+------------------------------------------------------------------------
 adminpack          | 2.1  | pg_catalog | administrative functions for PostgreSQL
 dblink             | 1.2  | postgres   | connect to other PostgreSQL databases from within a database
 oracle_fdw         | 1.2  | postgres   | foreign data wrapper for Oracle access
 pg_stat_statements | 1.9  | postgres   | track planning and execution statistics of all SQL statements executed
 plpgsql            | 1.0  | pg_catalog | PL/pgSQL procedural language
(5 行记录)

--查看扩展版本
postgres=# SELECT oracle_diag();
                         oracle_diag
-------------------------------------------------------------
 oracle_fdw 2.4.0, PostgreSQL 14.4, Oracle client 11.2.0.4.0
(1 行记录)

授权用户访问权限

psql -U postgres hrdb
postgres=# grant USAGE on FOREIGN data wrapper oracle_fdw to hr;
GRANT

创建server对象

--创建server语法:

CREATE SERVER <SERVER名称>
FOREIGN DATA WRAPPER oracle_fdw
OPTIONS (dbserver '//<Oracle数据库的内网连接地址>:<Oracle数据库的内网连接端口>/<数据库名>');


--普通用户hr操作创建server

psql -U hr hrdb
hrdb=> CREATE SERVER oradb FOREIGN DATA WRAPPER oracle_fdw OPTIONS (dbserver '//10.10.10.1:1521/orcl');
CREATE SERVER

--查看server对象
hrdb=> SELECT * FROM pg_foreign_server;
  oid  | srvname | srvowner | srvfdw | srvtype | srvversion | srvacl |            srvoptions
-------+---------+----------+--------+---------+------------+--------+-----------------------------------
 74245 | oradb   |    16446 |  74244 |         |            |        | {dbserver=//10.10.10.1:1521/orcl}
(1 行记录)


SELECT s.srvname, w.fdwname, s.srvoptions 
FROM pg_foreign_server s 
JOIN pg_foreign_data_wrapper w ON w.oid = s.srvfdw;

 srvname |  fdwname   |           srvoptions
---------+------------+--------------------------------
 oradb   | oracle_fdw | {dbserver=127.0.0.1:1521/orcl}
(1 行记录)

--删除server
DROP SERVER IF EXISTS oradb CASCADE;

创建映射

--创建到oracle的映射
create user mapping for hr server oradb options(user 'SCOTT',password 'tiger');

grant usage on foreign server oradb to hr;

--查看映射
postgres=# select * from pg_user_mapping;
  oid  | umuser | umserver |          umoptions
-------+--------+----------+-----------------------------
 74229 |  16446 |    74228 | {user=SCOTT,password=tiger}

创建外部表

创建需要访问Oracle的对应表,这里创建的时候要注意字段类型的转换,Oracle和PG库在字段类型上还是有所差别的。

create foreign table emptest(
EMPNO integer not null,
ENAME VARCHAR(10),
JOB VARCHAR(9),
MGR integer,
HIREDATE DATE,
SAL NUMERIC(7,2),
COMM NUMERIC(7,2),
DEPTNO integer
) server oradb options(schema 'SCOTT',table 'EMP')
;

create foreign table emptest1(
EMPNO integer not null,
ENAME VARCHAR(10),
JOB VARCHAR(9),
MGR integer,
HIREDATE DATE,
SAL NUMERIC(7,2),
COMM NUMERIC(7,2),
DEPTNO integer
) server oradb options(schema 'SCOTT',
table 'EMP',
max_long '32767',
readonly 'false',
sample_percent '100',
prefetch '200');

注意:

1、外部表的结构需要和Oracle中的映射表结构保持一致。 
2、oracle 端表字段发生变化,需要重建外部表。 
OPTIONS内的参数说明如下
key:是否设置对应的列为主键,取值为true或false,默认值为false。如果要执行UPDATE和DELETE操作,必须将所有主键列设置为true。 table 表名,一般是大写,必填参数。可以使用Oracle的SQL来定义table变量的值,例如:OPTIONS (table '(SELECT col FROM tab WHERE val = ''string'')'),此时不要使用schema参数。 
schema:一般是Oracle用户名,保持大写,用来访问不属于当前连接用户的表。 
max_long:限制Oracle表中LONG、LONG RAW、XMLTYPE类型列的最大长度,取值范围是1~1073741823,默认值是32767。 
readonly:限制Oracle表为只读,不允许INSERT、UPDATE、DELETE操作。 
sample_percent:设置随机选择Oracle表数据的比例,用于PostgreSQL表统计信息,取值范围是0.000001~100,默认值是100。 
prefetch:外表扫描时,PostgreSQL和Oracle数据表之间一次性传输的行数,取值范围是0~1024,默认值是200,0代表取消prefetch功能。


访问外部表

通过外部表查询oracle数据库t2表数据

hrdb=> select * from emptest;
 empno | ename  |    job    | mgr  |  hiredate  |   sal   |  comm   | deptno
-------+--------+-----------+------+------------+---------+---------+--------
  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |         |     20
  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30
  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30
  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |         |     20
  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30
  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |         |     30
  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |         |     10
  7839 | KING   | PRESIDENT |      | 1981-11-17 | 5000.00 |         |     10
  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30
  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |         |     30
  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |         |     20
  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |         |     10
  8558 | szrsu  | DBA       | 7782 | 2024-03-12 | 2500.00 |    0.00 |     10
(13 行记录)

dml操作外部表

hrdb=> insert into emptest values (8668,'aaa','ceshi',7782,'2025-4-18',2500,0,10);
INSERT 0 1
hrdb=> select * from emptest;
 empno | ename  |    job    | mgr  |  hiredate  |   sal   |  comm   | deptno
-------+--------+-----------+------+------------+---------+---------+--------
  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |         |     20
  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30
  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30
  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |         |     20
  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30
  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |         |     30
  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |         |     10
  7839 | KING   | PRESIDENT |      | 1981-11-17 | 5000.00 |         |     10
  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30
  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |         |     30
  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |         |     20
  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |         |     10
  8558 | szrsu  | DBA       | 7782 | 2024-03-12 | 2500.00 |    0.00 |     10
  8668 | aaa    | ceshi     | 7782 | 2025-04-18 | 2500.00 |    0.00 |     10
(14 行记录)

hrdb=>  delete from emptest where empno=8668;
错误:  no primary key column specified for foreign Oracle table
描述:  For UPDATE or DELETE, at least one foreign table column must be marked as primary key column.
提示:  Set the option "key" on the columns that belong to the primary key.
hrdb=> ALTER FOREIGN TABLE emptest
hrdb->   ALTER COLUMN empno OPTIONS (ADD key 'true');
ALTER FOREIGN TABLE
hrdb=>  delete from emptest where empno=8668;
hrdb=> update emptest set ename='ceshi' where empno=8558;
UPDATE 1
hrdb=> select * from emptest;
 empno | ename  |    job    | mgr  |  hiredate  |   sal   |  comm   | deptno
-------+--------+-----------+------+------------+---------+---------+--------
  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |         |     20
  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30
  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30
  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |         |     20
  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30
  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |         |     30
  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |         |     10
  7839 | KING   | PRESIDENT |      | 1981-11-17 | 5000.00 |         |     10
  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30
  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |         |     30
  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |         |     20
  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |         |     10
  8558 | ceshi  | DBA       | 7782 | 2024-03-12 | 2500.00 |    0.00 |     10
(13 行记录)

一键导入外部表

IMPORT FOREIGN SCHEMA will create foreign tables for all objects found in ALL_TAB_COLUMNS. That includes tables, views and materialized views, but not synonyms.

支持一键导入所有表、视图和物化视图

--导入语法:
IMPORT FOREIGN SCHEMA <ora_schema_name>
FROM SERVER <server_name>
INTO <schema_name>
OPTIONS (case 'lower');

--SCOTT用户的下的所有表结构导入到postgres下
IMPORT FOREIGN SCHEMA "SCOTT"
FROM SERVER oradb
INTO hr
OPTIONS (case 'lower');

###主要postgre相关用户下不要存在同名的表,否则会提示表已经存在,导入失败。

--导入后查看
hrdb=> \d
                            关联列表
 架构模式 |             名称             |     类型     | 拥有者
----------+------------------------------+--------------+--------
 hr       | a                            | 所引用的外表 | hr
 hr       | apple                        | 所引用的外表 | hr
 hr       | b                            | 所引用的外表 | hr
 hr       | big_emp                      | 所引用的外表 | hr
 hr       | bonus                        | 所引用的外表 | hr
 hr       | bonuses                      | 所引用的外表 | hr
 hr       | coursemaster                 | 所引用的外表 | hr
 hr       | demotable                    | 数据表       | hr
 hr       | department                   | 数据表       | hr
 hr       | department_employee          | 数据表       | hr
 hr       | department_manager           | 数据表       | hr
 hr       | dept                         | 所引用的外表 | hr
 hr       | dept2                        | 所引用的外表 | hr
 hr       | emp                          | 所引用的外表 | hr
 hr       | emp2                         | 所引用的外表 | hr
 hr       | emp@orcl                     | 所引用的外表 | hr
 hr       | emp_bonus                    | 所引用的外表 | hr
 hr       | employee                     | 数据表       | hr
 hr       | emptest                      | 所引用的外表 | hr
 hr       | emptest1                     | 所引用的外表 | hr
 hr       | fk_t                         | 所引用的外表 | hr
 hr       | greatab                      | 所引用的外表 | hr
 hr       | h_test                       | 所引用的外表 | hr
 hr       | hao1                         | 所引用的外表 | hr
 hr       | hao2                         | 所引用的外表 | hr
 hr       | hao3                         | 所引用的外表 | hr
 hr       | hao4                         | 所引用的外表 | hr


oracle_fdw使用效率

oracle_fdw是支持谓词下推。

看下单表查询情况:

hrdb=> EXPLAIN (ANALYZE, VERBOSE) SELECT * FROM hr.emptest WHERE empno = 8558;
                                                                                               QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Foreign Scan on hr.emptest  (cost=10000.00..20000.00 rows=1000 width=118) (actual time=0.363..0.371 rows=1 loops=1)
   Output: empno, ename, job, mgr, hiredate, sal, comm, deptno
   Oracle query: SELECT /*ab6b3416ada57caf2c6e342c08a46344*/ r1."EMPNO", r1."ENAME", r1."JOB", r1."MGR", r1."HIREDATE", r1."SAL", r1."COMM", r1."DEPTNO" FROM "SCOTT"."EMP" r1 WHERE (r1."EMPNO" = 8558)
   Oracle plan: SELECT STATEMENT
   Oracle plan:   TABLE ACCESS BY INDEX ROWID EMP
   Oracle plan:     INDEX UNIQUE SCAN PK_EMP (condition "R1"."EMPNO"=8558)
 Query Identifier: -977648984178158970
 Planning Time: 0.862 ms
 Execution Time: 0.393 ms
(9 行记录)
  • Oracle query: 发送到 Oracle 的实际 SQL 包含 WHERE (r1."EMPNO" = 8558),表明过滤条件已下推。

  • Oracle plan: Oracle 使用索引 PK_EMP 进行唯一扫描(INDEX UNIQUE SCAN),直接定位到符合条件的行,无需全表扫描

两表关联情况:

hrdb=> EXPLAIN (ANALYZE, VERBOSE) SELECT a.* FROM hr.emptest a,hr.emptest1 b WHERE a.empno=b.empno and a.empno = 8558;
                                                                                                  QUERY PLAN                                                                                     
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=20000.00..52502.50 rows=1000000 width=118) (actual time=0.477..0.492 rows=1 loops=1)
   Output: a.empno, a.ename, a.job, a.mgr, a.hiredate, a.sal, a.comm, a.deptno
   ->  Foreign Scan on hr.emptest a  (cost=10000.00..20000.00 rows=1000 width=118) (actual time=0.281..0.284 rows=1 loops=1)
         Output: a.empno, a.ename, a.job, a.mgr, a.hiredate, a.sal, a.comm, a.deptno
         Oracle query: SELECT /*ab6b3416ada57caf2c6e342c08a46344*/ r1."EMPNO", r1."ENAME", r1."JOB", r1."MGR", r1."HIREDATE", r1."SAL", r1."COMM", r1."DEPTNO" FROM "SCOTT"."EMP" r1 WHERE (r1."EMPNO" = 8558)
         Oracle plan: SELECT STATEMENT
         Oracle plan:   TABLE ACCESS BY INDEX ROWID EMP
         Oracle plan:     INDEX UNIQUE SCAN PK_EMP (condition "R1"."EMPNO"=8558)
   ->  Materialize  (cost=10000.00..20005.00 rows=1000 width=4) (actual time=0.194..0.204 rows=1 loops=1)
         Output: b.empno
         ->  Foreign Scan on hr.emptest1 b  (cost=10000.00..20000.00 rows=1000 width=4) (actual time=0.191..0.201 rows=1 loops=1)
               Output: b.empno
               Oracle query: SELECT /*ce99b44a36eda5bed5dcd62a15b9f0d5*/ r2."EMPNO" FROM "SCOTT"."EMP" r2 WHERE (r2."EMPNO" = 8558)
               Oracle plan: SELECT STATEMENT
               Oracle plan:   INDEX UNIQUE SCAN PK_EMP (condition "R2"."EMPNO"=8558)
 Query Identifier: -2990080726012200983
 Planning Time: 1.111 ms
 Execution Time: 0.528 ms
(18 行记录)

从提供的执行计划来看,查询的谓词条件已成功下推到 Oracle 数据库,且整体性能极优。

其他操作

-- 查询已创建的到oracle的连接
SELECT * from pg_user_mappings;

-- 删除创建的对象
drop foreign table emptest;
drop user mapping for hr server oradb;
drop server oradb;
喜欢这篇文章,欢迎动动你发财的小手点个赞👍!关注我,学习更多的数据库知识!

【声明】内容源于网络
0
0
老苏畅谈运维
1234
内容 122
粉丝 0
老苏畅谈运维 1234
总阅读760
粉丝0
内容122