1

Hibernate を使用して Oracle からデータを取得する必要がありますが、問題は、クエリに渡されるパラメーターの数が常に同じではないことです。

簡単にするために、次のクエリを考えてみましょう。

COL_1、COL_2、...、COL_N を TAB_1 から選択 (COL_1 は (?、?、... ?) 内)

in 句に渡されるパラメータの数は 1 ~ 500 です。数が 1 ~ 50 程度の場合は非常に高速に動作しますが、200 の場合はクエリの実行 (解析、説明計画の作成、クエリの実行) に数秒かかります。 . インデックスが作成され、使用されます - チェックされました。

クエリは動的に作成されるため、Hibernate Criteria API を使用します。最初のクエリ (パラメータが 100 個を超える場合) では 3 ~ 5 秒かかりますが、次のクエリでは (パラメータの数が異なる場合でも) より速く動作します。最初のクエリの応答時間を改善したいと考えています。その場合、Hibernate が必須であると仮定して何ができますか?

私はこの動的クエリを削除し、xml ファイルに名前付きクエリとしていくつかの静的クエリを作成します (その場合、これらのクエリは最初にプリコンパイルされます)。たとえば、

1) パラメーターの数が 50 未満の場合は 1 つのクエリ。

この場合、30 個のパラメーターがある場合、クエリは次のようになります。

TAB_1 から COL_1、COL_2、...、COL_N を選択します。COL_1 は (PAR_1、PAR_2、...、PAR_30、-1、-1、...、-1 ?) にあります。

2) 数が 50 から 100 の間の場合は 2 つ目など。

問題は、名前付きクエリと HQL を使用するのはそれほど単純ではないことです (JDBC では簡単です)。HQL では、リストのみを渡し、そのリストでパラメーターの数を指定しません。つまり、実際にはクエリは 1 つだけです。

'from Person where id in (:person_list)'

myQuery.setParameterList("person_list", myList)

それを解決するオプションはありますか?

ちなみに、新しいクエリごとに説明プランが実行されると思ったので、たとえば:

(a) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) <100> - 説明計画を作成する必要があります

(b) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) <100> - EXPLAIN PLAN はすでにキャッシュに存在するため作成されません

(c) select COL_1, COL_2, ..., COL_N from TAB_1 where COL_1 in (?, ?, ..., ?) <120> - 説明プランを作成する必要があります (120 個のパラメーターを持つクエリの説明プランはありません) )しかし、(a)に比べて時間がかからず、(b)とほぼ同じなので、同様のクエリが以前に実行された場合、おそらくOracleはこのプランをより速く作成できます

その理由は何ですか?

4

2 に答える 2

1

ここにはいくつかのことがあります。まず第一に、IN リストをバインドすることはできません。少なくとも、できないと確信しています。Hibernate は、配列の内容を Oracle が使用できる静的な inlist に入れる何らかのトリックを使用していると思われます。

第 2 に、このクエリが多数の異なるパラメーターで実行される場合、変数をバインドする必要があります。そうしないと、データベース全体のパフォーマンスが低下します。

とはいえ、Tom Kyte が彼のブログで説明している「トリック」を使用して IN リストをバインドする方法があります -

http://tkyte.blogspot.com/2006/01/how-can-i.html

そこにあるコードは次のようになります。

ops$tkyte@ORA10GR2> with bound_inlist
2  as
3  (
4  select
5    substr(txt,
6           instr (txt, ',', 1, level  ) + 1,
7           instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1 )
8           as token
9    from (select ','||:txt||',' txt from dual)
10  connect by level <= length(:txt)-length(replace(:txt,',',''))+1
11  )
12  select *
13    from all_users
14   where user_id in (select * from bound_inlist);

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYSTEM                                  5 30-JUN-05
OPS$TKYTE                             104 20-JAN-06

一部:

12  select *
13    from all_users
14   where user_id in (select * from bound_inlist);

基本的にクエリの場所です。上記のビットは、カンマ区切りの文字列を値のリストに分割するトリックです。リストを :txt プレースホルダーにバインドする代わりに、リストを文字列に変換してバインドする必要があります。

クエリ時間の違いは、マシンのキャッシュまたは負荷変動によるものではありませんか? クエリの解析には少し時間がかかりますが、数秒は長い時間です。

于 2009-11-12T13:02:03.440 に答える
0

IN(...)そのリストに最大 1000 の ID を持つクエリを使用しました。ステートメントの解析/準備/キャッシュに数秒もかからないことを保証できます。

実際、Hibernate は、渡されたリスト内の実際の要素数を使用して、指定したパラメーター リストを自動展開します。そのため、特定のレベルで「固定」したい場合は、十分な数の -1 を追加するだけで済みます。終わり。ただし、特に最初のクエリ実行の高速化について話しているため、これは間違いなく問題ではありません.とにかく、ステートメントはまだ準備/キャッシュされていません.

クエリの実行計画を確認しましたか? Explain Plan と Autotrace の両方が有効になっていますか? リストに 30 要素と 120 要素がある場合、それらは異なりますか? 実際のクエリは、投稿した "select ... from table where id in (...)" のように見えますか、それとももっと複雑ですか? 30 から 120 要素の間のどこかで Oracle が (おそらく誤って) インデックスを使用しない方が高速であると判断したことに賭けても構わないと思っています。これが、時間が増加している理由です。

于 2009-11-12T17:55:51.400 に答える