ODP.NETによるデータ・アクセス・パフォーマンスの最適化
目的このチュートリアルでは、ODP.NETを使用して.NETアプリケーションのパフォーマンスを最適化する方法について説明します。 所要時間約20分 トピックこのチュートリアルでは、以下のトピックについて説明します。
スクリーンショットの表示
注: 各手順に関連したスクリーンショットのみをロードして表示する場合は、それぞれの手順にあるアイコンの上にカーソルを置いてください。 個々のスクリーンショットはクリックすると、非表示になります。 概要Oracleデータベースには、問合せの実行やクライアントからのデータの取得を最適化するためのメソッドが多数用意されています。 ODP.NETを使用すると、このようなパフォーマンスの最適化が可能なため、.NET開発者は、より効率的なデータベース・プログラムを作成できます。 このチュートリアルでは、ODP.NET開発者向けのもっとも一般的に使用されているパフォーマンス・チューニング・メソッドをいくつか紹介します。 最初のセクションでは、ステートメント・キャッシュについて説明します。 ステートメント・キャッシュは、initial文の実行中に作成されるサーバー・カーソルをキャッシングすることで、実行済みの各SQL文またはPL/SQL文の再解析を不要にします。 その後、同じ文を実行する際には、そのカーソルから解析された情報を再使用し、再解析せずに文を実行できるので、パフォーマンスが向上します。 ステートメント・キャッシュによるパフォーマンスの改善を確認するためには、繰り返し実行される文だけをキャッシングすることを推奨します。 さらに、SOL文またはPL/SQL文では、リテラル値ではなくパラメータを使用することで、ステートメント・キャッシュを最大限に活用できます。これは、パラメータ化された文から解析された情報は、その後の実行でパラメータの値が変わっている場合でも再使用できるためです。 2番目のセクションでは、ODP.NETでFetchSizeプロパティおよびRowSizeプロパティを使用して、データベース・ラウンドトリップごとに取得されるデータ量を制御する方法を示します。複数のラウンドトリップで1つのデータ・セットを取得すると効率が良くない場合があります。 この2つのプロパティを使用すると、開発者はデータベース・サーバーから取得するデータの量を微調整することができ、使用する必要があるデータベース・ラウンドトリップの回数を減らすことができます。 3番目のセクションでは、ODP.NETとOracleデータベース間でPL/SQL連想配列を受け渡しする方法を示します。 PL/SQL連想配列を使用すると、大量の同じデータ型のデータを.NET配列に渡すことができます。 このメソッドは、必要に応じてデータを1つのパラメータにまとめて、この2層間で受け渡しする柔軟で簡単な方法を提供します。 4番目のセクションでは、SQLをバッチ化する方法、Oracle REF Cursorの使用方法を示し、Multiple Active Result Sets(MARS)を実際に使用します。 SQLをバッチ化すると、開発者は複数のSQL文を1回のデータベース・ラウンドトリップで実行できます。 Oracle REF Cursorは、Oracleデータベースに固有のデータ型です。 固有の結果セット処理機能を提供しており、柔軟なデータ処理とチューニングが可能になります。 MARSはOracleで常にサポートされており、使用すると、単一の接続で一度に複数の結果セットをアクティブにできます。 注:このデモで示されるパフォーマンスの向上は、必ずしも本番環境で示される結果を反映するものではありません。 時には、設定、ハードウェア、データベース構成などの違いにより、実際の結果がデモの結果より良かったり、悪かったりします。たとえば、このデモを同じマシン上にあるクライアントとサーバーを使用して実行する場合、データベース・ラウンドトリップの回数を減らすことで得られるパフォーマンスの向上は、本番環境よりも大きい場合があります。これは、本番環境では通常、2つの異なるマシンにクライアントとサーバーが配置されているためです。 前提条件このチュートリアルを始める前に以下を確認してください。
ステートメント・キャッシュの使用ステートメント・キャッシュは、同じSQL文またはPL/SQL文を繰り返し実行する必要がある場合に有効です。 ODP.NETは、直前に使用された文をキャッシュします。 開発者は、直前に使用された文のうちどれをいくつキャッシュするかを決めます。 文がキャッシュされると、その文の解析ツリーが保持され、迅速に検索することができるので、パフォーマンスが向上します。 ODP.NETステートメント・キャッシュを使用するには、以下の手順を実行します。
FetchSizeおよびRowSizeの使用データベース・ラウンドトリップごとにフェッチされるデータの量を制御すると、アプリケーションのパフォーマンスを最適化できます。 たとえば、エンド・ユーザーが行全体を使用する必要がある場合に、1回のラウンドトリップで行データの半分だけを取得するのは非効率的です。 ラウンドトリップを2回おこなう代わりに1回にすれば、パフォーマンスを最適化できるはずです。 ODP.NETを使用すると、開発者は問合せの行サイズを自動的に検出することができ、1回のラウンドトリップで取得する行数を指定できます。 この機能により、.NETプログラマはデータ取得の最適化をより簡単に実現できます。 このトピックでは、一度に1行をフェッチする問合せを実行し、次に、一度に100行をフェッチする同じ問合せを実行して、ラウンドトリップの回数を少なくすることによるパフォーマンスの向上を確認します。 以下の手順を実行します。
配列パラメータの受け渡し
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1. | Solution Explorerで、「array.sql」を右クリックして、「Open」を選択します。
|
| 2. | プロシージャを確認します。 PL/SQLストアド・プロシージャMYSPを使用して、PL/SQLパッケージMYPACKが作成されていることを確認します。 PL/SQL連想配列を使用するため、PL/SQLを使用する必要があります。 このストアド・プロシージャには、PL/SQL連想配列入力パラメータが設定されており、この値は出力用PL/SQL連想配列としてそのまま返されます。 ファイル内のすべてのコードを選択し、[Ctrl]+[C]を押して、クリップボードにコピーします。
|
| 3. | 「View」→「Server Explorer」を選択します。
|
| 4. | HRユーザー用のデータ接続を作成する必要があります。 データ接続の作成方法は、Oracle Developer Tools for Visual Studio .NETのチュートリアルで確認できます。 HR.ORCL接続を右クリックして、「Query Window」を選択します。 この例では、ORCLがデータベース・エイリアスですが、ご使用のシステムのデータベース・エイリアスとは異なる場合があります。
|
| 5. | [Ctrl]+[V]を押して、問合せウィンドウにコードを貼り付けます。 問合せウィンドウ内のすべてのテキストを選択して、「Execute」をクリックします。
|
| 6. | ストアド・プロシージャが正常に実行されたら、ODP.NET Best Practices.csコードに戻って、コードを確認します。
|
| 7. | 下方向にスクロールして、Demo 3セクションに移動します。 次のコードを入力します。 cmd = new OracleCommand("MYPACK.MYSP", con);
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter param1 = cmd.Parameters.Add("param1", OracleDbType.Varchar2);
OracleParameter param2 = cmd.Parameters.Add("param2", OracleDbType.Varchar2);
param1.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
param2.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
注: ODP.NETによって、MYPACK.MYSPストアド・プロシージャに対するコールが設定され、その配列パラメータがバインドされます。 配列の要素には、データ型Varchar2の値が含まれます。 配列がストアド・プロシージャで受け渡しされていることを示すために、ODP.NETは、Oracleデータベースのコレクション・データ型であるPL/SQL連想配列としてパラメータを設定します。
|
| 8. | ここで、パラメータの向き(入力または出力)を設定し、各パラメータの値を指定します。 param2をNULLに設定します。 次のコードを入力します。 param1.Direction = ParameterDirection.Input;
|
| 9. | 次に、配列内の要素の最大数(この例では3)、およびvarchar2の最大サイズ(この例では20文字)を指定します。 次のコードを入力します。 param1.Size = 3; ストアド・プロシージャを実行して結果を出力するには、次のコードを入力します。 cmd.ExecuteNonQuery(); for(int i=0; i<3; i++)
{
Console.Write((param2.Value as OracleString[])[i]);
Console.WriteLine();
}
|
| 10. | 注:前のトピックで実行したDemo 1セクションおよびDemo 2セクションのforループをコメント・アウトすれば、実行済みのデモに時間を費やさないようにできます。 ループだけをコメント・アウトすれば、チュートリアルの残りのトピックの結果には影響しません。 再度ループを繰り返し実行する時間を節約できます。
Demo 1セクションおよびDemo 2セクションのforループをコメントにしていない場合は、前の各デモの結果が表示されたあとで、[Enter]を計2回押して、このセクションでのプログラムの実行を続けます。 これでコードの確認を終えたので、アプリケーションを実行して結果を確認できます。 「Build」→「Build Solution」を選択します。
|
| 11. | 「Debug」→「Start Debugging」を選択するか、[F5]を押します。
|
| 12. | 結果を検証します。
[Enter]は押さないでください。 プログラムの実行を続行するために、後続のトピックでプログラムにコードを追加します。 コマンド・ウィンドウを閉じます。 |
複数のSQL文をバッチで実行して、データベース・ラウンドトリップの数を減らしたいと思うことはよくあるはずです。 ODP.NETでは、匿名のPL/SQLを介してこれを実現できます。 匿名のPL/SQLは、任意の数の問合せ、更新、挿入、および削除文をバッチ化するために使用できます。
このトピックでは、匿名のPL/SQLを使用して、1回のデータベース・ラウンドトリップで3つの問合せを実行します。 結果を取得するために、Oracle REF Cursorを使用して、いかに柔軟にデータベースからデータを取得できるかを確認できます。 REF Cursorでは、結果セットから必要なデータだけを取得できます。 最後に、ODP.NETが、同じ接続に対して複数のアクティブな結果セットをオープンしたままにできることを確認します。 以下の手順を実行します。
| 1. | ODP.NETにSQLをバッチ化させるために、匿名のPL/SQLを使用します。 注:匿名のPL/SQLは、"BEGIN"で始まり、"END;"で終わる文字列にすぎません。 これらの間に、1回のラウンドトリップで実行する必要があるSQL文を記載します。 この例では、3つの問合せをバッチ化して3つの結果セットを返します。 パラメータは、ODP.NETが問合せ結果をクライアントに返せるようにするためにバインドされます。 匿名のPL/SQLは、他のテキスト・コマンドと同様に実行されます。 そのため、コマンドはCommandType.Textとしてバインドされます。 下方向にスクロールして、コードのDemo 4セクションに移動します。 次のコードを入力します。 string cmdtxt = "BEGIN " +
"OPEN :1 for select FIRST_NAME, DEPARTMENT_ID from EMPLOYEES where DEPARTMENT_ID = 10; " +
"OPEN :2 for select FIRST_NAME, DEPARTMENT_ID from EMPLOYEES where DEPARTMENT_ID = 20; " +
"OPEN :3 for select FIRST_NAME, DEPARTMENT_ID from EMPLOYEES where DEPARTMENT_ID = 30; " +
"END;";
cmd = new OracleCommand(cmdtxt, con);
|
| 2. | 次に、3つの出力REF Cursorをコマンドにバインドします。 この例では、それぞれのREF CursorでDEPARTMENT_ID 10、20、および30の従業員を選択します。 次のコードを入力します。 OracleParameter p1 = cmd.Parameters.Add("refcursor1", OracleDbType.RefCursor); OracleParameter p2 = cmd.Parameters.Add("refcursor2", OracleDbType.RefCursor); OracleParameter p3 = cmd.Parameters.Add("refcursor3", OracleDbType.RefCursor);
|
| 3. | バッチ化した文を実行し、最初のパラメータから結果をフェッチせずに、2番目と3番目のパラメータからデータを取得します。 これが、REF Cursorを使用することの利点の1つであり、クライアントが要求する場合のみ、データを取得できます。 REF Cursorでは、フェッチする結果セット内のデータも個別に選択できます。 MARSを使用すると、両方のOracleDataReaderが同時にアクティブにデータを読み取っていることを確認できます。 関連するコメントの後に、以下のコードを入力します。 cmd.ExecuteNonQuery(); OracleDataReader dr1 = ((OracleRefCursor)cmd.Parameters[2].Value).GetDataReader(); whileループを追加して、両方のDataReadersを同時に取得して、MARSが機能しているかどうかをテストします次のコードを入力して、2つのREF Cursorの結果を出力します。 while (dr1.Read() && dr2.Read())
{
Console.WriteLine("Employee Name: " + dr1.GetString(0) + ", " +
"Employee Dept:" + dr1.GetDecimal(1));
Console.WriteLine("Employee Name: " + dr2.GetString(0) + ", " +
"Employee Dept:" + dr2.GetDecimal(1));
Console.WriteLine();
}
|
| 4. | これでコードの確認を終えたので、アプリケーションを実行して結果を確認できます。 「Build」→「Build Solution」を選択します。
|
| 5. | 「Debug」→「Start Debugging」を選択するか、[F5]を押します。
|
| 6. | 結果を検証します。
|
このチュートリアルで学習した内容は、次のとおりです。
| ステートメント・キャッシュの使用 | ||
| FetchSizeおよびRowSizeの使用 | ||
| 配列パラメータの受け渡し | ||
| バッチSQL、REF Cursor、およびMultiple Active Result Sets(MARS)の使用 | ||