| Oracle9i Heterogeneous Connectivity管理者ガイド リリース2(9.2) 部品番号B13816-01 |
|
この章では、Oracle Transparent GatewayとGeneric Connectivityの主機能について説明します。この章の内容は、次のとおりです。
SQL文の変換とデータ型のマッピングは、機能に従って実行されます。PL/SQLコールは、Oracle以外のシステムのストアド・プロシージャにマップされます。SQL文の場合、リモート・システムに機能がなければ、より単純な問合せが発行されるか、文が複数の問合せに分割され、必要な結果がOracleデータベースでの後処理により取得されます。
異機種間サービスでは、ほとんどの場合はOracle以外のシステムをOracle分散セッションに取り込むことができますが、これにはいくつかの制限があります。一般的な制限事項を次に示します。
INSERT INTO remote_table@link as SELECT * FROM local_table;
CONNECT BY句に対するサポートはありません。
SELECT remote_func@link(a,b) FROM remote_table@link
REF CURSOR型のout引数は指定できますが、inまたはin-outオブジェクトは使用できません。
Oracle以外のシステムとOracleサーバー間で、マテリアライズド・ビューを使用してデータをレプリケートできます。
マテリアライズド・ビューにより、Oracle以外のマスター・サイトにある表から特定の時点で取得されたデータがインスタンス化されます。この時点はリフレッシュ操作により定義されます。リフレッシュ操作により、このデータがOracleサーバーにコピーされ、Oracle上のコピーがOracle以外のシステム上のマスター・コピーと同期化されます。これにより、実体化されたデータがOracleサーバーでビューとして使用可能になります。
レプリケーション機能は、リフレッシュをスケジュールし、マテリアライズド・ビューを管理しやすいようにレプリケーション・グループに収集するためのメカニズムを提供します。リフレッシュ・グループにより、複数のマテリアライズド・ビューをシングル・オブジェクトと同様にリフレッシュできます。
異機種間レプリケーションのサポートは、必ずOracle間レプリケーション機能全体のサブセットに限定されます。
Oracleの異機種間サービス機能を介したOracle以外のデータへのアクセスには、その他の制限が適用されます。次に最も重要な制限事項を示します。
次の例に、Oracle以外のシステムからOracleデータ・ストアにデータをレプリケートする3つのマテリアライズド・ビューの基本設定と使用例を示します。
この例では、以降の例に使用する3つのマテリアライズド・ビューを作成します。
customer@remote_db表の主キーのマテリアライズド・ビューを作成します。
CREATE MATERIALIZED VIEW pk_mv REFRESH COMPLETE AS SELECT * FROM customer@remote_db WHERE "zip" = 94555;
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" );
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";
BEGIN dbms_refresh.make('refgroup1', 'pk_mv, sq_mv, cx_mv', NULL, NULL); END; /
BEGIN dbms_refresh.refresh('refgroup1'); END; /
パススルーSQL機能を使用すると、文をOracle9iサーバーで解析せずにOracle以外のシステムに直接送ることができます。この機能は、Oracle以外のシステムでOracleに等価文のない文での操作が見込まれる場合に役立ちます。
この項の内容は、次のとおりです。
PL/SQLのDBMS_HS_PASSTHROUGHパッケージを使用すると、パススルーSQL文をOracle以外のシステムで直接実行できます。このパッケージで実行される文は、標準SQL文と同じトランザクションで実行されます。
DBMS_HS_PASSTHROUGHパッケージは仮想パッケージです。概念上は、Oracle以外のシステムに常駐します。ただし、実際には、このパッケージのコールは異機種間サービスにより仲介され、1つ以上の異機種間サービスApplication Program Interface(API)コールにマップされます。次に、これらの異機種間サービスAPIコールは、ドライバによりOracle以外のシステムのAPIにマップされます。クライアント・アプリケーションは、Oracle以外のシステムのストアド・プロシージャをコールする場合と同じ方法で、データベース・リンクを介してパッケージ内のプロシージャをコールする必要があります。異機種間サービスにより実行される特殊処理は、ユーザーに対して透過的です。
Oracle以外のシステムでトランザクションを暗黙的にコミットまたはロールバックするパススルーSQL文を実行すると、そのトランザクションに影響します。たとえば、一部のシステムでは、データ定義言語(DDL)文を含むトランザクションが暗黙的にコミットされます。Oracleデータベース・サーバーは迂回されるため、Oracle以外のシステムでのコミットは認識されません。そのため、Oracleデータベース・サーバーではトランザクションがコミットされない間に、Oracle以外のシステムでデータがコミットされる可能性があります。
Oracleデータベース・サーバーでトランザクションがロールバックされると、Oracleデータベース・サーバーとOracle以外のサーバー間でデータに一貫性がなくなる可能性があります。このような状況では、グローバルなデータ非一貫性が発生します。
アプリケーションで通常のCOMMIT文を実行すれば、Oracleデータベース・サーバーはOracle以外のシステムと分散トランザクションを調整できることに注意してください。パススルー機能を使用して実行される文は、分散トランザクションの一部となります。
次の表に、DBMS_HS_PASSTHROUGHパッケージで提供されるファンクションとプロシージャを示します。これらを使用してパススルーSQL文を実行できます。
非問合せには、次の文およびタイプが含まれます。
非問合せ文を実行するには、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文を実行する手順は、次のとおりです。
図3-1に、バインド変数を使用した非問合せの実行のフロー図を示します。
文でバインド変数を指定する方法は、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;
Oracle以外のシステムでOUTバインド変数もサポートできる場合があります。 OUTバインド変数を使用すると、その値はSQL文の実行後まで認識されません。
OUTバインド変数はSQL文の実行後に移入されますが、Oracle以外のシステムでは、特定のバインド変数がOUTバインド変数であることをSQL文の実行前に認識する必要があります。バインド変数がOUTバインド変数であることを指定するには、BIND_OUT_VARIABLEプロシージャを使用します。
SQL文の実行後は、GET_VALUEプロシージャを使用して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構文のリスト項目をプログラム変数に取り出します。 すべての行がフェッチされた後に、カーソルをクローズできます。
すべての行をフェッチする必要はありません。カーソルのオープン後は、数行をフェッチした後など、いつでもカーソルをクローズできます。
次の例では、問合せが実行されます。
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(ゼロ)を戻すまで、ループ内で行がフェッチされて出力されます。
各種リレーショナル・データベースでは、ストアド・プロシージャで結果セットを戻すことができます。つまり、ストアド・プロシージャで1つ以上の行セットを戻すことができます。 これはどのデータベースにおいても比較的新しい機能です。
従来、データベースのストアド・プロシージャの動作は、高水準プログラミング言語のプロシージャと同じでした。引数の数は一定で、in、outまたは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以外の複数のシステムで、ストアド・プロシージャにより結果セットを戻すことができますが、その方法はまったく異なります。 他のリレーショナル・データベース管理システム(RDBMS)には、OracleのREF CURSORデータ型のようなものはありません。 DB2、Sybase、Microsoft SQL ServerおよびInformixでは、結果セットがある程度までサポートされています。これらのデータベースでの結果セットのサポートは、次の2つのモデルのどちらかに基づいています。
ユーザーは、ストアド・プロシージャの作成時に、そのストアド・プロシージャが戻すことのできる結果セットの最大数を明示的に指定できます。実行時には、ストアド・プロシージャは0(ゼロ)から事前に指定された最大数の範囲内で結果セットをオープンできます。ストアド・プロシージャの実行後に、クライアント・プログラムは埋込みSQLディレクティブを使用するか、クライアントのライブラリ・ファンクションをコールして、これらの結果セットへのハンドルを取得できます。その後、クライアント・プログラムは通常のカーソルの場合と同じ方法で結果からフェッチできます。
このモデルでは、ストアド・プロシージャが戻すことのできる結果セット数に事前指定の制限はありません。モデル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つの方法のどちらかで動作する必要があります。
異機種間サービスでの結果セットのサポートについては、次の注意事項があります。
REF CURSOR out引数がサポートされます。Inおよびin-out引数はサポートされません。
REF CURSOR out引数は、すべて匿名のREF CURSORとなります。 異機種間サービスでは、型指定のあるREF CURSORは戻されません。
通常、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以外のシステムのストアド・プロシージャが使用されています。
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がオープンされます。
次の例に、カーソル・モードで結果セットからフェッチする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 */
次の例に、逐次モードで結果セットからフェッチする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 */
表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;
表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です。このフィールドの値は、オブジェクト型に応じてTABLE、VIEW、SEQUENCEなどです。 ただし、Sybaseの場合、同じ情報がsysusersおよびsysobjectsという2つの表に格納され、その列名がOracleのALL_CATALOG表とは異なります。また、Oracleでは、表のタイプはTABLE、VIEWなどの値を含む文字列ですが、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で現行セッションのユーザー名を確認するには、次のように入力します。
SQL> SELECT a.USERNAME, b.USERNAME FROM USER_USERS a, USER_USERS@remote_db b; USERNAME USERNAME ------------------------------ ------------------------------ THSU thsu
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
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
| 関連項目:
データ・ディクショナリ変換の詳細は、付録D「データ・ディクショナリ変換のサポート」を参照してください。 |
Oracleには、次の5つの日時データ型があります。
異機種間サービスの汎用コードでは、SQLおよびストアド・プロシージャでのOracle日時データ型の使用がサポートされます。 Oracleではこれらのデータ型は、データ・ディクショナリ変換、またはデータ・ディクショナリ変換が関与する問合せではサポートされません。
異機種間サービスの汎用コードでOracle日時データ型がサポートされていても、ゲートウェイでサポートされるかどうかは、そのOracle以外のシステムのドライバで日時のサポートが実装されているかどうかによって異なります。また、ドライバに実装されていても、Oracle以外のシステムでの制限によってサポートが部分的である場合があります。これについては、該当するゲートウェイのマニュアルを参照してください。
ユーザーは、Oracle以外のシステムのタイムスタンプ書式をゲートウェイ初期化ファイル内で設定する必要があります。設定するパラメータは、HS_NLS_TIMESTAMP_FORMATおよびHS_NLS_TIMESTAMP_TZ_FORMATです。 また、ユーザーは初期化ファイル内でOracle以外のシステムのローカル・タイム・ゾーンも設定する必要があります。 設定するパラメータはHS_TIME_ZONEです。
異機種間サービスは、2フェーズ・コミット・メカニズムの実装のインフラストラクチャを提供します。そのサポート範囲はゲートウェイおよびリモート・システムに応じて異なります。詳細は、各ゲートウェイのマニュアルを参照してください。
以前のバージョンのゲートウェイでは、LONGデータ型のサポートが限定されていました。 LONGは、最大2GBの文字またはRAWデータ(LONG RAW)の格納に使用できるOracleデータ型です。これらの旧バージョンでは、LONGデータのサイズが4MBに制限されていました。これは、LONGデータが1ピースとして扱われるためでした。このため、処理できるデータのサイズにメモリーとネットワーク帯域幅という制限が課されていました。現行のゲートウェイの機能は、異機種間で2GBのLONGデータ全体をサポートするように拡張されています。データはエージェントとOracleサーバー間でピース単位で処理されるため、大きいメモリーおよびネットワーク帯域幅という要件から解放されます。
新しい異機種間サービス初期化パラメータHS_LONG_PIECE_TRANSFER_SIZEを使用すると、転送されるピースのサイズを設定できます。たとえば、異機種間ソースから2GBのLONGデータをフェッチする場合を考えます。ピース・サイズが小さくなるほどメモリー所要量が減少しますが、すべてのデータをフェッチするためのラウンドトリップは増加します。ピース・サイズが大きいとラウンドトリップは減少しますが、中間ピースを内部に格納するためのメモリー所要量が増加します。そのため、初期化パラメータを使用して最適のパフォーマンス(つまり、ラウンドトリップ回数とメモリー所要量との最適なトレードオフ)が得られるようにシステムをチューニングできます。この初期化パラメータを設定しなければ、システムではデフォルトでピース・サイズが64KBに設定されます。
Oracle9iより前では、SQL*PlusのDESCRIBEコマンドを使用してOracle以外のシステムのオブジェクトを記述できませんでした。Oracle9iでは、そのための機能が異機種間サービスに追加されています。ただし、このリリースにも制限事項があります。 たとえば、パッケージ、順序、シノニムまたは型の記述には、異機種間リンクを使用できません。
SQL*PlusのDESCRIBEコマンドはOCIDescribeAnyコールを使用して実装されます。これもOracle9iより前では使用できませんでした。 OCIDescribeAnyコールでは、データベースおよびスキーマの記述も可能です。SQL*PlusのDESCRIBEコマンドでは、これらの記述はできません。 異機種間サービスでは、どちらも実行できます。
この機能を実装するには、追加のドライバ・ロジックが必要です。ドライバによってはこの機能を実装していないものがあります。 ゲートウェイでこの機能がサポートされているかどうかについては、各ゲートウェイのマニュアルを参照してください。
この項では、分散環境でSQLに存在する制約について説明します。これらの制約は、Oracle以外のシステムまたはリモートOracleデータベースへのアクセスなど分散環境に適用されます。
この項の内容は、次のとおりです。
文は、制限付きではありますが、その文で参照されているデータベース・ノードまたはローカル・ノード上で実行できます。 参照されるオブジェクトすべてが1つの参照先ノードに解決される場合、Oracleはそのノードで問合せの実行を試みます。/*+ REMOTE_MAPPED */または/*+ DRIVING_SITE */ヒントを使用すると、参照先ノードで強制的に実行できます。 文が発行時とは異なるノードに転送される場合、その文をリモート・マップ文と呼びます。
文のリモート・マップを可能、必須および禁止にする方法には、特定のルールまたは制限が適用されます。これらのルールすべてに従わなければ、エラーが発生します。発行される文にこれらのルールすべてとの一貫性があるかぎり、ルールの適用順序は関係ありません。
分散環境でのリモート・マッピングにSQLを使用する場合は、異なる制約が存在します。 この分散環境には、OracleシステムとOracle以外のシステムの間のOracle Transparent Gateway接続またはGeneric Connectivity接続を必要とするデータベースのみでなく、リモートのOracleデータベースも含めることができます。
次の項では、分散環境でリモート・マッピングにSQLを使用する場合に存在する様々な制約について説明します。
Oracleのデータ定義言語では、ターゲット・オブジェクトの構文にはリモート参照の位置がありません。リモート参照を含むデータ定義言語文は常にローカルに実行されます。異機種間サービスの場合、これは、SQLを使用してOracle以外のデータベースにデータベース・オブジェクトを直接作成できないことを意味します。
ただし、パススルーSQLを使用する間接的な方法があります。
次の例を考えてみます。
BEGIN DBMS_HS.PASSTHROUGHSQL.EXECUTE_IMMEDIATE@remote_db ( 'create table x1 (c1 char, c2 number)' ); END;
このルールは、リモートの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つの特殊事例には、USER、USERENVおよびSYSDATEなど、セッション固有のSQL関数があります。これらの関数は、元のサイトで実行することが必要な場合があります。これらの関数を含むリモート・マップ文には、コールバック・リンクが含まれます。コールバックがサポートされていないOracle以外のデータベースの場合は、制限エラーに(デフォルトで)なる場合があります。
たとえば、次の文を考えてみます。
DELETE FROM emp1@remote_db WHERE hiredate > sysdate;
この文は次のエラー・メッセージを戻します。
ORA-02070: データベースREMOTE_DBはこのコンテキストでは特殊関数をサポートしません。
通常、このエラーを解決するには、次のように特殊関数をバインド変数で置き換える必要があります。
DELETE FROM emp1@remote_db WHERE hiredate > :1;
現在、異機種間アクセスの場合、前述の列型はサポートされていません。したがって、この制限が直接発生することはありません。
この例のようにコールバック・リンクや特殊関数などの特殊な構成メンバーについては、ルール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はローカルに実行する必要があります。
異機種間アクセス・モジュールでは表の式がサポートされていないため、この制限が直接発生することはありません。
たとえば、次の文を考えてみます。
SELECT long1 FROM table_with_long@remote_db, dual;
この文は次のエラー・メッセージを戻します。
ORA-02025: SQL文の中の表はすべてリモート・データベースにある必要があります
このエラーは、次の文で解決できます。
SELECT long1 FROM table_with_long@remote_db WHERE long_idx = 1;
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はこのコンテキストでは特殊関数をサポートしません。
Oracle以外のリモートの順序はサポートされないため、このルールは異機種間アクセスには発生しません。コールバック・リンクの制限により、Oracle以外のリモート・アクセスに関する制限はすでに存在します。
このルールも、ルールBで説明したコールバック・リンクの制限にすでに含まれています。
この制限を回避するには、一意のバインド変数を使用して数値でバインドします。
前の項で述べたように、OracleサーバーによるOracle以外のリモート・オブジェクトに対する更新は、Oracleデータベースに存在するコールバック機能サポートの欠落によって制限されます。 これによって、Oracle以外のリモート・データベース・オブジェクトにおけるデータ操作言語(DML)は、そのデータベース内のすべてのオブジェクトを参照する文、またはリテラルやバインド変数である文に制限されます。
このため、元のOracleサーバーまたはその他のリモート・オブジェクトからはオブジェクトを参照できません。
また、リモートUPDATEの場合と同様に、Oracle以外であるか前述のリモートUPDATEであるかに関係なく、Oracle形式のSQL UPDATEがサポートされていない場合は、次の書式でエラーが戻されます。
ORA-02070: データベース ... はこのコンテキストでは ... をサポートしません。
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 = ?
これを、ローカルt1のC1の値ごとに受信します。
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データベースのリモート結合最適化機能の例を示します。
次の例を考えてみます。
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
| 操作 | 名前 | 行 | バイト | コスト | 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"
たとえば、前述の例は次のようにリライトできます。
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ヒントを使用してネストした問合せに分離されるためです。
|
|
![]() Copyright © 2001, 2002 Oracle Corporation. All Rights Reserved. |
|