Articles
Adaptive CursorsとSQL Plan Managementバインド変数を使用して、利用前に比べて最適な実行計画となることを確認してみましょう。
すでに何度も耳にしている読者も多いと思いますが、バインド変数を使用してパフォーマンスを高めることができます。まだ知らない人のために、できるだけ簡単に中核となる概念を説明します(これ以外にも、Tom Kyteの asktom.oracle.comを参照することを推奨します。このページでは、SQL文のパフォーマンスを改善する上で、バインド変数を使用することがどれだけ重要であるかを知ることができます。また、バインド変数をいくつかの言語で使用する方法についても学習できます)。 CUSTOMERSという表が、STATE_CODEという列を持っているとします。この列に、顧客の居住州(米国)をCT、NYなどの2文字の略称で格納しています。コネティカット州('CT')に居住しており、3回以上の購入履歴を持つ顧客が何名いるかを知りたい場合、このようになるでしょう。 select count(1) from customers where state_code = 'CT' and times_purchased > 3; この問合せを実行すると、Oracleデータベースは解析という作業を行い、SQL文の実行計画を生成します。解析が終了すると実行の準備が整います。解析は、ソフトウェアのコードをコンパイルする概念と似ています。C++で何かを記述しても、そのままオペレーティング・システム上で実行することはできません。まずコンパイルしてから、実行可能な状態にします。 解析の作業によって、SQL文を実行可能な状態にするのです。 では、別のユーザーが以下のような文を実行したとします。 select count(1) from customers where state_code = 'NY' and times_purchased > 3; これは、上記の問合せとほぼ同一ですが、1つだけ異なる点があります。それは、state_codeで検索するのがCTではなくNYである点です。理想的には、定数値は実行時に受け渡し、解析コードは同一であるべきでしょう。 しかし、この問合せの記述方式では、Oracleデータベースは別の解析が必要があると解釈してしまいます。 代わりに、問合せが以下のように記述されたとします。 select count(1) from customers where state_code = <StateCode> and times_purchased > 3; 最初の問合せは<StateCode>の値としてNYを、2つ目の問合せはCTを受け渡します。この場合、解析は最初だけとなるでしょう。 この例の<StateCode>は概念的にバインド変数として知られており、実行時に値を受け渡すためのプレースホルダーです。バインド変数は、以下に示すとおり、:VariableNameという形式で表記されます。 where state_code = :state_code SQL文がバインド変数を持たず、代わりにwhere state_code = 'CT'といった定数値を参照する場合、初期化パラメータを指定して、すべての定数値をバインド変数へ強制的に変換できます。 cursor_sharing = force このパラメータは、たとえばwhere state_code = 'CT'という文を、where state_code = ":SYS_0001"に変換します。SYS_0001は、システムで生成した変数名です。この方式により、SQL文を同一化できます。 バインド変数の問題バインド変数が優れているにも関わらず、常に使用しないのはなぜでしょうか。特効薬のように、cursor_sharingは不適切なコードを共有可能な文へ変えるのでしょうか(すでに答えを知っていて、特にバインド・ピーキングの概念を理解している場合は、"Adaptive Cursors"のセクションまでスキップしてください)。 たとえば、STATE_CODE列にインデックスがあるとします。列の値は、以下のとおりです。 select state_code, count(1) from customers group by state_code; ST COUNT(1) -- ---------- NY 994901 CT 5099 明らかに、データには大きな偏りがあります。5%前後の行にのみ'CT'が入っており、残りは'NY'です。これは州の人口を考えれば、驚くことではありません。では、以前紹介した問合せで生成される実行計画を確認します。
SQL> set autot traceonly explain
SQL> select * from customers where state_code = 'NY' and times_purchased > 3
2 /
Execution Plan
----------------------------------------------------------
Plan hash value: 2008213504
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 895K| 26M| 1532 (9)| 00:00:19 |
|* 1 | TABLE ACCESS FULL| CUSTOMERS | 895K| 26M| 1532 (9)| 00:00:19 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("TIMES_PURCHASED">3 AND "STATE_CODE"='NY')
問合せでは全表スキャンを行っています。列の95%が問合せで返されるので、この作業は適切です。インデックス・スキャンではコストが非常に高くつきます。次に、同じ問合せを'CT'で実行してみます。
SQL> c/NY/CT
1* select * from customers where state_code = 'CT' and times_purchased > 3
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 4876992
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4589 | 138K| 56 (2)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| CUSTOMERS | 4589 | 138K| 56 (2)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IN_CUST_STATE | 5099 | | 12 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("TIMES_PURCHASED">3)
2 - access("STATE_CODE"='CT')
問合せはインデックスを使用しました。これについても、適切な作業と言えます。CTは列の5%のみで、インデックス・スキャンが効率的です。 バインド変数を使用したときは、どうなるのでしょうか。以下は、Oracle Database 10gでの動作となります。
SQL> var state_code varchar2(2)
SQL> exec :state_code := 'CT'
PL/SQL procedure successfully completed.
SQL> select max(times_purchased) from customers where state_code = :state_code
2 /
Execution Plan
----------------------------------------------------------
Plan hash value: 296924608
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 1511 (8)| 00:00:19 |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
|* 2 | TABLE ACCESS FULL| CUSTOMERS | 500K| 2929K| 1511 (8)| 00:00:19 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("STATE_CODE"=:STATE_CODE)
オプティマイザは、CUSTMOERS表に対して全表スキャンを選択しました。全レコードのわずか5%に満たないCTのみを検索するなら、インデックスを使用した方がいいのではないでしょうか。 なぜオプティマイザはインデックス・スキャンではなく、全表スキャンを選択したのでしょうか。 答えは、バインド・ピーキングという現象にあります。バインド変数値を'NY'に設定して問合せを実行したとき、オプティマイザはハード解析を実施し、バインド変数の値を確認しました。その値は、'NY'でした。'NY'は列の95%を占めるので、オプティマイザは全表スキャンを(期待どおり)選択したのです。さらに、オプティマイザは問合せの計画を凍結しました。そのため、次に同じ問合せを'CT'で実行しても計画は再計算されず、目的に合ったものでなくても以前使った同じ計画を使用してしまうのです。問合せの値で'CT'を使っていれば、オプティマイザは適切な計画を選択していたでしょう。 このように、バインド変数は多くの場合に有効ですが、'CT'と'NY'の値がそれぞれ5%と95%といった、値の比率が根本的に実行計画に影響を与える場合、適していないということです。 比率がほぼ等しいデータ分布では、実行計画は同じでしょう。よって、賢いSQL開発者であれば、バインド変数使用の鉄則を破り、代わりに定数を採用するという選択ができるはずです。 Adaptive Cursorsもしも、すぐれた開発者が揃っていなかったり、文を再記述する時間がない場合は、どうすればいいでしょうか。オラクルでは、別の最適な方法を提供していないのでしょうか。 もちろん、あります。Oracle Database 11gでは、カーソルに新たなインテリジェント機能を追加しました。問合せが実行されるたびに、ただ盲目的にキャッシュされた実行計画を使用するのではなく、バインド変数の値が変更された場合は、再計算の必要があるかどうかを判断できるようになったのです。 カーソルにバインド変数が含まれる場合、Oracleデータベースは変数にどのような値が受け渡されているかを検証し、計画を再計算するべきか判断します。 再計算する必要がある場合、カーソルは"バインド・センシティブ"とマークされます。 さきほど示した例が、非常にいい例です。バインド変数の値に基づいて、最適なオプティマイザ計画が使用されます。特別なことをする必要はなく、自動的にです。 V$SQLディクショナリ・ビューは、IS_BIND_SENSITIVE列とIS_BIND_AWARE列を新たに追加するよう修正されました。動作を確認してみましょう。 select is_bind_sensitive, is_bind_aware, sql_id, child_number from v$sql where sql_text = 'select count(1) from customers where state_code = :state_code and times_purchased > 3' I I SQL_ID CHILD_NUMBER - - ------------- ------------ Y Y 7cv5271zx2ttg 0 Y N 7cv5271zx2ttg 1 次に、列の意味を確認します。Oracleデータベースはカーソルをしばらく観察し、値の変化を確認します。値が異なるため計画を変更する可能性がある場合、カーソルには"バインド・センシティブ"というラベルが付けられ、IS_BIND_SENSITIVE列には"Y"が表示されます。
新しいV$SQL_CS_HISTOGRAMビューには、SQL文が何回実行されたかが表示されます。以下の通り、回数は子カーソルごとの3つのバケットに整理されます。 select * from v$sql_cs_histogram where sql_id = '7cv5271zx2ttg' / ADDRESS HASH_VALUE SQL_ID CHILD_NUMBER BUCKET_ID COUNT -------- ---------- ------------- ------------ ---------- ---------- 45C8218C 2144429871 7cv5271zx2ttg 5 0 0 45C8218C 2144429871 7cv5271zx2ttg 5 1 2 45C8218C 2144429871 7cv5271zx2ttg 5 2 0 45C8218C 2144429871 7cv5271zx2ttg 4 0 8 ... and so on ... 45C8218C 2144429871 7cv5271zx2ttg 0 2 0 Adaptive Cursorsの共有機能による、バインド変数の値に基づいた正しい計画を使用するため、情報を保持しておく必要があります。これは新たなOracle Database 11gでは、V$SQL_CS_SELECTIVITYビューで確認できます。これは、バインド変数に受け渡された値を示すビューです。 select * from v$sql_cs_selectivity where sql_id = '7cv5271zx2ttg' / ADDRESS HASH_VALUE SQL_ID CHILD_NUMBE PREDICATE R LOW HIGH -------- ---------- ------------- ----------- ----------- - -------- ---------- 45C8218C 2144429871 7cv5271zx2ttg 5 =STATE_CODE 0 0.895410 1.094391 45C8218C 2144429871 7cv5271zx2ttg 4 =STATE_CODE 0 0.004589 0.005609 45C8218C 2144429871 7cv5271zx2ttg 4 =STATE_CODE 1 0.002295 0.002804 45C8218C 2144429871 7cv5271zx2ttg 3 =STATE_CODE 0 0.002295 0.002804 45C8218C 2144429871 7cv5271zx2ttg 0 =STATE_CODE 0 0.004589 0.005609 PREDICATE列は、ユーザーが使用したさまざまな条件(WHERE条件)を示します。LOW値とHIGH値は、受け渡された値の範囲を示します。 最後に、3つ目の新たなV$SQL_CS_STATISTICSビューは、バインド・アウェアまたはバインド・センシティブとマークされたカーソルの動作状況を示します。
select child_number,
bind_set_hash_value,
peeked,
executions,
rows_processed,
buffer_gets,
cpu_time
from v$sql_cs_statistics
where sql_id = '7cv5271zx2ttg';
CHILD_NUMBER BIND_SET_HASH_VALUE P EXECUTIONS ROWS_PROCESSED BUFFER_GETS CPU_TIME
------------ ------------------- - ---------- -------------- ----------- ----------
1 22981142 Y 1 9592 3219 0
0 22981142 Y 1 9592 3281 0
このビューは、Oracleデータベースに記録された実行の統計情報を表示します。EXECUTIONS列には、バインド変数に異なる値を用いた問合せの回数が表示されます。 出力のPEEKED列("P"で表示)には、オプティマイザがバインド変数の値を確認したかが示されます。 これらのビューには、この機能の動作を知る上で必要のない追加情報も表示されます。なお、Adaptive Cursorsは自動的に起動して使用されます。 SQL Plan Management(SPM)これまで、何度となく目にしたことがあると思いますが、最適な計画を持つ問合せが、何かの原因で突然計画を放棄することがあります。その"何か"とは、誰かが表を再度分析したり、star_transformationのようにオプティマイザに影響を与えるパラメータを変更したなど、多くの可能性があります。 苛立つあまり、統計情報の収集やパラメータの変更などの、すべての変更を禁止したくなるかもしれません。 しかし、それは"言うは易し行うは難し"です。データの内容そのものが変化することもあります。たとえば、Adaptive Cursorsの項で示した例のように、CUSTOMERS表にニューヨークからの顧客が入力があり、STATE_CODEのほとんどが"NY"の状態であるとします。 そこで、下記の条件を持つ問合せを実行します。 where state_code = 'NY' インデックス・スキャンは実行されません。その代わり、システムは全表スキャンを行います。条件が、以下の場合はどうでしょう。 where state_code = 'CT' 数行を返すために、インデックスが使用されます。では、 データの内容が変わった場合、どうなるでしょうか。たとえば、Connecticut(state_code = 'CT')の顧客が急増し、CTの率が突然70%に跳ね上がったとします。この場合、CTの問合せに全表スキャンを使用する必要があります。しかし、オプティマイザが統計情報を収集しなくなったので、オプティマイザはパターンの変化を認識できず、非効率的なインデックス・スキャンを実行し続けるでしょう。では、どうすればいいのでしょうか。 たとえば、Oracleデータベースが自動的に、統計情報の収集やデータベース・パラメータの変更によって計画を再評価し、最適と判断したときに新しい計画を使用するとしたらどうでしょう。 これは非常に素晴らしいことです。Oracle Database 11gであれば、これが可能なのです。その方法を見ていきましょう。 SQL計画ベースラインOracle Database 11gでは、根本的な要素が変更されてオプティマイザにより計画が更新されるような場合でも、すぐには実施されません。新しい計画を評価して今より改善される場合に、新しい計画を実装します。 さらに、問合せごとに計算された計画の履歴や比較内容などを確認するツールやインタフェースが提供されます。 ライフ・サイクルは、1回以上実行された、または"繰返し可能"な文を、Oracleデータベースが認識するところから始まります。 繰返し可能な文を認識すると、計画を取得し、論理構造のデータベースであるSQL Management Base(SMB)にSQL計画ベースラインとして格納します。 この問合せの新しい計画が計算されると、その計画もSMBに格納されます。つまり、SMBは問合せの各計画や生成方法などを格納するのです。 計画は、SMBへ自動的に格納されるわけではありません。自動で格納すると、SMBがすべての種類の問合せに対する計画を保持することになり、容量が増加してしまうからです。SMBに問合せを格納できる数を制御できるので、そうすべきです。方法は2つあります。1つは、ベースラインとなる繰返し可能な問合せをSMBへ自動で格納する方法で、もう1つはベースラインとなる問合せを手動でロードする方法です。 まずは、もっとも簡単な例を見てみましょう。SQL Plan Management機能を使用して、すべての繰返し可能な問合せのSQL計画ベースラインを自動で取得するには、optimizer_capture_sql_plan_baselinesというデータベースのパラメータを設定します。このパラメータは、デフォルトでFALSEとなっており、これをTRUEに変更します。これは、動的なパラメータです。 SQL> alter system optimizer_capture_sql_plan_baselines = true; この文を実行すると、繰返し可能な文すべての実行計画は、SQL計画ベースラインとしてSMBに格納されます。SQL計画ベースラインは、DBA_SQL_PLAN_BASELINESというビューに格納されます。これは、Oracle Enterprise Managerでも確認できます。ベースライン計画を確認するには、以下の図にあるとおり、Oracle Enterprise Managerを起動して「 Server」タブをクリックします。 このページで、Query Optimizerから「 SQL Plan Control」をクリックし、以下のようなメインとなるSPMページを表示させます。 「 SQL Plan Baseline」タブをクリックして、以下のような画面を表示させます。 これは、メインとなるSQL Plan Baseline画面です。左上隅に、設定パラメータが表示されます。 Capture SQL Plan Baselinesは TRUEになっています。これはALTER SYSTEMコマンドで有効にしたものです。その下では、 Use SQL Plan Baselinesが TRUE(デフォルト)に設定されています。これは、利用可能であればSQL Plan Baselinesを問合せで使用することを示しています。 問合せのための新しい計画が生成されるたびに、古い計画はSMBに履歴として保持されます。ただし、これは計画の履歴でSMBがいっぱいになってしまうことも意味しています。 Plan Retention (Weeks)の横にあるテキスト・ボックスのパラメータで、計画を保持する週を制御できます。この画面では、 53週に設定されています。SQL Plan Baselineが53週間使用されていない場合は、自動的に消去されます。 画面中央には、SQL文を検索できる検索ボックスがあります。検索する文字列を入力して「 Go」をクリックすると、上記にあるとおり、SQL文と関連する計画が表示されます。各ベースライン計画は、多くの関連する統計情報を持っています。それぞれを見ていきましょう。
同じ情報や詳細は、以下のDBA_SQL_PLAN_BASELINESビューでも確認できます。 SQL> desc DBA_SQL_PLAN_BASELINES Name Null? Type ----------------------------------------- -------- --------------- SIGNATURE NOT NULL NUMBER SQL_HANDLE NOT NULL VARCHAR2(30) SQL_TEXT NOT NULL CLOB PLAN_NAME NOT NULL VARCHAR2(30) CREATOR VARCHAR2(30) ORIGIN VARCHAR2(14) PARSING_SCHEMA_NAME VARCHAR2(30) DESCRIPTION VARCHAR2(500) VERSION VARCHAR2(64) CREATED NOT NULL TIMESTAMP(6) LAST_MODIFIED TIMESTAMP(6) LAST_EXECUTED TIMESTAMP(6) LAST_VERIFIED TIMESTAMP(6) ENABLED VARCHAR2(3) ACCEPTED VARCHAR2(3) FIXED VARCHAR2(3) AUTOPURGE VARCHAR2(3) OPTIMIZER_COST NUMBER MODULE VARCHAR2(48) ACTION VARCHAR2(32) EXECUTIONS NUMBER ELAPSED_TIME NUMBER CPU_TIME NUMBER BUFFER_GETS NUMBER DISK_READS NUMBER DIRECT_WRITES NUMBER ROWS_PROCESSED NUMBER FETCHES NUMBER END_OF_FETCH_COUNT NUMBER 計画名をクリックすると、計画の詳細が表示されます。出力結果は、以下のとおりです。 詳細では、問合せのEXPLAIN PLAN以外にも、計画のステータス(ACCEPTED、ENABLED、FIXEDなど)といった関連情報が表示されます。もう1つの重要な属性は、"Origin"です。これは AUTO-CAPTUREを示すもので、optimizer_capture_sql_plan_baselinesがTRUEに設定されていたため、計画がシステムによって自動で取得されたことを示します。 「 Return」をクリックして、前の図の計画リストへ戻ります。次に、ステータスが許可されていない計画を選択し、「 Evolve」をクリックして最良の計画になれるかどうかを検証します。次の画面が表示されます。 この画面で重要なのは、 Verify Performanceラジオ・ボタンです。計画を検証して、すでに存在する問合せのSQL Plan Baselineとパフォーマンスを比較する場合は、これを選択します。「 OK」をクリックします。比較レポートが表示されます。
-------------------------------------------------------------------------------
Evolve SQL Plan Baseline Report
-------------------------------------------------------------------------------
Inputs:
-------
PLAN_LIST = SYS_SQL_PLAN_b5429522ee05ab0e
SYS_SQL_PLAN_b5429522e53beeec
TIME_LIMIT = DBMS_SPM.AUTO_LIMIT
VERIFY = YES
COMMIT = YES
Plan: SYS_SQL_PLAN_b5429522e53beeec
-----------------------------------
It is already an accepted plan.
Plan: SYS_SQL_PLAN_b5429522ee05ab0e
-----------------------------------
Plan was verified: Time used 3.9 seconds.
Failed performance criterion: Compound improvement ratio <= 1.4.
Baseline Plan Test Plan Improv. Ratio
------------- --------- -------------
Execution Status: COMPLETE COMPLETE
Rows Processed: 1 1
Elapsed Time(ms): 3396 440 7.72
CPU Time(ms): 1990 408 4.88
Buffer Gets: 7048 5140 1.37
Disk Reads: 4732 53 89.28
Direct Writes: 0 0
Fetches: 4732 25 189.28
Executions: 1 1
これは、計画がどのように比較されたかを見るための優れたレポートです。特定の計画のパフォーマンスがよければ、オプティマイザはその計画を使用します。 新しい計画のパフォーマンスが相応に改善されない場合は、許可せず使用しません。SQL Performance Managementは、まず計画の比較内容を表示してから、最良の計画を選択できるようにします。 計画の許可ステータスは、DBMS_SPMパッケージを実行すれば変更できます。
declare
ctr binary_integer;
begin
ctr := dbms_spm.alter_sql_plan_baseline (
sql_handle => 'SYS_SQL_e0b19f65b5429522',
plan_name => 'SYS_SQL_PLAN_b5429522ee05ab0e',
attribute_name => 'ACCEPTED',
attribute_value => 'NO'
);
end;
SQL Plan Baselineを無効化することで、オプティマイザで使用できないようにします。これは、後で有効化することもできます。無効化するには、以下の文を使用します。
declare
ctr binary_integer;
begin
ctr := dbms_spm.alter_sql_plan_baseline (
sql_handle => 'SYS_SQL_e0b19f65b5429522',
plan_name => 'SYS_SQL_PLAN_b5429522ee05ab0e',
attribute_name => 'ENABLED',
attribute_value => 'NO'
);
end;
特定のSQL文による計画のステータスがベースラインでFIXEDに変更されたときは、それがEXPLAIN PLANに明記されます。計画の最後には、計画がベースラインによってFIXEDに変更されたことを示す行があります。 ストアド・アウトラインとの違いストアド・アウトラインに慣れていると、SQL Plan Managementとの違いがわからないかもしれません。両機能とも、問合せに対して特定の実行計画を強制するという、同じ動作をするからです。 しかし、両者には1つだけ小さな違いがあります。それは、SQL Plan Managementではベースラインが最良の計画かを検証でき、元の計画の代わりに実施できる点です。 一方のストアド・アウトラインは固定されており、無効化または別のプロフィールと置き換えない限り、上書きすることはできません。さらに、ベースライン計画は履歴を持っており、ある期間でどのように計画が進化したかを確認することも可能です。 関連する質問で、もしも問合せがストアド・アウトラインを持っており、ベースラインが最良の計画を検出した場合どうなるか、というものがあります。これは競合してしまうように思えますが、実際は違います。問合せをストアド・アウトラインで解析すると、その実行計画は問合せのSQL Plan Baselineとして取得されます。オプティマイザが文に対する別の実行計画を検出した場合、取得されてSMBに格納されますが、そのままで許可はされません。新しい実行計画が既存のSQL Plan Baseline(古いストアド・アウトライン)と比べて最良であることを証明するため、進化プロセスを実行する必要があります。 Stored Profilesとの違いStored Profilesは、"計画"というよりも、データに基づき、実行計画の一部として格納されたメタデータです。つまり、問合せ計画はプロフィールの条件に応じて変更されるということです。しかし、SQL Plan Baselinesでは条件の値が何であれ、計画は変更されません。 ユースケースこの機能を使用できるシナリオの例を紹介します。もっともいい例は、アップグレードやパラメータを変更するときです。問合せのセットをベースラインにする方法の1つに、SQL Tuning Set(STS)を使用してSTSからSPMへ文をロードする方法があります。これにより、Oracle Database 10g からSTSを生成してエクスポートし、Oracle Database 11gにインポートして、DBMS_SPM.UNPACK_STGTAB_BASELINEパッケージを実行して実行計画をSQL Plan Baselinesとしてインポートできます。その後、オプティマイザがより最良の計画を検出した場合、SMBに追加して比較できるようにします。 結論Adaptive CursorsとSQL Plan Managementは、まさに取得したリクエストに対してどのような処理を実施するか判断できる、インテリジェント化されたデータベースの2つの例です。 いずれについても、最良の機能が実現します。Adaptive Cursorsはバインド変数を使用しながらリスクを冒さず、SQL Plan Managementは実行計画を固定させずに安定性を維持しながら、短期間で進化させることができます。 "Oracle Database 11g:DBAと開発者のための主要な機能"ホームページに戻る
Arup Nanda (
arup@proligence.com) は、Oracle データベーステクノロジのあらゆる分野で12年以上の経験を持つ優秀なOracle DBAであり、2003年の『Oracle Magazine』で"DBA of the Year"に選ばれました。Arupは、Oracle関連のイベントの講演や雑誌の寄稿を頻繁に行っている
Oracle ACE Directorです。また、『
RMAN Recipes for Oracle Database 11g: A Problem Solution Approach』を含む4冊の書籍を共同で執筆しています。
|