web-dev-qa-db-ja.com

2つの列を結合するサブクエリを使用したHibernate基準クエリ

整数IDと日付の複合キーといくつかの追加の列を持つHibernateにマップされたテーブル「Quote」があります。 DetachedCriteriaを使用して、日付が最も大きい各IDの行を取得する条件クエリを作成したいと思います。

SQLでは、次のようなクエリを作成できます

SELECT * FROM Quote q1
  INNER JOIN (SELECT id, max(date) as maxdate FROM Quote
               GROUP BY id, date) q2
  ON q1.id = q2.id AND q1.date = q2.maxdate

Hibernateでは、次のように「groupby」サブクエリのDetachedCriteriaを作成できると思います(Quoteはテーブルをマッピングするクラスであり、「Qid」はキーの複合IDクラスであり、プロパティIDと日付があり、 Quoteクラスの「qid」プロパティ):

DetachedCriteria maxDateQry = DetachedCriteria.forClass(Quote.class);
maxDateQry.setProjection(
    Projections.projectionList()
        .add(Projections.max("qid.date", "maxdate"))
        .add(Projections.groupProperty("qid.id")));

ただし、上記のSQLの外側の部分と同等の条件クエリでこれを使用する方法がわかりません。私はの線に沿って何かを探しています

Criteria criteria = session.createCriteria(Quote.class);
criteria.add(
    Restrictions.and(
        Property.forName("qid.id").eq(maxDateQry???),
        Property.forName("qid.date").eq(maxDateQry???)));
List<Quote> quoteList = criteria.list();

上記の2つのProperty.forNameは、外部テーブルをサブクエリの対応する列に関連付けます。内部結合で値が1つしかない場合は、DetachedCriteriaに単一のProjectionを指定し、DetachedCriteriaをProperty.forName(...)。eq(..)に直接渡します。プロジェクションで2つの値(idとmaxdate)を指定してDetachedCriteriaを使用する方法がわかりません。

13
cha

まったく同じ問題があり、複数列のサブクエリの一致に対する正確な解決策を見つけることができませんでした。私がしたことは、1つの列の副選択にのみ一致する必要があるようにクエリを書き直すことでした。だから代わりに

SELECT *
FROM Quote q1
INNER JOIN (
    SELECT id, MAX(date) AS maxdate
    FROM Quote
    GROUP BY id
) q2
    ON q1.id = q2.id AND q1.date = q2.maxdate;

試してください:

SELECT *
FROM Quote q1
WHERE q1.date = (SELECT MAX(date) FROM Quote inner where inner.id = q1.id)

主な違いは、JOIN基準のMAX条件がすべて内部SELECT内にあることです。

このための基準/分離基準は次のようになります。

DetachedCriteria innerCriteria = DetachedCriteria.forClass(Quote.class, "inner")
    .add(Restrictions.eqProperty("inner.id","q1.id"))
    .setProjection(Projections.projectionList().add(Projections.max("inner.date")));

DetachedCriteria outerCriteria= DetachedCriteria.forClass(ClmClaim.class, "q1");
outerCriteria.add(Subqueries.propertyEq("q1.date", innerCriteria ));

生成されるSQLは次のようになります。

select
        this_.<Blah> as blah2_49_0_,
        this_.<Blah> as blah2_50_0_,
        this_.<Blah> as blah2_51_0_,

    from
        Quote this_ 
    where
         this_.date = (
            select
                max(inner_.date) as y0_ 
            from
                Quote inner_ 
            where
                inner_.claim_number=this_.claim_number
        );

必要がないため、SQLには「groupby」がないことに注意してください。副選択に複数の一致条件がある場合は、そこにあると思います。

とにかく、それが役立つことを願っています。それは私がクリスマスの前にやろうとしている最後のことです!

12
Mark O'Driscoll

同様のユースケースがあります。 Criteriaではできないと確信しています。 Hibernateはfrom句の結合をサポートしていません: https://hibernate.atlassian.net/browse/HHH-3356 (執筆時点ではまだ開いています)。

2013年のクリスマスの答えはかなり良いですが、残念ながら、相関サブクエリは私のユースケースでは本当に悪いです:

  • mySQLを使用する
  • iDごとに数千の日付が存在する可能性があります

しかし、これは問題ありません(とにかくMySQL 5.6.25の場合):

SELECT * FROM Quote q1
WHERE (q1.id, q1.date) IN 
(
    SELECT id, max(date)
    FROM Quote
    GROUP BY id, date
)

私は答えを探してここに来ました

DetachedCriteria maxDateQry = DetachedCriteria.forClass(Quote.class);
maxDateQry.setProjection(
    Projections.projectionList()
        .add(Projections.groupProperty("qid.id"))
        .add(Projections.max("qid.date", "maxdate"))
    );

Criteria criteria = session.createCriteria(Quote.class);
Object environmentIdAndStartedDateProperty = "(environmentId, startedDate)" // but not this, some sort of magic
criteria.add(
    Subqueries.in(environmentIdAndStartedDateProperty, maxDateQry));
List<Quote> quoteList = criteria.list();

しかし、そのような魔法は存在しないようです。 hqlをあきらめて使用する必要がありました。これは、Hibernate 4.2のドキュメントによると、Hibernate基準APIが非推奨になっているため、おそらく最善です。 https://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/apb.html

4
Don Willis