ヘッダーをスキップ

Oracle9i Heterogeneous Connectivity管理者ガイド
リリース2(9.2)

部品番号B13816-01
Go To Table Of Contents
目次
Go To Index
索引

Go to previous page Go to next page

3
Oracle Transparent GatewayとGeneric Connectivityの機能

この章では、Oracle Transparent GatewayとGeneric Connectivityの主機能について説明します。この章の内容は、次のとおりです。

SQLとPL/SQLのサポート

SQL文の変換とデータ型のマッピングは、機能に従って実行されます。PL/SQLコールは、Oracle以外のシステムのストアド・プロシージャにマップされます。SQL文の場合、リモート・システムに機能がなければ、より単純な問合せが発行されるか、文が複数の問合せに分割され、必要な結果がOracleデータベースでの後処理により取得されます。

異機種間サービスでは、ほとんどの場合はOracle以外のシステムをOracle分散セッションに取り込むことができますが、これにはいくつかの制限があります。一般的な制限事項を次に示します。

  1. Oracle以外のリモート・システム上のオブジェクトを更新するDML文は、ローカルのOracleデータベース上のオブジェクトを参照できません。 たとえば、次のような文ではエラーが発生します。

    INSERT INTO remote_table@link as SELECT * FROM local_table;
    
    
  2. SQL文のCONNECT BY句に対するサポートはありません。

  3. ROWIDのサポートは限定的です。詳細は、個々のゲートウェイのマニュアルを参照してください。 Oracle9iゲートウェイでは、OracleのユニバーサルROWIDデータ型はサポートされません。

  4. LOB、ユーザー定義型およびREFはサポートされません。

  5. SQLにおけるPL/SQLはサポートされません。 たとえば、次のような文ではエラーが発生します。

    SELECT remote_func@link(a,b) FROM remote_table@link
    
    
  6. リモート・パッケージはサポートされません。

  7. リモート・ストアド・プロシージャでは、REF CURSOR型のout引数は指定できますが、inまたはin-outオブジェクトは使用できません。

  8. Oracle9iゲートウェイでは、共有データベース・リンクはサポートされません。


    注意:

    各ゲートウェイには、前述の一般的な制限事項に加えて、付加的な制限事項が存在する場合があります。製品の制限事項の全リストは、個々のゲートウェイのマニュアルを参照してください。


異機種間レプリケーション

Oracle以外のシステムとOracleサーバー間で、マテリアライズド・ビューを使用してデータをレプリケートできます。


注意:

Oracle9i リリース2(9.2)からは、他の方法によりデータベース間で情報を共有できます。 この機能はStreamsと呼ばれ、OracleデータベースとOracle以外のデータベース間での情報のレプリケーションが含まれます。

Streamsの使用方法の詳細は、『Oracle9i Streams』を参照してください。


マテリアライズド・ビューにより、Oracle以外のマスター・サイトにある表から特定の時点で取得されたデータがインスタンス化されます。この時点はリフレッシュ操作により定義されます。リフレッシュ操作により、このデータがOracleサーバーにコピーされ、Oracle上のコピーがOracle以外のシステム上のマスター・コピーと同期化されます。これにより、実体化されたデータがOracleサーバーでビューとして使用可能になります。

レプリケーション機能は、リフレッシュをスケジュールし、マテリアライズド・ビューを管理しやすいようにレプリケーション・グループに収集するためのメカニズムを提供します。リフレッシュ・グループにより、複数のマテリアライズド・ビューをシングル・オブジェクトと同様にリフレッシュできます。

異機種間レプリケーションのサポートは、必ずOracle間レプリケーション機能全体のサブセットに限定されます。

Oracleの異機種間サービス機能を介したOracle以外のデータへのアクセスには、その他の制限が適用されます。次に最も重要な制限事項を示します。

次の例に、Oracle以外のシステムからOracleデータ・ストアにデータをレプリケートする3つのマテリアライズド・ビューの基本設定と使用例を示します。


注意:

次の例で、remote_dbはOracleデータベース・サーバーからアクセスするOracle以外のシステムを指します。


例1: 異機種間レプリケーションに使用するマテリアライズド・ビューの作成

この例では、以降の例に使用する3つのマテリアライズド・ビューを作成します。

  1. customer@remote_db表の主キーのマテリアライズド・ビューを作成します。

    CREATE MATERIALIZED VIEW pk_mv REFRESH COMPLETE AS
      SELECT * FROM customer@remote_db WHERE "zip" = 94555;
    
    
  2. orders@remote_db表およびcustomer@remote_db表の副問合せのマテリアライズド・ビューを作成します。

    CREATE MATERIALIZED VIEW sq_mv REFRESH COMPLETE AS
      SELECT * FROM orders@remote_db o WHERE EXISTS
        (SELECT c."c_id" FROM customer@remote_db c
           WHERE c."zip" = 94555 and c."c_id"  = o."c_id" );
    
    
  3. remote_db上の複数の表からのデータの複合マテリアライズド・ビューを作成します。

    CREATE MATERIALIZED VIEW cx_mv
      REFRESH COMPLETE AS
      SELECT  c."c_id", o."o_id"
        FROM customer@remote_db c,
             orders@remote_db o,
             order_line@remote_db ol
        WHERE c."c_id" = o."c_id"
        AND o."o_id" = ol."o_id";
    

例2: 異機種間レプリケーションに使用するリフレッシュ・グループの設定

BEGIN
  dbms_refresh.make('refgroup1',
   'pk_mv, sq_mv, cx_mv',
   NULL, NULL);
 END;
 /

例3: 3つのマテリアライズド・ビューすべての強制リフレッシュ

BEGIN
   dbms_refresh.refresh('refgroup1');
END;
 /

関連項目:

マテリアライズド・ビューとレプリケーション機能の詳細は、『Oracle9i アドバンスト・レプリケーション』を参照してください。

パススルーSQL

パススルーSQL機能を使用すると、文をOracle9iサーバーで解析せずにOracle以外のシステムに直接送ることができます。この機能は、Oracle以外のシステムでOracleに等価文のない文での操作が見込まれる場合に役立ちます。

この項の内容は、次のとおりです。

DBMS_HS_PASSTHROUGHパッケージの使用

PL/SQLのDBMS_HS_PASSTHROUGHパッケージを使用すると、パススルーSQL文をOracle以外のシステムで直接実行できます。このパッケージで実行される文は、標準SQL文と同じトランザクションで実行されます。

DBMS_HS_PASSTHROUGHパッケージは仮想パッケージです。概念上は、Oracle以外のシステムに常駐します。ただし、実際には、このパッケージのコールは異機種間サービスにより仲介され、1つ以上の異機種間サービスApplication Program Interface(API)コールにマップされます。次に、これらの異機種間サービスAPIコールは、ドライバによりOracle以外のシステムのAPIにマップされます。クライアント・アプリケーションは、Oracle以外のシステムのストアド・プロシージャをコールする場合と同じ方法で、データベース・リンクを介してパッケージ内のプロシージャをコールする必要があります。異機種間サービスにより実行される特殊処理は、ユーザーに対して透過的です。

関連項目:

このパッケージの詳細は、『Oracle9i PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

パススルーSQLを使用する含意の考慮

Oracle以外のシステムでトランザクションを暗黙的にコミットまたはロールバックするパススルーSQL文を実行すると、そのトランザクションに影響します。たとえば、一部のシステムでは、データ定義言語(DDL)文を含むトランザクションが暗黙的にコミットされます。Oracleデータベース・サーバーは迂回されるため、Oracle以外のシステムでのコミットは認識されません。そのため、Oracleデータベース・サーバーではトランザクションがコミットされない間に、Oracle以外のシステムでデータがコミットされる可能性があります。

Oracleデータベース・サーバーでトランザクションがロールバックされると、Oracleデータベース・サーバーとOracle以外のサーバー間でデータに一貫性がなくなる可能性があります。このような状況では、グローバルなデータ非一貫性が発生します。

アプリケーションで通常のCOMMIT文を実行すれば、Oracleデータベース・サーバーはOracle以外のシステムと分散トランザクションを調整できることに注意してください。パススルー機能を使用して実行される文は、分散トランザクションの一部となります。

パススルーSQL文の実行

次の表に、DBMS_HS_PASSTHROUGHパッケージで提供されるファンクションとプロシージャを示します。これらを使用してパススルーSQL文を実行できます。

プロシージャ/ファンクション 説明

OPEN_CURSOR

カーソルのオープン

CLOSE_CURSOR

カーソルのクローズ

PARSE

文の解析

BIND_VARIABLE

IN変数のバインド

BIND_OUT_VARIABLE

OUT変数のバインド

BIND_INOUT_VARIABLE

IN OUT変数のバインド

EXECUTE_NON_QUERY

非問合せの実行

EXECUTE_IMMEDIATE

バインド変数を使用しない非問合せの実行

FETCH_ROW

問合せからの行のフェッチ

GET_VALUE

SELECT文からの列値の取得、またはOUTバインド・パラメータの取得

非問合せの実行

非問合せには、次の文およびタイプが含まれます。

非問合せ文を実行するには、EXECUTE_IMMEDIATEファンクションを使用します。 たとえば、データベース・リンクSalesDBを使用してアクセスできるOracle以外のシステムでDDL文を実行するには、次のように実行します。

DECLARE
  num_rows INTEGER;

BEGIN
  num_rows := DBMS_HS_PASSTHROUGH.EXECUTE_IMMEDIATE@SalesDB
            ('CREATE TABLE DEPT (n SMALLINT, loc CHARACTER(10))');
END;

num_rows変数には、実行の影響を受ける行数が割り当てられます。DDL文の場合は、0(ゼロ)が戻されます。EXECUTE_IMMEDIATEでは問合せを実行できず、バインド変数を使用できないことに注意してください。

バインド変数の使用: 概要

バインド変数を使用すると、同じSQL文にそれぞれ異なる値を指定して複数回使用でき、SQL文に必要な解析回数が減少します。たとえば、特定の表に4行挿入する必要がある場合は、SQL文を1回解析し、行ごとにバインドして実行できます。1つのSQL文に0個以上のバインド変数を使用できます。

バインド変数を使用してパススルーSQL文を実行する手順は、次のとおりです。

  1. カーソルをオープンします。

  2. Oracle以外のシステムでSQL文を解析します。

  3. 変数をバインドします。

  4. Oracle以外のシステムでSQL文を実行します。

  5. カーソルをクローズします。

図3-1に、バインド変数を使用した非問合せの実行のフロー図を示します。 

図3-1 非問合せのパススルーSQLのフロー図

INバインド変数の使用

文でバインド変数を指定する方法は、Oracle以外のシステムの構文により決定されます。 たとえば、Oracleでは、次のように先行コロンを使用してバインド変数を定義します。

UPDATE EMP
SET SAL=SAL*1.1
WHERE ENAME=:ename

この文で、enameはバインド変数です。 他のOracle以外のシステムでは、次のように、バインド変数の指定に疑問符が必要になる場合があります。

UPDATE EMP
SET SAL=SAL*1.1
WHERE ENAME= ?

バインド変数の手順では、ホスト・プログラム変数(この場合はPL/SQL)をこれらの各バインド変数と位置的に関連付ける必要があります。

たとえば、前述の文を実行するには、次のPL/SQLプログラムを使用できます。

DECLARE
  c INTEGER;
  nr INTEGER;
BEGIN
  c := DBMS_HS_PASSTHROUGH.OPEN_CURSOR@SalesDB;
  DBMS_HS_PASSTHROUGH.PARSE@SalesDB(c,
        'UPDATE EMP SET SAL=SAL*1.1 WHERE ENAME=?');
  DBMS_HS_PASSTHROUGH.BIND_VARIABLE(c,1,'JONES');
  nr:=DBMS_HS_PASSTHROUGH.EXECUTE_NON_QUERY@SalesDB(c);
  DBMS_OUTPUT.PUT_LINE(nr||' rows updated');
  DBMS_HS_PASSTHROUGH.CLOSE_CURSOR@salesDB(c);
END;
OUTバインド変数の使用

Oracle以外のシステムでOUTバインド変数もサポートできる場合があります。 OUTバインド変数を使用すると、その値はSQL文の実行後まで認識されません。

OUTバインド変数はSQL文の実行後に移入されますが、Oracle以外のシステムでは、特定のバインド変数がOUTバインド変数であることをSQL文の実行前に認識する必要があります。バインド変数がOUTバインド変数であることを指定するには、BIND_OUT_VARIABLEプロシージャを使用します。

SQL文の実行後は、GET_VALUEプロシージャを使用してOUTバインド変数の値を取得できます。

IN OUTバインド変数の使用

1つのバインド変数でIN変数とOUT変数を兼ねることができます。これは、バインド変数の値はSQL文の実行前に認識される必要があるが、SQL文の実行後に変更されてもかまわないことを意味します。

IN OUTバインド変数の場合は、BIND_INOUT_VARIABLEプロシージャを使用してSQL文の実行前に値を提供する必要があります。 SQL文の実行後は、GET_VALUEプロシージャを使用してバインド変数の新規の値を取得します。

問合せの実行

問合せでは、非問合せとは異なり、SELECT文から結果セットが取得されます。結果セットは、カーソルの反復により取得されます。

図3-2に、パススルーSQL問合せのステップを示します。 システムでSELECT文が解析された後、FETCH_ROWプロシージャで結果セットの各行をフェッチできます。行がフェッチされた後、GET_VALUEプロシージャを使用してSELECT構文のリスト項目をプログラム変数に取り出します。 すべての行がフェッチされた後に、カーソルをクローズできます。

図3-2 問合せのパススルーSQL

すべての行をフェッチする必要はありません。カーソルのオープン後は、数行をフェッチした後など、いつでもカーソルをクローズできます。


注意:

フェッチするのは1度に1行ですが、異機種間サービスでは、1回のラウンドトリップで複数行をバッファに入れてOracle以外のデータ・システムからフェッチすることで、Oracle9iサーバーとOracle以外のシステム間のラウンドトリップが最適化されます。


次の例では、問合せが実行されます。

DECLARE
   val  VARCHAR2(100);
   c    INTEGER;
   nr   INTEGER;
BEGIN
  c := DBMS_HS_PASSTHROUGH.OPEN_CURSOR@SalesDB;
  DBMS_HS_PASSTHROUGH.PARSE@SalesDB(c,
    'select ename
     from   emp
     where  deptno=10');
  LOOP
    nr := DBMS_HS_PASSTHROUGH.FETCH_ROW@SalesDB(c);
    EXIT WHEN nr = 0;
    DBMS_HS_PASSTHROUGH.GET_VALUE@SalesDB(c, 1, val);
    DBMS_OUTPUT.PUT_LINE(val);
  END LOOP;
  DBMS_HS_PASSTHROUGH.CLOSE_CURSOR@SalesDB(c);
END;

SELECT文の解析後は、FETCH_ROWファンクションが値0(ゼロ)を戻すまで、ループ内で行がフェッチされて出力されます。

関連項目:

付録C「パススルーSQLに対応したDBMS_HS_PASSTHROUGH」 

結果セットのサポート

各種リレーショナル・データベースでは、ストアド・プロシージャで結果セットを戻すことができます。つまり、ストアド・プロシージャで1つ以上の行セットを戻すことができます。 これはどのデータベースにおいても比較的新しい機能です。

従来、データベースのストアド・プロシージャの動作は、高水準プログラミング言語のプロシージャと同じでした。引数の数は一定で、inoutまたはin-out型を使用できました。プロシージャにn引数が指定されている場合は、結果として最大n個の値が戻されます。ただし、ストアド・プロシージャでSELECT * FROM empなどの問合せを実行して結果を戻す必要がある場合を考えます。emp表の列数は固定ですが、プロシージャの作成時には行数を指示する手段がありません。このため、従来のストアド・プロシージャを作成しても、このような問合せの結果を戻すことはできません。その結果、複数のリレーショナル・データベース・ベンダーがストアド・プロシージャから結果セットを戻す機能を追加していますが、戻す方法はリレーショナル・データベースごとに異なっています。

Oracleには、REF CURSORというデータ型が用意されています。 ストアド・プロシージャでは、他のすべてのOracleデータ型と同様に、このデータ型をinまたはout引数として使用できます。Oracleでは、ストアド・プロシージャは次の方法で結果セットを戻すことができます。結果セットを戻すには、ストアド・プロシージャにREF CURSOR型の出力引数が必要です。次に、SQL文のカーソルをオープンし、その出力パラメータのそのカーソルにハンドルを置きます。これにより、コール元は他のカーソルの場合と同じ方法でREF CURSORからフェッチできます。

Oracleでは、単に結果セットを戻す以上の操作を実行できます。REF CURSORをPL/SQLルーチンに入力引数として渡し、クライアント・プログラムとPL/SQLルーチン間、または複数のPL/SQLルーチン間でやりとりできます。 最近まで、OracleのREF CURSORは分散環境では動作しませんでした。 つまり、同じデータベース内のPL/SQLルーチン間、またはクライアント・プログラムとPL/SQLルーチン間でREF CURSORの値を渡すことはできますが、データベース間では渡すことができませんでした。 Oracle9iでは、異機種間サービスにおいてはこの制限が解除されています。

Oracle以外のシステムでの結果セットのサポート

Oracle以外の複数のシステムで、ストアド・プロシージャにより結果セットを戻すことができますが、その方法はまったく異なります。 他のリレーショナル・データベース管理システム(RDBMS)には、OracleのREF CURSORデータ型のようなものはありません。 DB2、Sybase、Microsoft SQL ServerおよびInformixでは、結果セットがある程度までサポートされています。これらのデータベースでの結果セットのサポートは、次の2つのモデルのどちらかに基づいています。

モデル1

ユーザーは、ストアド・プロシージャの作成時に、そのストアド・プロシージャが戻すことのできる結果セットの最大数を明示的に指定できます。実行時には、ストアド・プロシージャは0(ゼロ)から事前に指定された最大数の範囲内で結果セットをオープンできます。ストアド・プロシージャの実行後に、クライアント・プログラムは埋込みSQLディレクティブを使用するか、クライアントのライブラリ・ファンクションをコールして、これらの結果セットへのハンドルを取得できます。その後、クライアント・プログラムは通常のカーソルの場合と同じ方法で結果からフェッチできます。

モデル2

このモデルでは、ストアド・プロシージャが戻すことのできる結果セット数に事前指定の制限はありません。モデル1とOracleには制限があります。Oracleの場合、ストアド・プロシージャから戻される結果セットの数はREF CURSOR out引数の範囲内であり、モデル1の場合はストアド・プロシージャ言語によるディレクティブを使用して上限を指定されます。また、Oracleおよびモデル1とは異なり、モデル2では結果セットへのハンドルが戻されるかわりに、ストアド・プロシージャからの戻り時に結果セット全体が戻されます。Oracleの場合のハンドルはREF CURSOR out引数で、モデル1の場合はストアド・プロシージャの実行後に別個に取得されます。Oracleとモデル1の場合はどちらも、ハンドルが取得されると、結果セットからのデータがハンドルでのフェッチにより取得されます。一連のカーソルをオープンして任意の順序でフェッチできます。ただし、モデル2の場合は、すべてのデータがすでにフェッチされており、結果セットはストアド・プロシージャにより決定された順序で戻され、プロシージャの出力引数が最後になります。そのため、すべての結果がフェッチされるまで、最初の結果セット全体がフェッチされてから次の結果セット全体というように順番にフェッチされる必要があります。最後に、ストアド・プロシージャのout引数がフェッチされる必要があります。

異機種間サービスでの結果セットのサポート

前述のように、Oracle以外のデータベース間では、結果セットのサポートが様々な形式で存在します。これらはいずれも、OracleのREF CURSORモデルにマップする必要があります。Oracle以外の各種システム間では動作が大幅に異なるため、異機種間サービスでの結果セットのサポートは、接続先となるOracle以外のシステムに応じて2つの方法のどちらかで動作する必要があります。

異機種間サービスでの結果セットのサポートについては、次の注意事項があります。

カーソル・モード

通常、Oracleの動作では、Oracle以外のシステムのストアド・プロシージャから戻される各結果セットが、ドライバによりREF CURSOR型のout引数にマップされます。クライアント・プログラムには、REF CURSOR型の複数のout引数を持つストアド・プロシージャが表示されます。ストアド・プロシージャの実行後に、クライアント・プログラムはOracleストアド・プロシージャにより戻されるREF CURSORの場合と同じ方法でREF CURSORからフェッチできます。モデル1で説明したように、異機種間サービスはゲートウェイへの接続時にカーソル・モードになります。

逐次モード

Oracleでは、特定のストアド・プロシージャが戻すことのできる結果セットの最大数が事前に指定されます。戻される結果セットの数は、ストアド・プロシージャのREF CURSOR out引数の数以下です。戻される結果セットの数は、この上限より少なくてもかまいませんが、上限を超えることはできません。

モデル2で説明したシステムの場合、戻すことのできる結果セットの最大数が事前に指定されていません。モデル1の場合は、プロシージャが戻すことのできる結果セットの最大数がわかっており、ドライバはそれと同数のREF CURSOR out引数を持つストアド・プロシージャの記述を異機種間サービスに戻すことができます。ストアド・プロシージャの実行時に、戻される結果セットが最大数より少なければ、その他のREF CURSOR out引数がNULLに設定されます。

モデル2のデータベース・サーバーにはもう1つの問題があります。つまり、結果セットはデータベースにより戻された順序で取得する必要があります。このため、これらのデータベースへの接続時には、異機種間サービスをカーソル・モードで実行できません。これらのストアド・プロシージャから戻された結果セットにアクセスするには、異機種間サービスを逐次モードで操作する必要があります。

逐次モードでは、ドライバから戻されるプロシージャ記述に次の情報が含まれます。

クライアントは、このREF CURSORからフェッチし、仮想パッケージ・ファンクションdbms_hs_result_set.get_next_result_setをコールして、次の結果セットに対応するREF CURSORを取得します。このファンクション・コールは、すべての結果セットがフェッチされるまで繰り返されます。最後に戻される結果セットは、実際にはリモート・ストアド・プロシージャのout引数となります。

逐次モードの主な制限事項は、次のとおりです。

結果セットを使用したコード例

この項のすべての例において、次のOracle以外のシステムのストアド・プロシージャが使用されています。


注意:

説明のため、次の例はOracle PL/SQLストアド・プロシージャを想定しています。 ただし、DB2、Microsoft SQL Server、およびSybaseについても等価のストアド・プロシージャを作成できます。


create or replace package rcpackage is
  type rctype is ref cursor;
end rcpackage;
/

create or replace procedure refcurproc
  (arg1 in varchar2, arg2 out varchar2,
   rc1 out rcpackage.rctype,
   rc2 out rcpackage.rctype) is
begin
  arg2 := arg1;
  open rc1 for select * from emp;
  open rc2 for select * from dept;
end;
/

このストアド・プロシージャでは、入力パラメータarg1が出力パラメータarg2に割り当てられ、REF CURSOR rc1の問合せSELECT * FROM empがオープンされ、さらにREF CURSOR rc2の問合せSELECT * FROM deptがオープンされます。

例1: カーソル・モードで結果セットからフェッチするOCIプログラム

次の例に、カーソル・モードで結果セットからフェッチするOCIプログラムを示します。

  OCIEnv  *ENVH;
  OCISvcCtx *SVCH;
  OCIStmt *STMH;
  OCIError *ERRH;
  OCIBind *BNDH[4];
  OraText  arg1[20];
  OraText  arg2[20];
  OCIResult *arg3, *arg4;
  OCIStmt   *rstmt1, *rstmt2;
  ub2      rcode[4];
  ub2      rlens[4];
  sb2      inds[4];
  OraText *stmt = (OraText *) "begin refcurproc@link(:1,:2,:3,:4); end;";

  /* Handle Initialization code skipped */

  /* Prepare procedure call statement */

  OCIStmtPrepare(STMH, ERRH, stmt, strlen(stmt), OCI_NTV_SYNTAX,
                 OCI_DEFAULT);

  /* Bind procedure arguments */

  inds[0] = 0;
  strcpy((char *) arg1, "Hello World");
  rlens[0] = strlen(arg1);
  OCIBindByPos(STMH, &BNDH[0], ERRH, 1, (dvoid *) arg1, 20, SQLT_CHR,
               (dvoid *) &(inds[0]), &(rlens[0]), &(rcode[0]),
               0, (ub4 *) 0, OCI_DEFAULT);

  inds[1] = 0;
  rlens[1] = 0;
  OCIBindByPos(STMH, &BNDH[1], ERRH, 2, (dvoid *) arg2, 20, SQLT_CHR,
               (dvoid *) &(inds[1]), &(rlens[1]), &(rcode[1]),
               0, (ub4 *) 0, OCI_DEFAULT);

  inds[2] = 0;
  rlens[2] = 0;
  OCIDescriptorAlloc(ENVH, (dvoid **) &arg3, OCI_DTYPE_RSET, 0,
                     (dvoid **) 0);
  OCIBindByPos(STMH, &BNDH[2], ERRH, 3, (dvoid *) arg3, 0, SQLT_RSET,
               (dvoid *) &(inds[2]), &(rlens[2]), &(rcode[2]),
               0, (ub4 *) 0, OCI_DEFAULT);

  inds[3] = 0;
  rlens[3] = 0;
  OCIDescriptorAlloc(ENVH, (dvoid **) &arg4, OCI_DTYPE_RSET, 0,
                     (dvoid **) 0);
  OCIBindByPos(STMH, &BNDH[3], ERRH, 4, (dvoid *) arg4, 0, SQLT_RSET,
               (dvoid *) &(inds[3]), &(rlens[3]), &(rcode[3]),
               0, (ub4 *) 0, OCI_DEFAULT);

  /* Execute procedure */

  OCIStmtExecute(SVCH, STMH, ERRH, 1, 0, (CONST OCISnapshot *) 0,
                 (OCISnapshot *) 0, OCI_DEFAULT);

  /* Convert result set descriptors to statement handles */

  OCIResultSetToStmt(arg3, ERRH);
  OCIResultSetToStmt(arg4, ERRH);
  rstmt1 = (OCIStmt *) arg3;
  rstmt2 = (OCIStmt *) arg4;

  /* After this the user can fetch from rstmt1 and rstmt2 */

例2: 逐次モードで結果セットからフェッチするOCIプログラム

次の例に、逐次モードで結果セットからフェッチするOCIプログラムを示します。

  OCIEnv  *ENVH;
  OCISvcCtx *SVCH;
  OCIStmt *STMH;
  OCIError *ERRH;
  OCIBind *BNDH[2];
  OraText  arg1[20];
  OCIResult *rset;
  OCIStmt   *rstmt;
  ub2      rcode[2];
  ub2      rlens[2];
  sb2      inds[2];
  OraText *stmt = (OraText *) "begin refcurproc@link(:1,:2); end;";
  OraText *n_rs_stm = (OraText *)
    "begin :ret := DBMS_HS_RESULT_SET.GET_NEXT_RESULT_SET@link; end;";

  /* Prepare procedure call statement */

  /* Handle Initialization code skipped */

  OCIStmtPrepare(STMH, ERRH, stmt, strlen(stmt), OCI_NTV_SYNTAX,
                 OCI_DEFAULT);

  /* Bind procedure arguments */

  inds[0] = 0;
  strcpy((char *) arg1, "Hello World");
  rlens[0] = strlen(arg1);
  OCIBindByPos(STMH, &BNDH[0], ERRH, 1, (dvoid *) arg1, 20, SQLT_CHR,
               (dvoid *) &(inds[0]), &(rlens[0]), &(rcode[0]),
               0, (ub4 *) 0, OCI_DEFAULT);

  inds[1] = 0;
  rlens[1] = 0;
  OCIDescriptorAlloc(ENVH, (dvoid **) &rset, OCI_DTYPE_RSET, 0,
                     (dvoid **) 0);
  OCIBindByPos(STMH, &BNDH[1], ERRH, 2, (dvoid *) rset, 0, SQLT_RSET,
               (dvoid *) &(inds[1]), &(rlens[1]), &(rcode[1]),
               0, (ub4 *) 0, OCI_DEFAULT);

  /* Execute procedure */

  OCIStmtExecute(SVCH, STMH, ERRH, 1, 0, (CONST OCISnapshot *) 0,
                 (OCISnapshot *) 0, OCI_DEFAULT);

  /* Convert result set to statement handle */

  OCIResultSetToStmt(rset, ERRH);
  rstmt = (OCIStmt *) rset;

  /* After this the user can fetch from rstmt */

  /* Issue get_next_result_set call to get handle to next_result set */

  /* Prepare Get next result set procedure call */

  OCIStmtPrepare(STMH, ERRH, n_rs_stm, strlen(n_rs_stm), OCI_NTV_SYNTAX,
                 OCI_DEFAULT);

  /* Bind return value */

  OCIBindByPos(STMH, &BNDH[1], ERRH, 1, (dvoid *) rset, 0, SQLT_RSET,
               (dvoid *) &(inds[1]), &(rlens[1]), &(rcode[1]),
               0, (ub4 *) 0, OCI_DEFAULT);

  /* Execute statement to get next result set*/

  OCIStmtExecute(SVCH, STMH, ERRH, 1, 0, (CONST OCISnapshot *) 0,
                 (OCISnapshot *) 0, OCI_DEFAULT);

  /* Convert next result set to statement handle */

  OCIResultSetToStmt(rset, ERRH);
  rstmt = (OCIStmt *) rset;

  /* Now rstmt will point to the second result set returned by the
     remote stored procedure */

  /* Repeat execution of get_next_result_set to get the output
     arguments */

例3: カーソル・モードで結果セットからフェッチするPL/SQLプログラム

loc_empは、リモート表empと同じローカル表であるとします。 loc_deptでも同じことを前提としています。

  declare
    rc1   rcpackage.rctype;
    rec1  loc_emp%rowtype;
    rc2   rcpackage.rctype;
    rec2  loc_dept%rowtype;
    arg2  varchar2(20);

  begin

    -- Execute procedure

    refcurproc@link('Hello World', arg2, rc1, rc2);

    -- Fetch 20 rows from the remote emp table and insert them
    -- into loc_emp

    for i in 1 .. 20 loop
      fetch rc1 into rec1;
      insert into loc_emp (rec1.empno, rec1.ename, rec1.job,
                           rec1.mgr, rec1.hiredate, rec1.sal,
                           rec1.comm, rec1.deptno);
    end loop;

    -- Close the ref cursor

    close rc1;

    -- Fetch 5 rows from the remote dept table and insert them
    -- into loc_dept

    for i in 1 .. 5 loop
      fetch rc2 into rec2;
      insert into loc_dept values (rec2.deptno, rec2.dname, rec2.loc);
    end loop;

    -- Close the ref cursor

    close rc2;

  end;

逐次モードで結果セットからフェッチするPL/SQLプログラム

loc_empおよびloc_deptは前述と同じです。 表outargumentsには、リモート・ストアド・プロシージャのout引数に対応する列が含まれます。

  declare
    rc1   rcpackage.rctype;
    rec1  loc_emp%rowtype;
    rc2   rcpackage.rctype;
    rec2  loc_dept%rowtype;
    rc3   rcpackage.rctype;
    rec3  outargs%rowtype;

  begin

    -- Execute procedure

    refcurproc@link('Hello World', rc1);

    -- Fetch 20 rows from the remote emp table and insert them
    -- into loc_emp

    for i in 1 .. 20 loop
      fetch rc1 into rec1;
      insert into loc_emp (rec1.empno, rec1.ename, rec1.job,
                           rec1.mgr, rec1.hiredate, rec1.sal,
                           rec1.comm, rec1.deptno);
    end loop;

    -- Close ref cursor

    close rc1;

    -- Get the next result set returned by the stored procedure

    rc2 := dbms_hs_result_set.get_next_result_set@link;

    -- Fetch 5 rows from the remote dept table and insert them
    -- into loc_dept

    for i in 1 .. 5 loop
      fetch rc2 into rec2;
      insert into loc_dept values (rec2.deptno, rec2.dname, rec2.loc);
    end loop;

    --Close ref cursor

    close rc2;

    -- Get the output arguments from the remote stored procedure
    -- Since we are in sequential mode, they will be returned in the
    -- form of a result set

    rc3 := dbms_hs_result_set.get_next_result_set@link;

    --Fetch them and insert them into the outarguments table

    fetch rc3 into rec3;
    insert into outarguments (rec3.col);

--Close ref cursor

    close rc3;

  end;

データ・ディクショナリ変換

ほとんどのデータベース・システムには、なんらかの形式のデータ・ディクショナリがあります。データ・ディクショナリは、システムの各種ユーザーにより作成されたデータベース・オブジェクトに関する情報のコレクションです。リレーショナル・データベースの場合、データ・ディクショナリはデータベース内のデータに関する情報を含む表およびビューのセットです。これには、システムを使用中のユーザーや、そのユーザーが作成したオブジェクト(表、ビュー、トリガーなど)に関する情報が含まれます。ほとんどの場合、すべてのデータ・ディクショナリには(データベース・システムに関係なく)同じ情報が含まれていますが、情報の編成方法はデータベース・システムごとに異なります。

たとえば、Oracleのデータ・ディクショナリ・ビューALL_CATLOGには、データベース内の表、ビューおよび順序のリストが表示されます。これには3つの列があり、1列目はオブジェクトの所有者名を示すOWNER、2列目はオブジェクト名を示すTABLE_NAME、3列目はタイプを示すTABLE_TYPEです。このフィールドの値は、オブジェクト型に応じてTABLEVIEWSEQUENCEなどです。 ただし、Sybaseの場合、同じ情報がsysusersおよびsysobjectsという2つの表に格納され、その列名がOracleのALL_CATALOG表とは異なります。また、Oracleでは、表のタイプはTABLEVIEWなどの値を含む文字列ですが、Sybaseでは1文字です。たとえば、Sybaseでは、Uはユーザー表、Sはシステム表、Vはビューを意味します。

クライアント・プログラムがSybaseにあるALL_CATALOG表からの情報を必要とする場合は、ALL_CATALOG@database_linkを参照する問合せをゲートウェイに送信するだけですみます。この問合せは、異機種間サービスによりsystablesの該当する問合せに変換されてから、Sybaseに送信されます。

SELECT SU."name" OWNER, SO."name" TABLE_NAME,
   DECODE(SO."type", 'U ','TABLE', 'S ', 'TABLE', 'V ', 'VIEW')
TABLE_TYPE
FROM "dbo"."sysusers"@link  SU, "dbo"."sysobjects"@link SO
WHERE SU."uid" = SO."uid" AND
   (SO."type" = 'V' OR SO."type" = 'S' OR SO."type" = 'U')>

このように、Oracleデータ・ディクショナリ表に対する問合せからOracle以外のシステムのデータ・ディクショナリ表に対する等価の問合せへの変換に依存するために、異機種間サービスはそのシステム用のデータ・ディクショナリ変換を必要とします。データ・ディクショナリ変換は、Oracle以外の1つ以上のシステムのデータ・ディクショナリ表に対するビュー定義(実際にはSELECT文)です。ビューには、これらのデータ・ディクショナリ表が、Oracleデータ・ディクショナリ表と同じ列名および同じ情報形式を使用して表示されます。 データ・ディクショナリ変換は、前述の例のように単純でなくてもかまいません。通常、必要な情報が1つまたは2つの表ではみつからず、多数の表にまたがって散在しており、データ・ディクショナリ変換はこれらの表の複雑な結合となっています。

また、必要な情報がOracle以外のシステムに存在しないため、Oracleデータ・ディクショナリ表に変換がない場合があります。このような場合、ゲートウェイは変換をまったくアップロードしないように決定するか、または疑似実行という代替アプローチに依存できます。ゲートウェイがデータ・ディクショナリ表を疑似実行する必要がある場合は、それを異機種間サービスに認識させます。異機種間サービスは、ローカル・データベースを問い合せてデータ・ディクショナリ表の記述を取得しますが、データをフェッチするように要求されると行が選択されていないことをレポートします。

データ・ディクショナリ問合せの例

次の例では、Informixに送信されるデータ・ディクショナリ問合せの出力を示し、Oracleで同じビューを問い合せたときに生成される結果と比較しています。


注意:

次の例では、Oracle以外のシステムとしてInformixを使用しています。


例1: OracleおよびInformixにおける現行セッションのユーザー名の確認

OracleおよびInformixで現行セッションのユーザー名を確認するには、次のように入力します。

SQL> SELECT a.USERNAME, b.USERNAME FROM USER_USERS a, USER_USERS@remote_db b;

 USERNAME                       USERNAME
 ------------------------------ ------------------------------
 THSU                           thsu


注意:

ユーザー名は、Oracleでは大文字で保持され、Informixでは小文字で保持されます。


例2: OracleおよびInformixにおける現行セッションのユーザーIDの確認

OracleおよびInformixで現行セッションのユーザーIDを確認するには、次のように入力します。

SQL> SELECT a.USER_ID, b.USER_ID FROM USER_USERS a, USER_USERS@remote_db b;

    USER_ID    USER_ID
 ---------- ----------
         25          0


注意:

Informixでは数値のUSER_ID値は保持されないため、InformixのユーザーIDはデフォルトで0(ゼロ)に設定されます。 このため、Oracleサーバー上の他の情報にアクセスする場合にも注意が必要です。 接続先のOracle以外のシステムからUSER_IDに相当する値が戻されたとしても、この値はそのOracle以外のシステムでのみ適用されるものであり、OracleサーバーではUSER_IDとしての意味を持ちません。 Oracle以外のシステムのUSER_IDをキーとしてOracleデータ・ディクショナリに他の問合せを実行しても、正しい結果は得られません。


例3: Oracle以外のシステムで定義された任意のユーザーが所有する表の制約の確認

Oracle以外のシステムで定義された、任意のユーザーが所有する表の制約を確認するには、次のように入力します。

SQL SELECT CONSTRAINT_NAME, TABLE_NAME FROM ALL_CONSTRAINTS@remote_db
  2  WHERE OWNER = 'thsu';

 CONSTRAINT_NAME                TABLE_NAME
 ------------------------------ ------------------------------
 u19942_5270                    thsmv_order_line
 u24612_7116                    thsmv_customer
 u24613_7117                    thsmv_orders


注意:

InformixではOracleと異なる形式の制約名が使用され、Informixのデータ・ディクショナリでは、表名は大文字ではなく小文字で保持されます。


関連項目:

データ・ディクショナリ変換の詳細は、付録D「データ・ディクショナリ変換のサポート」を参照してください。 

日時データ型

Oracleには、次の5つの日時データ型があります。

異機種間サービスの汎用コードでは、SQLおよびストアド・プロシージャでのOracle日時データ型の使用がサポートされます。 Oracleではこれらのデータ型は、データ・ディクショナリ変換、またはデータ・ディクショナリ変換が関与する問合せではサポートされません。

異機種間サービスの汎用コードでOracle日時データ型がサポートされていても、ゲートウェイでサポートされるかどうかは、そのOracle以外のシステムのドライバで日時のサポートが実装されているかどうかによって異なります。また、ドライバに実装されていても、Oracle以外のシステムでの制限によってサポートが部分的である場合があります。これについては、該当するゲートウェイのマニュアルを参照してください。

ユーザーは、Oracle以外のシステムのタイムスタンプ書式をゲートウェイ初期化ファイル内で設定する必要があります。設定するパラメータは、HS_NLS_TIMESTAMP_FORMATおよびHS_NLS_TIMESTAMP_TZ_FORMATです。 また、ユーザーは初期化ファイル内でOracle以外のシステムのローカル・タイム・ゾーンも設定する必要があります。 設定するパラメータはHS_TIME_ZONEです。

関連項目:

日時データ型の詳細は、『Oracle9i SQLリファレンス』を参照してください。

2フェーズ・コミット・プロトコル

異機種間サービスは、2フェーズ・コミット・メカニズムの実装のインフラストラクチャを提供します。そのサポート範囲はゲートウェイおよびリモート・システムに応じて異なります。詳細は、各ゲートウェイのマニュアルを参照してください。

関連項目:

2フェーズ・コミット・プロトコルの詳細は、『Oracle9i データベース管理者ガイド』を参照してください。

ピース単位LONG

以前のバージョンのゲートウェイでは、LONGデータ型のサポートが限定されていました。 LONGは、最大2GBの文字またはRAWデータ(LONG RAW)の格納に使用できるOracleデータ型です。これらの旧バージョンでは、LONGデータのサイズが4MBに制限されていました。これは、LONGデータが1ピースとして扱われるためでした。このため、処理できるデータのサイズにメモリーとネットワーク帯域幅という制限が課されていました。現行のゲートウェイの機能は、異機種間で2GBのLONGデータ全体をサポートするように拡張されています。データはエージェントとOracleサーバー間でピース単位で処理されるため、大きいメモリーおよびネットワーク帯域幅という要件から解放されます。

新しい異機種間サービス初期化パラメータHS_LONG_PIECE_TRANSFER_SIZEを使用すると、転送されるピースのサイズを設定できます。たとえば、異機種間ソースから2GBのLONGデータをフェッチする場合を考えます。ピース・サイズが小さくなるほどメモリー所要量が減少しますが、すべてのデータをフェッチするためのラウンドトリップは増加します。ピース・サイズが大きいとラウンドトリップは減少しますが、中間ピースを内部に格納するためのメモリー所要量が増加します。そのため、初期化パラメータを使用して最適のパフォーマンス(つまり、ラウンドトリップ回数とメモリー所要量との最適なトレードオフ)が得られるようにシステムをチューニングできます。この初期化パラメータを設定しなければ、システムではデフォルトでピース・サイズが64KBに設定されます。


注意:

この機能を、クライアント側のLONGデータに対するピース単位操作と混同しないでください。クライアント側でのピース単位のFETCHおよびINSERT操作は、旧バージョンのゲートウェイで動作しており、今後も動作します。クライアント側での唯一の違いは、旧バージョンのゲートウェイでフェッチできるLONGデータは最大4MBのみだったのに対して、このバージョンでは2GBのLONGデータ全体をフェッチできることです。 これは、4MBがデータ型の全容量のわずか0.2%にすぎないことを考慮すると、かなりの改善点といえます。


SQL*PlusのDESCRIBEコマンド

Oracle9iより前では、SQL*PlusのDESCRIBEコマンドを使用してOracle以外のシステムのオブジェクトを記述できませんでした。Oracle9iでは、そのための機能が異機種間サービスに追加されています。ただし、このリリースにも制限事項があります。 たとえば、パッケージ、順序、シノニムまたは型の記述には、異機種間リンクを使用できません。

SQL*PlusのDESCRIBEコマンドはOCIDescribeAnyコールを使用して実装されます。これもOracle9iより前では使用できませんでした。 OCIDescribeAnyコールでは、データベースおよびスキーマの記述も可能です。SQL*PlusのDESCRIBEコマンドでは、これらの記述はできません。 異機種間サービスでは、どちらも実行できます。

この機能を実装するには、追加のドライバ・ロジックが必要です。ドライバによってはこの機能を実装していないものがあります。 ゲートウェイでこの機能がサポートされているかどうかについては、各ゲートウェイのマニュアルを参照してください。

分散環境におけるSQLの制約

この項では、分散環境でSQLに存在する制約について説明します。これらの制約は、Oracle以外のシステムまたはリモートOracleデータベースへのアクセスなど分散環境に適用されます。

この項の内容は、次のとおりです。

リモート参照と異機種間参照の解決


注意:

異機種間アクセスに関するルールの多くは、リモート参照にも適用されます。 詳細は、『Oracle9i データベース管理者ガイド』の分散データベースの項を参照してください。


文は、制限付きではありますが、その文で参照されているデータベース・ノードまたはローカル・ノード上で実行できます。 参照されるオブジェクトすべてが1つの参照先ノードに解決される場合、Oracleはそのノードで問合せの実行を試みます。/*+ REMOTE_MAPPED */または/*+ DRIVING_SITE */ヒントを使用すると、参照先ノードで強制的に実行できます。 文が発行時とは異なるノードに転送される場合、その文をリモート・マップ文と呼びます。

文のリモート・マップを可能、必須および禁止にする方法には、特定のルールまたは制限が適用されます。これらのルールすべてに従わなければ、エラーが発生します。発行される文にこれらのルールすべてとの一貫性があるかぎり、ルールの適用順序は関係ありません。

分散環境でのリモート・マッピングにSQLを使用する場合は、異なる制約が存在します。 この分散環境には、OracleシステムとOracle以外のシステムの間のOracle Transparent Gateway接続またはGeneric Connectivity接続を必要とするデータベースのみでなく、リモートのOracleデータベースも含めることができます。

重要な制限の解決

次の項では、分散環境でリモート・マッピングにSQLを使用する場合に存在する様々な制約について説明します。


注意:

以降の例では、remote_dbはOracle以外のリモート・システム、remote_oracle_dbはリモートのOracleサーバーを指します。


ルールA: データ定義言語文はリモート・マップできません。

Oracleのデータ定義言語では、ターゲット・オブジェクトの構文にはリモート参照の位置がありません。リモート参照を含むデータ定義言語文は常にローカルに実行されます。異機種間サービスの場合、これは、SQLを使用してOracle以外のデータベースにデータベース・オブジェクトを直接作成できないことを意味します。

ただし、パススルーSQLを使用する間接的な方法があります。

次の例を考えてみます。

BEGIN
  DBMS_HS.PASSTHROUGHSQL.EXECUTE_IMMEDIATE@remote_db
  (
     'create table x1 (c1 char, c2 number)'
  );
END;

ルールB: リモート・ターゲット表を使用するINSERT、UPDATEおよびDELETE文はリモート・マップする必要があります。

このルールは、リモートのOracleデータベースよりもOracle以外のリモート・データベースの場合に限定的です。これは、Oracle以外のシステムの表に対するDML文の実行中は、リモート・システムで元のOracleデータベースからデータをフェッチできないためです。

たとえば、ローカルのemp1表からローカルの全従業員をOracleのリモートのemp2表に挿入するには、次の文を使用します。

INSERT INTO emp2@remote_oracle_db SELECT * FROM emp1;

この文は、リモート・データベースにマップされます。 リモート・データベースに送信されるリモート・マップ文には、emp1に関する元のデータベースへのリモート参照が含まれます。このようにリモート・データベースが受信するリモート・リンクを、コールバック・リンクと呼びます。

ただし、通常はゲートウェイのコールバック・リンクはサポートされません。 ローカル表を参照するSELECT文を使用してOracle以外のシステムに挿入を試みると、エラーが発生します。

たとえば、次の文を考えてみます。

INSERT INTO emp2@remote_db SELECT * FROM emp1;

この文は次のエラー・メッセージを戻します。

ORA-02025: SQL文の中の表はすべてリモート・データベースにある必要があります

このエラーを回避するには、PL/SQLブロックを記述します。

DECLARE
CURSOR remote_insert IS SELECT * FROM emp2;
BEGIN

   FOR rec IN remote_insert LOOP
    INSERT INTO emp1@remote_db (empno, ename, deptno) VALUES (
      rec.empno,
      rec.ename,
      rec.deptno
    );
   END loop;
END;
/

もう1つの特殊事例には、USERUSERENVおよびSYSDATEなど、セッション固有のSQL関数があります。これらの関数は、元のサイトで実行することが必要な場合があります。これらの関数を含むリモート・マップ文には、コールバック・リンクが含まれます。コールバックがサポートされていないOracle以外のデータベースの場合は、制限エラーに(デフォルトで)なる場合があります。

たとえば、次の文を考えてみます。

DELETE FROM emp1@remote_db WHERE hiredate > sysdate;

この文は次のエラー・メッセージを戻します。

ORA-02070: データベースREMOTE_DBはこのコンテキストでは特殊関数をサポートしません。

通常、このエラーを解決するには、次のように特殊関数をバインド変数で置き換える必要があります。

DELETE FROM emp1@remote_db WHERE hiredate > :1;

ルールC: NESTED TABLE型の列、ユーザー定義型の列、不透明型の列またはREF型の列を含む表などのオブジェクト機能は、リモート・マップできません。

現在、異機種間アクセスの場合、前述の列型はサポートされていません。したがって、この制限が直接発生することはありません。

ルールD: リモート・サイトでサポートされていない演算子および構成メンバーを含むSQL文は、リモート・マップできません。

この例のようにコールバック・リンクや特殊関数などの特殊な構成メンバーについては、ルールBの説明ですでに言及していることに注意してください。

文がselect(またはターゲット表がローカルのdml)で、残りのどのルールでも文のリモート・マップが要求されていない場合は、ローカルのSQLエンジンとリモートのselect操作を使用して問合せをローカルに処理し、文を実行できます。

リモートのselect操作とは、ローカル表データの行を取得する全表スキャンや索引アクセスなどの他の操作とは異なり、リモート表データの行を取得する操作です。リモート表スキャンの操作には、SQL文が関連付けられています。emp1表の全表スキャンは、SELECT * FROM emp1として発行されます(*がすべての列のリストに拡張されます)。 索引へのアクセスは、WHERE句の条件に変換されます。また、サポートできるフィルタは、リモート行ソースのWHERE句に渡されます。

Oracleサーバーにより生成されたSQL文をチェックするには、その文を記述し、REMOTE操作ごとにEXPLAIN PLAN表のOTHER列を問い合せます。

関連項目:

リモート参照を含むEXPLAIN PLANを解析する方法の詳細は、「索引統計と表統計の使用例」を参照してください。

たとえば、次の文を考えてみます。

SELECT COUNT(*) FROM emp1@remote_db WHERE hiredate < sysdate;

この文では次の出力が戻されます。

COUNT(*)
----------
        14
1 row selected.

リモート表スキャンは次のとおりです。

SELECT hiredate FROM emp1

sysdateはremote_dbまたは評価ルールでサポートされていないため、フィルタに変換される条件を生成してリモート操作に渡すことはできません。そのため、sysdateはローカルに実行する必要があります。


注意:

リモート表スキャン操作は元の問合せについては一部のみ関連するため、受信行数が予想より大幅に多くなり、パフォーマンスが大幅に低下する場合があります。


ルールE: 表の式を含むSQL文はリモート・マップできません。

異機種間アクセス・モジュールでは表の式がサポートされていないため、この制限が直接発生することはありません。

ルールF: SQL文でLONGが選択される場合は、その文をLONGを含む表が常駐するノードにマップする必要があります。

たとえば、次の文を考えてみます。

SELECT long1 FROM table_with_long@remote_db, dual;

この文は次のエラー・メッセージを戻します。

ORA-02025: SQL文の中の表はすべてリモート・データベースにある必要があります

このエラーは、次の文で解決できます。

SELECT long1 FROM table_with_long@remote_db WHERE long_idx = 1;

ルールG: SQL文がSELECT...FOR UPDATE OF...形式の場合、その文はFOR UPDATE OF句で参照される列を持つ表が常駐するノードにマップする必要があります。

SQL文がSELECT...FOR UPDATE OF...形式の場合、その文はFOR UPDATE OF句で参照される列を持つ表が常駐するノードにマップする必要があります。

たとえば、次の文を考えてみます。

SELECT ename FROM emp1@remote_db WHERE hiredate < sysdate FOR UPDATE OF empno

この文は次のエラー・メッセージを戻します。

ORA-02070: データベースREMOTE_DBはこのコンテキストでは特殊関数をサポートしません。

ルールH: SQL文にSEQUENCEまたは順序が含まれている場合は、その文を各順序が常駐するサイトにマップする必要があります。

Oracle以外のリモートの順序はサポートされないため、このルールは異機種間アクセスには発生しません。コールバック・リンクの制限により、Oracle以外のリモート・アクセスに関する制限はすでに存在します。

ルールI: 文に1つ以上のユーザー定義演算子が含まれている場合は、その文を各演算子が定義されているノードにマップする必要があります。

このルールも、ルールBで説明したコールバック・リンクの制限にすでに含まれています。

ルールJ: 重複するバインド変数を含む文はリモート・マップできません。

この制限を回避するには、一意のバインド変数を使用して数値でバインドします。

UPDATE、INSERTおよびDELETE

前の項で述べたように、OracleサーバーによるOracle以外のリモート・オブジェクトに対する更新は、Oracleデータベースに存在するコールバック機能サポートの欠落によって制限されます。 これによって、Oracle以外のリモート・データベース・オブジェクトにおけるデータ操作言語(DML)は、そのデータベース内のすべてのオブジェクトを参照する文、またはリテラルやバインド変数である文に制限されます。

このため、元のOracleサーバーまたはその他のリモート・オブジェクトからはオブジェクトを参照できません。

また、リモートUPDATEの場合と同様に、Oracle以外であるか前述のリモートUPDATEであるかに関係なく、Oracle形式のSQL UPDATEがサポートされていない場合は、次の書式でエラーが戻されます。

ORA-02070: データベース ... はこのコンテキストでは ... をサポートしません。


注意:

前述の制限は、Oracle以外のデータベース・オブジェクトまたはリモートOracleデータベース・オブジェクトを参照するローカルのターゲット・オブジェクトを含むDMLには適用されません。


PL/SQLを使用すると、サポートされていないOracle形式でリモートOracleまたはOracle以外のターゲット表に対してDMLを実行できます。該当する行を選択して選択した行ごとに更新を実行するカーソルを宣言します。行が一意であることが必要な場合があります。各行は、主キーを選択するか、主キーが使用できない場合はROWIDを選択することで識別されます。

次の例を考えてみます。

DECLARE
  CURSOR c1 IS SELECT empno FROM emp e, dept d
               WHERE  e.deptno = d.deptno
               AND    d.dname = 'SALES';
BEGIN
  FOR REC IN c1 LOOP
    UPDATE emp1@remote_db SET comm = .1 * sal;
    WHERE empno = rec.empno;
  END loop;
END;
/

最適化

Oracleのオプティマイザを異機種間サービスと併用できます。異機種間サービスは、Oracle以外のシステムのそれぞれの表について特定の表および索引統計情報を収集し、この情報をOracleサーバーに渡します。Oracleのコストベース・オプティマイザは、この情報を問合せ計画の作成時に使用します。

その他にも、コストベース・オプティマイザでは複数の最適化が実行されます。最も重要な最適化は、リモート・ソートの排除とリモート結合です。

索引統計と表統計の使用例

Oracleデータベースに10行の表を作成する次の文を考えてみます。

CREATE table_T1 (C1 number);

次のSQL文を発行して表を分析します。

ANALYZE table_T1 COMPUTE STATISTICS;

Oracle以外のシステムに1000行を含む表を作成します。

CREATE TABLE remote_t1 (C1 number)

次のSQL文を発行します。

SELECT a.* FROM remote_t1@remote_db a, T1 b
    WHERE a.C1 = b.C1

Oracleオプティマイザは、エージェントに対して次のSQL文を発行します。

SELECT C1 FROM remote_t1

この文は、Oracle以外のシステムから1000行すべてをフェッチし、Oracleデータベース内で結合を実行します。

remote_t1表のC1列に一意索引を追加して同じSQL文を再発行すると、エージェントは次のSQL文を受信します。

SELECT C1 FROM remote_t1 WHERE C1 = ?

これを、ローカルt1C1の値ごとに受信します。


注意:

('?')はバインド・パラメータ・マーカーです。 また、Oracleにより生成されたバインド変数を含む結合条件は、ネステッド・ループ結合方法についてのみ生成されます。


SQL実行計画を確認するには、SQL文のEXPLAIN PLANを生成します。 最初に、adminディレクトリにutlxplanをロードします。

コマンド・プロンプトに、次のように入力します。

EXPLAIN PLAN FOR SELECT a.* FROM   remote_t1@remote_db a, T1 b
    WHERE a.C1 = b.C1;

次の文を入力してutlxplsユーティリティ・スクリプトを実行します。

@utlxpls

操作REMOTEは、リモートSQLが参照されていることを示します。

どの文が送信されるかを確認するには、コマンド・プロンプトに次の文を入力します。

SELECT ID, OTHER FROM EXPLAIN_PLAN WHERE OPERATION = 'REMOTE';

リモート結合の最適化の例

ここでは、Oracleデータベースのリモート結合最適化機能の例を示します。


注意:

Oracle以外のシステムからの表を使用するEXPLAIN PLANは、ローカルまたはリモートのOracle表スキャンを使用する同様の文とは異なる場合があります。これは、Oracle以外の表についてOracleで使用可能な統計に制限があるためです。最も重要なのは、Oracle以外の表の非一意索引には列を選択できないことです。使用可能な統計に制限があるため、次の例はリモート結合を実行する場合の内容とは異なる場合があり、あくまでも一例にすぎません。


次の例を考えてみます。

EXPLAIN PLAN FOR
SELECT e.ename, d.dname, f.ename, f.deptno FROM
 dept d,
 emp@remote_db e,
 emp@remote_db f
  WHERE e.mgr = f.empno
  AND e.deptno = d.deptno
  AND e.empno = f.empno;

@utlxpls
表3-1 EXPLAIN PLAN
操作 名前 バイト コスト Pstar

SELECT STATEMENT

-

1

101

128

-

HASH JOIN

-

2K

132K

19

-

TABLE ACCESS FULL

DEPT

21

462

1

-

REMOTE

-

2K

89K

16

-

次の文を発行します。

SET longwidth 300
SELECT other FROM plan_table WHERE operation = 'REMOTE';

次のように出力されます。

SELECT
A1."ENAME",A1."MGR",A1."DEPTNO",A1."EMPNO",A2."ENAME",A2."DEPTNO",A2."EMPNO",A2.
"EMPNO" FROM "EMP" A1,"EMP" A2 WHERE A1."EMPNO"=A2."EMPNO" AND
A1."MGR"=A2."EMPNO"

Oracle以外のアクセスに対するオプティマイザの制限

  1. リモート・オブジェクトに関する列統計情報はありません。このため、適切な実行計画が得られない場合があります。実行計画を検証し、ヒントを使用して最適化してください。

  2. リモート結合を強制実行するためのオプティマイザ・ヒントはありません。ただし、リモート問合せブロックの最適化機能は用意されています。この機能を使用すると、リモート結合を取得するために問合せを少しリライトできます。

    たとえば、前述の例は次のようにリライトできます。

    SELECT v.ename, d.dname, d.deptno FROM
     dept d,
        (SELECT /*+ NO_MERGE */
         e.deptno deptno, e.ename ename emp@remote_db e, emp@remote_db f
             WHERE e.mgr = f.empno
             AND e.empno = f.empno;
        )
      WHERE v.deptno = d.deptno;
    
    

    これにより、リモート結合が保証されます。これは、NO_MERGEヒントを使用してネストした問合せに分離されるためです。


Go to previous page Go to next page
Oracle
Copyright © 2001, 2002 Oracle Corporation.

All Rights Reserved.
Go To Table Of Contents
目次
Go To Index
索引