<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>FDW &#8211; SRA OSS Tech Blog</title>
	<atom:link href="https://www.sraoss.co.jp/tech-blog/tag/fdw/feed/index.xml" rel="self" type="application/rss+xml" />
	<link>https://www.sraoss.co.jp/tech-blog/</link>
	<description>OSS専門の技術ブログ。PostgreSQL、Zabbixを中心に10種類以上のOSSの解説、アップデート情報、運用ヒントを提供。</description>
	<lastBuildDate>Wed, 04 Nov 2026 06:23:34 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/07/blog-icon-150x150.png</url>
	<title>FDW &#8211; SRA OSS Tech Blog</title>
	<link>https://www.sraoss.co.jp/tech-blog/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>oracle_fdw を使ってみる（後編）</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/using_oracle_fdw_2/</link>
		
		<dc:creator><![CDATA[高塚 遥 (Haruka Takatsuka)]]></dc:creator>
		<pubDate>Tue, 13 Nov 2018 03:55:10 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[FDW]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=799</guid>

					<description><![CDATA[oracle_fdw 紹介の後編です。 前編では oracle_fdw を PostgreSQL に導入・外部サーバ定義・ユーザマッピング・外部テーブル定義を順に行い Oracleデータベース上のテーブルに Postgr ...]]></description>
										<content:encoded><![CDATA[<p>oracle_fdw 紹介の後編です。<br />
<a href="/tech-blog/pgsql/using_oracle_fdw_1/">前編</a>では oracle_fdw を PostgreSQL に導入・外部サーバ定義・ユーザマッピング・外部テーブル定義を順に行い Oracleデータベース上のテーブルに PostgreSQL からアクセスする流れを紹介しました。<br />
今回は、機能をもう少し深堀りをしていきます。</p>
<p><span id="more-799"></span></p>
<p>本記事で使用するデータは以下の通りです。これらテーブルが Oracle側に在って、対応する同名のoracle_fdw外部テーブルがPostgreSQL側に作成されています。</p>
<ul>
<li style="list-style-type: none;">● EMP表</li>
</ul>
<pre>      EMP_NO EMP_NAME                       DEPT_CD         POST_CD       SALARY LAST_MODIFIED
  ---------- ------------------------------ --------------- --------- ---------- -------------------
           1 Lion                                           001            10000 2018/08/15 11:00:00
           2 Tiger                                          002             7000 2018/08/15 11:00:00
           3 Fox                            00005           003             5000 2018/08/15 11:10:00
           4 Pig                            00005           005             2000 2018/08/15 11:15:30
           5 Dog                            00002           004             3500 2018/08/15 11:25:00
           6 Cat                            00001           004             3500 2018/08/15 11:25:30
           7 Cow                            00006           003             5200 2018/08/15 11:30:15
           8 Rat                            00004           005             2000 2018/08/15 11:45:00
</pre>
<ul>
<li style="list-style-type: none;">● DEPT表</li>
</ul>
<pre>  DEPT_CD         DEPT_NAME
  --------------- ------------------------------
  00001           総務部
  00002           経理部
  00003           営業部
  00004           広報部
  00005           人事部
  00006           開発部
</pre>
<ul>
<li style="list-style-type: none;">● POST表</li>
</ul>
<pre>  POST_CD   POST_NAME
  --------- ------------------------------
  001       社長
  002       専務
  003       部長
  004       課長
  005       係長
  006       一般社員
</pre>
<h2>実行プランを調べてみる</h2>
<p>PostgreSQLでは EXPLAIN文を使って SQLの実行計画を確認することができます。oracle_fdw は EXPLAIN にも対応していて、PostgreSQL側の EXPLAIN で Oracle側で実行されるSQLや実行計画などの情報を参照できます。</p>
<p>oracle_fdw の外部テーブルを使った SQL の実行計画がどのように見えるかを調べて見ます。</p>
<pre>  db1=&gt; explain SELECT * FROM emp;
                                                                                   QUERY PLAN
  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   Foreign Scan on emp  (cost=10000.00..20000.00 rows=1000 width=178)
     Oracle query: SELECT /*582407e117cf2d80398b826784bcb7fc*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY", r1."LAST_MODIFIED" FROM "ORAUSER"."EMP" r1
  (2 行)
</pre>
<p>empテーブルをスキャンするために Oracle側で実行する SQL が「Oracle query:」の行に報告されます。</p>
<pre>  db1=&gt; explain (analyze) SELECT * FROM emp;
                                                                                   QUERY PLAN
  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   Foreign Scan on emp  (cost=10000.00..20000.00 rows=1000 width=178) (actual time=3.096..3.189 rows=8 loops=1)
     Oracle query: SELECT /*582407e117cf2d80398b826784bcb7fc*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY", r1."LAST_MODIFIED" FROM "ORAUSER"."EMP" r1
   Planning time: 1.687 ms
   Execution time: 3.223 ms
  (4 行)
</pre>
<p>SQLを実際に実行してみた結果を報告する analyzeオプションを付与した場合は、上記のように PostgreSQL側から見た所要時間が報告されいます。</p>
<pre>  db1=&gt; explain (analyze, verbose) select * from emp;
                                                                                   QUERY PLAN
  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   Foreign Scan on pguser.emp  (cost=10000.00..20000.00 rows=1000 width=178) (actual time=0.603..0.650 rows=8 loops=1)
     Output: emp_no, emp_name, dept_cd, post_cd, salary, last_modified
     Oracle query: SELECT /*582407e117cf2d80398b826784bcb7fc*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY", r1."LAST_MODIFIED" FROM "ORAUSER"."EMP" r1
     Oracle plan: SELECT STATEMENT
     Oracle plan:   TABLE ACCESS FULL EMP
   Planning time: 1.085 ms
   Execution time: 0.673 ms
  (7 行)
</pre>
<p>verboseオプションをつけると、「Oracle plan:」という行が出力されます。<br />
これがOracle側の実行計画です。<br />
本当にそうであるか、Oracle側で確認してみます。<br />
「Oracle query:」の行に出力されているSQL文にコメント「/*582407e117cf2d80398b826784bcb7fc*/」が挿入されているので、これをOracleで検索します。</p>
<pre> SQL&gt; SELECT SQL_ID, SQL_TEXT FROM V$SQL WHERE SQL_TEXT LIKE '%582407e117cf2d80398b826784bcb7fc%';

  SQL_ID          SQL_TEXT
  --------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------
  2zk7rdr3gtgvd   SELECT SQL_ID, SQL_TEXT FROM V$SQL WHERE SQL_TEXT LIKE '%582407e117cf2d80398b826784bcb7fc%'
  fd3s5h31jvhy1   SELECT /*582407e117cf2d80398b826784bcb7fc*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY", r1."LAST_MODIFIED" FROM "ORAUSER"."EMP" r1
</pre>
<p>上記の SQL_ID が「fd3s5h31jvhy1」の行が、PostgreSQLで実行したSQLです。このSQLの実行計画を確認します。</p>
<pre>  SQL&gt; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('fd3s5h31jvhy1', 0));

  PLAN_TABLE_OUTPUT
  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  SQL_ID  fd3s5h31jvhy1, child number 0
  -------------------------------------
  SELECT /*582407e117cf2d80398b826784bcb7fc*/ r1."EMP_NO", r1."EMP_NAME",
  r1."DEPT_CD", r1."POST_CD", r1."SALARY", r1."LAST_MODIFIED" FROM
  "ORAUSER"."EMP" r1

  Plan hash value: 3956160932

  --------------------------------------------------------------------------
  | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
  --------------------------------------------------------------------------
  |   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
  |   1 |  TABLE ACCESS FULL| EMP  |     8 |   552 |     3   (0)| 00:00:01 |
  --------------------------------------------------------------------------

  Note
  -----
     - dynamic sampling used for this statement (level=2)


  19行が選択されました。
</pre>
<p>「verbose」オプション付きで EXPLAINを実行したときに出力された「Oracle plan:」は、確かにOracle側の実行計画であることがわかりました。<br />
この例のように、実行したSQLがどのようにOracleで実行されているのかを確認したいときにはSQL文に付与されたコメントの文字列を使って調べることができます。</p>
<h2>プランナ統計情報</h2>
<p>PostgreSQLではバージョン9.2以降から外部テーブルについてもプランナ統計情報の取得が可能となりました。oracle_fdwではプランナ統計情報にも対応しています。通常のテーブルと同様に ANALYZEコマンドを使用します。ただし、autovacuumプロセスによる自動ANALYZEには対応していない点に注意が必要です。定期的な ANALYZEコマンド実行を運用に組み入れるようにしましょう。</p>
<p>それでは oracle_fdw にANALYZEをしたときの挙動を見ていきましょう。</p>
<pre>  db1=&gt; explain (analyze) SELECT * FROM emp;
                                                                                   QUERY PLAN
  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   Foreign Scan on emp  (cost=10000.00..20000.00 rows=1000 width=178) (actual time=1.450..1.499 rows=8 loops=1)
     Oracle query: SELECT /*582407e117cf2d80398b826784bcb7fc*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY", r1."LAST_MODIFIED" FROM "ORAUSER"."EMP" r1
   Planning time: 190.054 ms
   Execution time: 1.524 ms
  (4 行)
</pre>
<p>これは外部テーブルを作った直後、未だプランナ統計情報が無い状態で実行しています。<br />
EXPLAIN (analyze) の出力では、「(actual time= &#8230; )」の部分に実際の所要時間と行数、ループ数が報告されます。<br />
応答先頭行の「cost=10000.00..20000.00 rows=1000 width=178」部分の rowsを見ると、実際の行数が 8件であるにもかかわらず 1000件存在している想定で実行計画を立てています。1000件というのは不明な場合のデフォルトの件数です。</p>
<p>次に ANALYZEを実行してから、再度 EXPLAIN を実行してみます。</p>
<pre>  db1=&gt; ANALYZE emp;
  ANALYZE

  db1=&gt; explain (analyze) SELECT * FROM emp;
                                                                                   QUERY PLAN
  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   Foreign Scan on emp  (cost=10000.00..10080.00 rows=8 width=34) (actual time=0.900..0.991 rows=8 loops=1)
     Oracle query: SELECT /*582407e117cf2d80398b826784bcb7fc*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY", r1."LAST_MODIFIED" FROM "ORAUSER"."EMP" r1
   Planning time: 1.592 ms
   Execution time: 1.023 ms
  (4 行)
</pre>
<p>統計情報のと実際の件数が一致しました。oracle_fdw外部テーブルでもプランナ統計情報が機能しているといえます。</p>
<h2>Push-down機能</h2>
<p>oracle_fdwにより Oracleデータベース上のテーブルにアクセスする SQLが実行できました。<br />
このとき、SQL処理過程でどの部分が Oracleで実行されて、どの部分が PostgreSQLで実行されるのかが気になるところです。<br />
100万件のデータから20件が絞り込まれるようなSQLを実行したとき、以下のパターンが考えられますができることなら Oracle側で絞り込みを行った結果の20件のみをPostgreSQLが取得できる方が良いでしょう。</p>
<ul>
<li style="list-style-type: none;">(A) Oracleで絞り込みを実行した結果の20件をPostgreSQLが取得</li>
<li style="list-style-type: none;">(B) Oracleから100万件のデータを取得し、PostgerSQLで20件に絞り込みを実行</li>
</ul>
<p>検索条件による絞込みをより末端の方で行わせることを push-down（プッシュダウン）と呼びます。<br />
上記例でいえば (B) であったものを (A) の処理にするのが push-down です。</p>
<p>oracle_fdwでは、すべてのケースではないですが WHERE句の push-downに対応しています。<br />
ここから、実際にpush-down が行われていることを EXPLAIN文を使って確認していきます。</p>
<h3>WHERE句のpush-down</h3>
<p>WHERE句の条件を指定した場合について、確認してみます。<br />
emp表から 所属（dept_cd列）が　00005（＝人事部）であるような社員を抽出する SQL を実行します。</p>
<pre>    db1=&gt; select * from emp where dept_cd = '00005';
                                                                                                     QUERY PLAN
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Foreign Scan on pguser.emp  (cost=10000.00..20000.00 rows=1000 width=178) (actual time=1.636..1.656 rows=2 loops=1)
       Output: emp_no, emp_name, dept_cd, post_cd, salary, last_modified
       Oracle query: SELECT /*4b7ea9f8203e1ae2db752c3c52185bd8*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY", r1."LAST_MODIFIED" FROM "ORAUSER"."EMP" r1 WHERE (r1."DEPT_CD" = '00005')
       Oracle plan: SELECT STATEMENT
       Oracle plan:   TABLE ACCESS FULL EMP  (filter "R1"."DEPT_CD"='00005')
     Planning time: 194.733 ms
     Execution time: 1.685 ms
    (7 行)
</pre>
<p>「Oracle plan:」に「(filter &#8220;R1&#8221;.&#8221;DEPT_CD&#8221;=&#8217;00005&#8242;)」と出力されていることから、絞り込みがpush-downされていることがわかります。</p>
<p>続いては問題が起きそうなケースを試してみます。PostgreSQLとOracleで結果が異なる関数を実行するとどうなるでしょうか。<br />
substr関数は PostgreSQL と Oracle で仕様が違います。そのため、以下のように結果が変わってきます。</p>
<ul>
<li>PostgreSQLのsubstr関数実行結果</li>
</ul>
<pre>    db1=&gt; SELECT emp_no, emp_name, substr(emp_name, -3, 2) FROM emp WHERE substr(emp_name, -3, 2) = 'io';
     emp_no | emp_name | substr
    --------+----------+--------
    (0 行)
</pre>
<ul>
<li>Oracleのsubstr関数実行結果</li>
</ul>
<pre>    SQL&gt; SELECT EMP_NO, EMP_NAME, SUBSTR(EMP_NAME, -3, 2) FROM EMP WHERE SUBSTR(EMP_NAME, -3, 2) = 'io';

        EMP_NO EMP_NAME                       SUBSTR(EMP_NAME,-3,2)
    ---------- ------------------------------ ------------------------
             1 Lion                           io
</pre>
<p>この SUBSTR関数を WHERE句の条件に指定したとき、PostgreSQLとOracleのどちらで実行されるでしょうか。</p>
<pre>    db1=&gt; SELECT emp_no, emp_name, substr(emp_name, -3, 2) FROM emp WHERE substr(emp_name, -3, 2) = 'io';
     emp_no | emp_name | substr
    --------+----------+--------
          1 | Lion     |
    (1 行)

    db1=&gt; explain (verbose) SELECT emp_no, emp_name, substr(emp_name, -3, 2) FROM emp WHERE substr(emp_name, -3, 2) = 'io';
                                                                             QUERY PLAN
    ------------------------------------------------------------------------------------------------------------------------------------------------------------
     Foreign Scan on pguser.emp  (cost=10000.00..20002.50 rows=1000 width=154)
       Output: emp_no, emp_name, substr((emp_name)::text, '-3'::integer, 2)
       Oracle query: SELECT /*aadff49a5fa2767492d5844f517ca51c*/ r1."EMP_NO", r1."EMP_NAME" FROM "ORAUSER"."EMP" r1 WHERE (substr(r1."EMP_NAME", -3, 2) = 'io')
       Oracle plan: SELECT STATEMENT
       Oracle plan:   TABLE ACCESS FULL EMP  (filter SUBSTR("R1"."EMP_NAME",-3,2)='io')
    (5 行)
</pre>
<p>本例では WHERE句の条件が push-downされたことにより、SUBSTR関数は Oracleで実行されました。<br />
その結果該当データが1件となっています。<br />
この振る舞いは push-downの有無により応答が変わる可能性があるということであり、アプリケーションの不具合の原因となります。<br />
対策としては関数がどちらで実行される可能性もありうるということを考慮にいれて、関数の仕様差異があらわれる使い方を避けることです。</p>
<h3>ORDER BY句のpush-down</h3>
<p>PostgreSQL9.2以降から ORDER BY句の push-down が可能になり、oracle_fdwでも対応しています。<br />
ただし、文字列で並び替えを行う場合には push-down されません。</p>
<p>以下の定義となっている外部テーブルの数値型列（emp_no）と文字型列（emp_name）に対して ORDER BY の push-down を試してみます。</p>
<pre>     db1=&gt; d+ emp
                                                             外部テーブル "pguser.emp"
           列       |               型               | 照合順序 | Null 値を許容 | デフォルト | FDW オプション | ストレージ | 統計の対象 | 説明
     ---------------+--------------------------------+----------+---------------+------------+----------------+------------+------------+------
      emp_no        | integer                        |          | not null      |            | (key 'true')   | plain      |            |
      emp_name      | character varying(50)          |          |               |            |                | extended   |            |
      dept_cd       | character varying(5)           |          |               |            |                | extended   |            |
      post_cd       | character varying(3)           |          |               |            |                | extended   |            |
      salary        | bigint                         |          |               |            |                | plain      |            |
      last_modified | timestamp(0) without time zone |          |               |            |                | plain      |            |
     サーバー: oradb112
     FDW オプション: (schema 'ORAUSER', "table" 'EMP')
</pre>
<ul>
<li>ORDER BY句に数値型カラム(emp_no)を指定</li>
</ul>
<pre>  db1=&gt; explain (analyze, verbose) SELECT * FROM emp ORDER BY emp_no;
                                                                                               QUERY PLAN
      ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       Foreign Scan on pguser.emp  (cost=10000.00..20000.00 rows=1000 width=170) (actual time=3.169..3.225 rows=7 loops=1)
         Output: emp_no, emp_name, dept_cd, post_cd, salary
         Oracle query: SELECT /*e721c330da0d10a646db6bccb997a7e3*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY" FROM "ORAUSER"."EMP" r1 ORDER BY r1."EMP_NO" ASC NULLS LAST
         Oracle plan: SELECT STATEMENT
         Oracle plan:   SORT ORDER BY
         Oracle plan:     TABLE ACCESS FULL EMP
       Planning time: 3.001 ms
       Execution time: 3.267 ms
      (8 行)
</pre>
<ul>
<li>ORDER BY句に文字列型カラム(emp_name)を指定</li>
</ul>
<pre>db1=&gt; explain (analyze, verbose) SELECT * FROM emp ORDER BY emp_name;
                                                                                QUERY PLAN
      ---------------------------------------------------------------------------------------------------------------------------------------------------------------
       Sort  (cost=20049.83..20052.33 rows=1000 width=170) (actual time=2.003..2.005 rows=7 loops=1)
         Output: emp_no, emp_name, dept_cd, post_cd, salary
         Sort Key: emp.emp_name
         Sort Method: quicksort  Memory: 25kB
         -&gt;  Foreign Scan on pguser.emp  (cost=10000.00..20000.00 rows=1000 width=170) (actual time=1.876..1.928 rows=7 loops=1)
               Output: emp_no, emp_name, dept_cd, post_cd, salary
               Oracle query: SELECT /*f26579891b741d370f31c72dea761fe0*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD", r1."POST_CD", r1."SALARY" FROM "ORAUSER"."EMP" r1
               Oracle plan: SELECT STATEMENT
               Oracle plan:   TABLE ACCESS FULL EMP
       Planning time: 1.684 ms
       Execution time: 2.055 ms
      (11 行)
</pre>
<p>数値型カラムの場合だけ「Oracle plan:」に「SORT ORDER BY」が出力されていることが確認できました。</p>
<h3>結合のpush-down</h3>
<p>PostgreSQL9.6以降では、以下の条件を満たすとき、結合においてもpush-downが動作します。</p>
<ul>
<li>SELECT文であること</li>
<li>結合対象のテーブルは、同一の外部サーバに定義されていること。</li>
<li>結合は内部結合(inner join)であること。</li>
<li>結合は2つまでであること</li>
<li>結合がpush-downされるとき、ORDER BYはpush-downされない</li>
</ul>
<p>条件を満たすSQLを実行してみます。</p>
<pre>    db1=&gt; explain verbose
    db1-&gt; select e.emp_no, e.emp_name, d.dept_name
    db1-&gt;   from emp e
    db1-&gt;  inner join dept d on e.dept_cd = d.dept_cd;
                                                                                                QUERY PLAN
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Foreign Scan  (cost=10000.00..20000.00 rows=1000 width=240)
       Output: e.emp_no, e.emp_name, d.dept_name
       Oracle query: SELECT /*1f2567bfb61910168f19b658c678aa90*/ r1."EMP_NO", r1."EMP_NAME", r2."DEPT_NAME" FROM ("ORAUSER"."EMP" r1 INNER JOIN "ORAUSER"."DEPT" r2 ON (r1."DEPT_CD"= r2."DEPT_CD"))
       Oracle plan: SELECT STATEMENT
       Oracle plan:   HASH JOIN   (condition "R1"."DEPT_CD"="R2"."DEPT_CD")
       Oracle plan:     TABLE ACCESS FULL DEPT
       Oracle plan:     TABLE ACCESS FULL EMP
    (7 行)
</pre>
<p>問題なくpush-downされていることが確認できます。</p>
<p>条件違反になるように、結合を3つにして実行するとpush-downは行われないのでしょうか。試してみます。</p>
<pre>    db1=&gt; explain verbose
    db1-&gt; select e.emp_no, e.emp_name, d.dept_name
    db1-&gt;   from emp e
    db1-&gt;  inner join dept d on e.dept_cd = d.dept_cd
    db1-&gt;  inner join post p on e.post_cd = p.post_cd;
                                                                                                  QUERY PLAN
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Hash Join  (cost=30012.50..40177.50 rows=5000 width=126)
       Output: e.emp_no, e.emp_name, d.dept_name
       Hash Cond: ((e.dept_cd)::text = (d.dept_cd)::text)
       -&gt;  Foreign Scan  (cost=10000.00..20000.00 rows=1000 width=14)
             Output: e.emp_no, e.emp_name, e.dept_cd
             Oracle query: SELECT /*6c04b217ae3b4d3ffd35b76e854e0966*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD" FROM ("ORAUSER"."EMP" r1 INNER JOIN "ORAUSER"."POST" r4 ON (r1."POST_CD" = r4."POST_CD"))
             Oracle plan: SELECT STATEMENT
             Oracle plan:   NESTED LOOPS
             Oracle plan:     TABLE ACCESS FULL EMP
             Oracle plan:     INDEX UNIQUE SCAN PK_POST (condition "R1"."POST_CD"="R4"."POST_CD")
       -&gt;  Hash  (cost=20000.00..20000.00 rows=1000 width=142)
             Output: d.dept_name, d.dept_cd
             -&gt;  Foreign Scan on pguser.dept d  (cost=10000.00..20000.00 rows=1000 width=142)
                   Output: d.dept_name, d.dept_cd
                   Oracle query: SELECT /*8e88abaedea07ba4637cb0f885201300*/ r2."DEPT_CD", r2."DEPT_NAME" FROM "ORAUSER"."DEPT" r2
                   Oracle plan: SELECT STATEMENT
                   Oracle plan:   TABLE ACCESS FULL DEPT
    (17 行)
</pre>
<p>emp表とpost表の結合、depto表の参照に分かれてpush-dowされ、それぞれの結果をPostgreSQL側で結合しているようです。</p>
<p>今度は、外部結合(left join)を実行を試してみます。</p>
<pre>    db1=&gt; explain verbose
    db1-&gt; select e.emp_no, e.emp_name, d.dept_name
    db1-&gt;   from emp e
    db1-&gt;  left outer join dept d on e.dept_cd = d.dept_cd;
                                                                    QUERY PLAN
    ------------------------------------------------------------------------------------------------------------------------------------------
     Hash Right Join  (cost=20080.10..30086.65 rows=30 width=126)
       Output: e.emp_no, e.emp_name, d.dept_name
       Hash Cond: ((d.dept_cd)::text = (e.dept_cd)::text)
       -&gt;  Foreign Scan on pguser.dept d  (cost=10000.00..20000.00 rows=1000 width=142)
             Output: d.dept_cd, d.dept_name
             Oracle query: SELECT /*8e88abaedea07ba4637cb0f885201300*/ r2."DEPT_CD", r2."DEPT_NAME" FROM "ORAUSER"."DEPT" r2
             Oracle plan: SELECT STATEMENT
             Oracle plan:   TABLE ACCESS FULL DEPT
       -&gt;  Hash  (cost=10080.00..10080.00 rows=8 width=14)
             Output: e.emp_no, e.emp_name, e.dept_cd
             -&gt;  Foreign Scan on pguser.emp e  (cost=10000.00..10080.00 rows=8 width=14)
                   Output: e.emp_no, e.emp_name, e.dept_cd
                   Oracle query: SELECT /*f8e48586f7f43a704253aec0be7a2a79*/ r1."EMP_NO", r1."EMP_NAME", r1."DEPT_CD" FROM "ORAUSER"."EMP" r1
                   Oracle plan: SELECT STATEMENT
                   Oracle plan:   TABLE ACCESS FULL EMP
    (15 行)
</pre>
<p>emp表、dept表への参照がそれぞれpush-downされ、PostgreSQLで外部結合が行われました。</p>
<p>最後に、結合と&#8221;order by&#8221;を同時に行います。</p>
<pre>    db1=&gt; explain verbose
    db1-&gt; select e.emp_no, e.emp_name, d.dept_name
    db1-&gt;   from emp e
    db1-&gt;  inner join dept d on e.dept_cd = d.dept_cd
    db1-&gt;  order by e.emp_no;
                                                                                                   QUERY PLAN
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Sort  (cost=20049.83..20052.33 rows=1000 width=126)
       Output: e.emp_no, e.emp_name, d.dept_name
       Sort Key: e.emp_no
       -&gt;  Foreign Scan  (cost=10000.00..20000.00 rows=1000 width=126)
             Output: e.emp_no, e.emp_name, d.dept_name
             Oracle query: SELECT /*1f2567bfb61910168f19b658c678aa90*/ r1."EMP_NO", r1."EMP_NAME", r2."DEPT_NAME" FROM ("ORAUSER"."EMP" r1 INNER JOIN "ORAUSER"."DEPT" r2 ON (r1."DEPT_CD" = r2."DEPT_CD"))
             Oracle plan: SELECT STATEMENT
             Oracle plan:   HASH JOIN   (condition "R1"."DEPT_CD"="R2"."DEPT_CD")
             Oracle plan:     TABLE ACCESS FULL DEPT
             Oracle plan:     TABLE ACCESS FULL EMP
    (10 行)
</pre>
<p>結合はpush-downされますが、「order by」はPostgreSQLで実行されることがわかりました。</p>
<p>このように、条件を満たさない SQL の場合には、push-down は部分的にだけ行われます。<br />
少しでも条件に反したら全く push-downしないわけではありません。</p>
<h2>更新とトランザクション</h2>
<p>oracle_fdwは更新SQLにも対応しています。<br />
以下のように PostgreSQL から Oracle側のデータを変更できます。</p>
<pre>db1=&gt; INSERT INTO post VALUES ('007', 'アルバイト');
INSERT 0 1
db1=&gt; SELECT * FROM post ;
post_code | post_name
-----------+------------
001 | 社長
002 | 専務
003 | 部長
004 | 課長
005 | 係長
006 | 一般社員
007 | アルバイト
(7 rows)
</pre>
<p>トランザクションにも対応していて、PostgreSQLで更新内容をロールバックすると、Oracle側でもロールバックされます。以下に例を示します。</p>
<pre>db1=&gt; BEGIN;
BEGIN
db1=&gt; DELETE FROM post WHERE post_code = '007';
DELETE 1
db1=&gt; SELECT * FROM post ;
 post_code | post_name
-----------+-----------
 001       | 社長
 002       | 専務
 003       | 部長
 004       | 課長
 005       | 係長
 006       | 一般社員
(6 rows)

db1=&gt; ROLLBACK;
ROLLBACK


SQL&gt; SELECT * FROM post WHERE post_id = '007';

POST_ID
---------------------------------------------
POST_NAME
--------------------------------------------------------------------------------
007
アルバイト
</pre>
<h3>更新における注意点</h3>
<p>更新SQLを実行する場合には特に、PostgreSQLと Oracle のデータ型の振る舞いの違いについて注意が必要です。</p>
<p>POSTテーブルの POST_NAME列は VARCHAR(30) 型、PostgreSQL内の外部テーブルとしても varchar(30) 型です。そこに以下のように マルチバイト文字で20文字のデータ投入しますと、Oracle側でエラーが発生します。これは Oracleでは VARCHARの長さは文字数ではなくバイト数を基準としているからです。</p>
<pre>db1=&gt; INSERT INTO post VALUES ('999', '例外ケース／特別ケース／上記非該当ケース'
);
ERROR:  error executing query: OCIStmtExecute failed to execute remote query
DETAIL:  ORA-12899: value too large for column "ORAUSER"."POST"."POST_NAME" (actual: 40, maximum: 30)
</pre>
<p>そのほか、浮動小数点データ型や日付時刻データ型における端数の丸め方など、データ型の振る舞いの違いにより、値が両データベース間を行き来することで期待と違う値が見えたり、格納されてしまったりすることがあります。</p>
<p>PostgreSQLとOracleで対応させることができるデータ型であっても、必ずしも同一の振る舞いをするデータ型ではないことを認識するようにしてください。</p>
<h2>まとめ</h2>
<p>後編では oracle_fdw の外部テーブルが実際にどのように動作するかを見てきました。</p>
<p>検索処理のpush-down、更新SQL、トランザクション処理などに対応していることが確認できました。<br />
EXPLAIN を使って SQL処理のどの部分が Oracle側で実行されるのかを調べることで、応答データが期待に反していたり、性能が出ないといった問題に対処できました。<br />
また、両データベース製品の関数やデータ型における仕様の違いに注意が必要です。</p>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>oracle_fdw を使ってみる（前編）</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/using_oracle_fdw_1/</link>
		
		<dc:creator><![CDATA[高塚 遥 (Haruka Takatsuka)]]></dc:creator>
		<pubDate>Fri, 10 Aug 2018 04:00:19 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[FDW]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=303</guid>

					<description><![CDATA[更新日時: 2020年3月16日 PostgreSQLには、標準SQLの拡張である SQL/MED (&#8220;Management of External Data&#8221;)に沿った外部データアクセスの仕組み ...]]></description>
										<content:encoded><![CDATA[<h6><span id="timestamp">更新日時: <b>2020年3月16日</b></span></h6>
<p>PostgreSQLには、標準SQLの拡張である SQL/MED (&#8220;Management of External Data&#8221;)に沿った外部データアクセスの仕組みが備わっています。外部データの種類に応じた外部データラッパー（ Foreign Data Wrapper 、FDW）を適用することで、様々な外部データをPostgreSQL内のテーブルと同様に扱うことができます。</p>
<p>oracle_fdw は外部データラッパーの1つで Oracle Database に対応しています。oracle_fdw を使用することで、Oracle Database のテーブルやビューを PostgreSQL 上のSQLから読み書きできるようになります。</p>
<p>oracle_fdwはバージョン9.1以降のPostgreSQLおよびバージョン10.1以上のOracle Client（および Oracle Instant Client）で利用可能です。Oracle Database のサーバ側バージョンは、使用するOracle Clientが対応していれば大丈夫です。</p>
<p>本記事ではoracle_fdw の導入方法、使用方法を紹介します。続いて公開予定の後編の記事では、実際の使用にあたって気になる「こんなことをしたらどうなる？」「どの程度 PostgreSQL側で、どの程度Oracle側で処理されるの？」「データ型の違いはどうなるの？」といった様々な挙動の詳細に迫ります。</p>
<p><span id="more-303"></span></p>
<h2>oracle_fdw を導入する</h2>
<p>始めに導入方法を解説します。本記事作成にあたっては以下のバージョンで動作確認を行っています。</p>
<table>
<tbody>
<tr>
<td>PostgreSQL 10.4</td>
</tr>
<tr>
<td>oracle_fdw 2.1.0</td>
</tr>
<tr>
<td>Oracle Database 11g R2 XE</td>
</tr>
<tr>
<td>Oracle Instant Client 12.1</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<h3>Oracleデータベースの用意</h3>
<p>Oracle Database のデータベースは既にセットアップ済みであるものとします。本記事では Oracle Database 11g R2 XE バージョンを使用しています。</p>
<p>&nbsp;</p>
<h3>OCI ライブラリのインストール</h3>
<p>oracle_fdw は OCI（Oracle Call Interface）ライブラリを使って Oracleデータベースにアクセスします。そのため、PostgreSQL稼動サーバに OCI ライブラリのインストールが必要になります。本記事では Oracle Instant Client 12.1をインストールします。Oracle Instant Clientは <a href="http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html">OTN（Oracle Technology Network）Webサイト</a>で配布されています。</p>
<pre># rpm -ivh oracle-instantclient12.1-basic-12.1.0.2.0-1.x86_64.rpm 
           oracle-instantclient12.1-devel-12.1.0.2.0-1.x86_64.rpm
</pre>
<p>また、接続確認用にSQL*Plusモジュールもインストールしています。</p>
<pre># rpm -ivh oracle-instantclient12.1-sqlplus-12.1.0.2.0-1.x86_64.rpm
</pre>
<p>Oracle ClientとServerとの互換性については、以下のページを参考にして下さい。<br />
<a href="https://www.oracle.com/jp/system-requirement/interoperability-support-195844-ja.html"> https://www.oracle.com/jp/system-requirement/interoperability-support-195844-ja.html</a></p>
<h3>インストール</h3>
<p>oracle_fdwのページ（<a href="https://github.com/laurenz/oracle_fdw">https://github.com/laurenz/oracle_fdw</a>）からoracle_fdwのソースコードをZIP形式でダウンロードします。ここではリポジトリ上の最新コードをダウンロードすることにします。</p>
<p>ダウンロード後、解凍処理を行いソースコンパイルを実施します。このとき、PostgreSQLのコマンドにPATHが通っている必要があります。また、ソースコードを展開したディレクトリにあるMakefile 内に、Oracle Clientの標準的なパスが一通り列挙されています。導入したOracle Clientのパスがここに無ければ、既存の記述を真似て適宜に書き加えてから make を実行してください。</p>
<pre>$ unzip oracle_fdw-master.zip
$ cd oracle_fdw-master
$ make
$ make install
</pre>
<p>ソフトウェアのインストールが済んだら、PostgreSQLサービスを起動（既に起動しているなら再起動）します。このとき、PostgreSQLを起動するユーザにおいて Oracle Instant Client のライブラリにライブラリパスが通っている必要があります。</p>
<p>PostgreSQLをrpmからインストールしている場合には、以下のファイルが作られていますので、</p>
<pre>/etc/ld.so.conf.d/postgresql-pgdg-libs.conf
</pre>
<p>そこに以下のようにライブラリのパスを追加しておきます。ファイルを書き換えたら忘れずに rootユーザでldconfigコマンドを実行して変更を反映させましょう。</p>
<pre>/usr/pgsql-10/lib
/usr/lib/oracle/12.1/client64/lib</pre>
<p>手動でPostgreSQLを起動しているなら、以下のように LD_LIBRARY_PATH環境変数を使って指定しても良いでしょう。</p>
<pre>$ export LD_LIBRARY_PATH=/usr/lib/oracle/12.1/client64/lib
$ pg_ctl start
</pre>
<h3>データベースへの登録</h3>
<p>次に使用したいデータベース（ここでは db1）に対して、SQL の CREATE EXTENSIONコマンドを使ってoracle_fdw拡張モジュールをインストールします。ややこしいのですが、これも「インストールする」と表現します。psql の dx コマンドでインストール済みの拡張モジュールを確認できます。</p>
<pre>db1=# CREATE EXTENSION oracle_fdw;
CREATE EXTENSION
db1=# \dx oracle_fdw
インストール済みの拡張一覧
名前 | バージョン | スキーマ | 説明
------------+------------+----------+----------------------------------------
oracle_fdw | 1.1 | public | foreign data wrapper for Oracle access
(1 行)
</pre>
<p>このとき、PostgreSQL起動前に Oracle Clientライブラリの設定が正しく行われていないときには以下のエラーが発生します。</p>
<pre>db1=# CREATE EXTENSION oracle_fldw;
ERROR: could not load library "/usr/pgsql-10/lib/oracle_fdw.so": libclntsh.so.12.1: cannot open shared object file: No such file or directory
</pre>
<h2>Oracle外部テーブルを定義する</h2>
<p>それでは Oracleデータベース上に存在するテーブルをPostgreSQLの外部テーブルとして定義してみましょう。</p>
<p>Oracleデータベース上には以下のようにテーブルTBL_ORA_TEST1 があるものとします。</p>
<pre>SQL&gt; SELECT OWNER, TABLE_NAME FROM DBA_TABLES WHERE OWNER = 'ORAUSER';

OWNER TABLE_NAME
--------------- ------------------------------
ORAUSER TBL_ORA_TEST1

SQL&gt; DESC TBL_ORA_TEST1
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER(5)
ITEM VARCHAR2(100)
UPD_DATE TIMESTAMP(6)

SELECT * FROM TBL_ORA_TEST1;

ID ITEM UPD_DATE
---------- ------------------------------ ------------------------------
1 ORACLE DATA 1 18-07-18 14:19:29.402316
2 PostgreSQL INSERT DATA 1 18-07-18 14:56:15.870785
</pre>
<p>以下の(1) から(3) の手順で、OracleデータベースのテーブルをPostgreSQLの外部テーブルに定義します。</p>
<h3>(1) 外部サーバ作成</h3>
<p>Oracleデータベースにアクセスするための接続情報を外部サーバ(oradb112)として定義します。</p>
<pre>db1=# CREATE SERVER oradb112 FOREIGN DATA WRAPPER oracle_fdw
db1-# OPTIONS (dbserver '//192.168.156.102:1521/XE');
CREATE SERVER
db1=# \des
外部サーバー一覧
名前 | 所有者 | 外部データラッパ
----------+----------+------------------
oradb112 | postgres | oracle_fdw
(1 行)
</pre>
<p>テスト用の一般データベースユーザpguserが定義した外部サーバを使用できるように、権限を付与します。</p>
<pre>db1=# GRANT USAGE ON FOREIGN SERVER oradb112 TO pguser;
GRANT
</pre>
<h3>(2) ユーザマッピング定義</h3>
<p>外部サーバ（oradb112）とユーザ（pguser）の対応付けの定義を行います。PostgreSQL上で ユーザpguser が外部テーブルにアクセスしたとき、oradb112外部サーバに定義されたリモート先Oracleデータベースにアクセスするデータユーザはorauserである、ということを定義しています。</p>
<pre>db1=&gt; CREATE USER MAPPING FOR pguser SERVER oradb112 OPTIONS (USER 'orauser', PASSWORD 'orapass');
CREATE USER MAPPING
db1=&gt;
db1=&gt; deu
ユーザーマッピング一覧
サーバー | ユーザー名
----------+------------
oradb112 | pguser
(1 行)
</pre>
<h3>(3) 外部テーブル定義</h3>
<p>Oracleデータベース上のテーブル、ビューをPostgreSQLの外部テーブルとして定義します。ここでは Oracleデータベース上のテーブルTBL_ORA_TEST1 を外部テーブルftbl_ora_test1 として定義します。</p>
<pre>db1=&gt; CREATE FOREIGN TABLE ftbl_ora_test1
db1-&gt; (
db1(&gt; id int OPTIONS (key 'true') NOT NULL,
db1(&gt; item varchar(100),
db1(&gt; upd_date timestamp
db1(&gt; )
db1-&gt; SERVER oradb112 OPTIONS (SCHEMA 'ORAUSER', TABLE 'TBL_ORA_TEST1');
CREATE FOREIGN TABLE
</pre>
<p>id列に付加されている「OPTIONS (key &#8216;true&#8217;)」という記述は、この列が主キー列であることを示しています。複数の列から構成される主キーである場合には、それらの列全てにこのオプションを付加します。</p>
<p>作成した外部テーブルを確認します。</p>
<pre>db1=&gt; \det
外部テーブル一覧
スキーマ | テーブル | サーバー
----------+----------------+----------
public | ftbl_ora_test1 | oradb112
(1 行)
db1=&gt;
db1=&gt; \d ftbl_ora_test1
外部テーブル "public.ftbl_ora_test1"
列 | 型 | 照合順序 | Null 値を許容 | デフォルト | FDW オプション
----------+-----------------------------+----------+---------------+------------+----------------
id | integer | | not null | | (key 'true')
item | character varying(100) | | | |
upd_date | timestamp without time zone | | | |
サーバー: oradb112
FDW オプション: (schema 'ORAUSER', "table" 'TBL_ORA_TEST1')
</pre>
<p>oracle_fdwではクエリを外部テーブルとして定義することもできます。</p>
<p>以下の例では、Oracleデータベース上のテーブルに対するクエリを外部テーブルとして定義しています。<br />
クエリを外部テーブルとして定義する際には、OPTIONS句のSCHEMAを定義してはいけません。</p>
<pre>db1=&gt; CREATE FOREIGN TABLE ftbl_ora_test1_q
db1-&gt; (
db1(&gt; id int OPTIONS (key 'true') NOT NULL,
db1(&gt; item varchar(100),
db1(&gt; upd_date timestamp
db1(&gt; )
db1-&gt; SERVER oradb112 OPTIONS(TABLE '(SELECT id, item, upd_date FROM tbl_ora_test1 where item like ''%PostgreSQL%'')');
CREATE FOREIGN TABLE

db1=&gt; \det
外部テーブル一覧
スキーマ | テーブル | サーバー
----------+------------------+----------
public | ftbl_ora_test1_q | oradb112

db1=&gt; d ftbl_ora_test1_q
外部テーブル "public.ftbl_ora_test1_q"
列 | 型 | 照合順序 | Null 値を許容 | デフォルト | FDW オプション
----------+-----------------------------+----------+---------------+------------+----------------
id | integer | | not null | | (key 'true')
item | character varying(100) | | | |
upd_date | timestamp without time zone | | | |
サーバー: oradb112
FDW オプション: ("table" '(SELECT id, item, upd_date FROM tbl_ora_test1 where item like ''%PostgreSQL%'')')
</pre>
<h2>Oracle外部テーブルを使用する</h2>
<p>外部テーブルとして定義を行うことで、OracleのテーブルをPostgreSQLからSQLでアクセスできるようになりました。</p>
<h3>SELECTの実行</h3>
<p>上記４で定義を行った外部テーブルftbl_ora_test1、 ftbl_ora_test1_q に SELECT を実行します。</p>
<pre>db1=&gt; select * from ftbl_ora_test1;
id | item | upd_date
----+--------------------------+----------------------------
1 | ORACLE DATA 1 | 2018-07-18 14:19:29.402316
2 | PostgreSQL INSERT DATA 1 | 2018-07-18 14:56:15.870785
(2 行)

db1=&gt; select * from ftbl_ora_test1_q;
id | item | upd_date
----+--------------------------+----------------------------
2 | PostgreSQL INSERT DATA 1 | 2018-07-18 14:56:15.870785
(1 行)
</pre>
<p>無事データを取り出すことができました。</p>
<h3>INSERT/UPDATE/DELETEの実行</h3>
<p>PostgreSQL9.3以降であれば、oracle_fdw は外部テーブルに対して、INSERT/UPDATE/DELETEを実行することができます。<br />
ただし、UPDATE/DELETEについてはOracleのテーブルに設定された主キー列に対応する外部テーブルの列に&#8221;key&#8221;オプションを設定することが必要です。</p>
<p>&#8220;key&#8221; オプションが指定されている場合、以下のように動作します。</p>
<pre>db1=&gt; d ftbl_ora_test1
外部テーブル "public.ftbl_ora_test1"
列 | 型 | 照合順序 | Null 値を許容 | デフォルト | FDW オプション
----------+--------------------------------+----------+---------------+------------+----------------
id | integer | | not null | | (key 'true')
item | character varying(100) | | | |
upd_date | timestamp(6) without time zone | | | |
サーバー: oradb112
FDW オプション: (schema 'ORAUSER', "table" 'TBL_ORA_TEST1')

db1=&gt; UPDATE ftbl_ora_test1
db1-&gt; SET item = 'PostgreSQL UPDATE DATA2',
db1-&gt; upd_date = CURRENT_TIMESTAMP
db1-&gt; WHERE id = 3;
UPDATE 1
</pre>
<p>&#8220;key&#8221; オプションが指定されていない場合、以下のように更新操作に対してエラーが発生します。</p>
<pre>db1=&gt; d ftbl_ora_test1_ng
外部テーブル "public.ftbl_ora_test1_ng"
列 | 型 | 照合順序 | Null 値を許容 | デフォルト | FDW オプション
----------+-----------------------------+----------+---------------+------------+----------------
id | integer | | not null | |
item | character varying(100) | | | |
upd_date | timestamp without time zone | | | |
サーバー: oradb112
FDW オプション: (schema 'ORAUSER', "table" 'TBL_ORA_TEST1')

db1=&gt; UPDATE ftbl_ora_test1_ng
db1-&gt; SET item = 'PostgreSQL UPDATE DATA2',
db1-&gt; upd_date = CURRENT_TIMESTAMP
db1-&gt; WHERE id = 3;
ERROR: no primary key column specified for foreign Oracle table
DETAIL: For UPDATE or DELETE, at least one foreign table column must be marked as primary key column.
HINT: Set the option "key" on the columns that belong to the primary key.
</pre>
<h3>IMPORT FOREIGN SCHEMA</h3>
<p>oracle_fdw の機能を使用することで、OracleデータベースのテーブルやビューをPostgreSQLの外部テーブルとして定義しアクセスが可能であることを説明してきました。<br />
ここで、Oracleのデータベースリンク機能と対比して考えたとき、Oracleのデータベースリンクであればスキーマに対して１つの定義で<br />
対象スキーマが権限を保有するオブジェクトへのアクセスが可能となることに対して、PostgreSQLではアクセスを行いたいテーブル１つ１つを定義することに手間を感じることもあるかと思います。<br />
PostgreSQLが9.5以降であれば、「IMPORT FOREIGN SCHEMA」を使用することでOracleデータベースの特定スキーマに存在するテーブルやビューの定義をPostgreSQLの外部テーブルとして一括で定義することが可能となります。</p>
<p>Oracleデータベース上のテーブルリストが以下のようにであるとします。</p>
<pre>SQL&gt; SELECT owner, table_name FROM dba_tables WHERE OWNER = 'ORAUSER' ORDER BY table_name

OWNER TABLE_NAME
---------- ------------------------------
ORAUSER TBL_ORA_TEST1
ORAUSER TBL_ORA_TEST2
ORAUSER TBL_ORA_TEST3
ORAUSER TBL_ORA_TEST4
ORAUSER TBL_ORA_TEST5
</pre>
<p>上記のOracleデータベース上のスキーマ「ORAUSER」が所有するテーブルをIMPORT FOREIGN SCHEMAでまとめて外部テーブル定義をすることができます。</p>
<pre>db1=&gt; det
外部テーブル一覧
スキーマ | テーブル | サーバー
----------+----------+----------
(0 行)
db1=&gt;
db1=&gt; IMPORT FOREIGN SCHEMA "ORAUSER" FROM SERVER oradb112 INTO pguser;
IMPORT FOREIGN SCHEMA
db1=&gt;
db1=&gt; det
外部テーブル一覧
スキーマ | テーブル | サーバー
----------+---------------+----------
pguser | tbl_ora_test1 | oradb112
pguser | tbl_ora_test2 | oradb112
pguser | tbl_ora_test3 | oradb112
pguser | tbl_ora_test4 | oradb112
pguser | tbl_ora_test5 | oradb112
(5 行)
</pre>
<h3>識別子の大文字と小文字</h3>
<p>OracleデータベースとPostgreSQLではテーブル名やユーザ名などの識別子について、大文字小文字の扱いが異なります。クォーティングすることなく識別子名を書くと、Oracleデータベースでは大文字に揃えられ、PostgreSQLでは小文字に揃えられます。この違いにより以下の点に注意が必要となります。</p>
<ul>
<li>外部テーブル定義（CREATE FOREIGN TABLE）の際、OPTIONS句のスキーマ名およびユーザ名は Oracleのシステムカタログに定義されている情報（基本的には大文字）と同様に指定する必要があります。</li>
<li>IMPORT FOREIGN SCHEMAを実行する際に、Oracleデータベースのスキーマを指定しますが Oracleのシステムカタログに定義されている情報（基本的には大文字）と同様に指定する必要があります。</li>
<li>PostgreSQLでは、小文字に変換されてしまうため、Oracleデータベース上のスキーマ名を &#8220;（ダブルクォート）で括る必要があります。</li>
</ul>
<p>&nbsp;</p>
<h2>まとめ</h2>
<p>oracle_fdw は、Oracle上のテーブルやビューをPostgreSQLの外部テーブルにマッピングすることで PostgreSQLから直接SQLアクセスを実行可能とするツールです。これにより、PostgreSQL上のテーブルとOracle上のテーブルを結合させるSQLが実行可能となります。</p>
<p>PostgreSQLとOracleが共存している環境で Oracleのデータベースリンクのような機能をお探しであればデータへのアクセスのみとなりますが、代替機能として有用な手段となります。</p>
<p>本記事の後編ではoracle_fdw の動作・挙動の詳細を検証していきます。</p>
<p>&nbsp;</p>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
