7

Oracleデータベースの単一のテーブルからの単純なクエリを実行する次のコードがあります。

entityManager.createQuery(
        "SELECT a FROM " + Person.class.getSimpleName() 
        + " a WHERE lower(a.firstName) = '" + firstName + "'")
        .getSingleResult();

Hibernateは次のSQLを生成します:

select
        * 
    from
        ( select
            person0_.id as id75_,
            person0_.FIRSTNAME as FIRSTNAME75_,
            person0_.LASTNAME as LASTNAME75_
        from
            PERSONS person0_ 
        where
            lower(person0_.FIRSTNAME)='john' ) 
    where
        rownum <= ?

DBAは、パフォーマンス上の理由から、このクエリはより単純にする必要があることを示唆しています。次のようにクエリを単純化するために休止状態を作成するにはどうすればよいですか?

select ID, FIRSTNAME, LASTNAME from PERSONS 
where lower(FIRSTNAEM) = 'john' and rownum <= 1

ありがとう

4

4 に答える 4

8

私はexplain planあなたと同様のクエリを調べましたが、計画は両方のクエリでまったく同じであるため、DBAが提案しているパフォーマンス上の理由がわかりません。

クエリをでラップするとselect * from ( ... ) where rownum = 1、1行後に内部クエリを停止するSTOPKEYが導入されます。Oracleは、サブクエリからすべての結果を実際に取得するのではなく、最初の行のみを取得する必要があることを認識しています。

Hibernateによって生成されたクエリを変更するには、Hibernateのソースコード自体を変更しないと不可能です。

このネストが必要な理由は、ORDER BY句を導入しようとすると明らかになることに注意してください。

select ID, FIRSTNAME, LASTNAME 
  from PERSONS 
 where lower(FIRSTNAME) = 'john' 
   and rownum <= 1
 order by LASTNAME

に異なる結果を生成します

select * from (
    select ID, FIRSTNAME, LASTNAME 
      from PERSONS 
     where lower(FIRSTNAME) = 'john' 
     order by LASTNAME)
  where rownum <= 1

...where rownumの前に適用されるように。order by clause

編集:

参考までに、これがExplain Planの出力であり、これは両方のクエリでまったく同じです。

---------------------------------------------------------------------------------
| Id  | Operation          | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |            |     1 |   112 |     2   (0)| 00:00:01 |
|*  1 |  COUNT STOPKEY     |            |       |       |            |          |
|*  2 |   TABLE ACCESS FULL| TABLE_NAME |     1 |   112 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------

機能インデックスを設定することでパフォーマンスを向上させることができますがlower(FIRST_NAME)、両方のクエリでまったく同じように使用されます。

于 2013-03-15T10:14:44.337 に答える
2

クエリパラメータを使用することを強くお勧めします。

Query query = entityManager.createQuery("SELECT a FROM "
    + Person.class.getSimpleName() 
    + " a WHERE lower(a.firstName) = :name");
query.setParameter("name", firstName);
return query.getSingleResult();

これには2つの重要な理由があります。

  • SQLインジェクションから保護します
  • SQLサーバーが解析されたクエリをキャッシュできるようにして、結果として実行されるパフォーマンスを向上させます

検討中

select * from (...) where rownum <= ?

ラッパー:これはパフォーマンスをまったく犠牲にしません。あなたはそれを無視することができます。

于 2013-03-15T10:27:16.073 に答える
0

驚いたことに、Hibernateが生成するものに固執する代わりに、ネイティブクエリとして書き直すことを提案した人は誰もいませんでした。私も同じ問題を抱えていましたが、パフォーマンスとは何の関係もありませんが、クエリの結果が同じになることはありませんでした。実際、クエリを理解するのは困難でした。

短期的および長期的な理由で進む最善の方法は、クエリを制御し、可能な限りいつでもどこでもネイティブクエリを使用することです。DBAが提案したようにクエリを記述し、JPQL NamedNativeQueryとして入力するだけで、誰もが満足します。楽しみ!

于 2019-01-15T04:15:55.777 に答える
-2

要するに-あなたはしません。Hibernateが生成するクエリを微調整するのに十分な理由はほとんどありません。また、ソースコードを変更しなくても可能であると私は確信しています。

私が考えることができる唯一の単純化は、のgetResultList().get(0)代わりに呼び出すことですgetSingleResult()。生成されたSQLは少し単純化されますが、一致するすべての行をDBからフェッチするため、パフォーマンスが低下するだけでなく、パフォーマンスが低下します。

ただし、クエリを少し改善することはできます。

クエリしているクラスの単純な名前を知っています。最初にエンティティから取得して連結する必要はありません。使用するだけです。

SELECT a FROM Person a...

クエリのパラメータを連結することは良い習慣ではありません。これにより、クエリがSQLインジェクション攻撃に対して脆弱になります。次のように書くことをお勧めします。

...WHERE lower(a.firstName) = :firstName");
query.setParameter("firstName", firstName);
于 2013-03-15T10:17:01.133 に答える