在Hibernate或JPA中`SELECT COUNT(*)FROM(SELECT DISTINCT ...)`

时间:2018-05-18 14:40:58

标签: java hibernate jpa select distinct

对于Hibernate或JPA而言,这感觉就像一个微不足道的用例,但我已经挣扎了几天才能让它发挥作用。

我有一个position实体类,其中包含latitudelongitudeupdateTime字段(以及其他字段)。我想计算这三个字段的不同组合的数量,而忽略其他字段。在SQL中,这是微不足道的:

SELECT COUNT(*) FROM (SELECT DISTINCT LONGITUDE, LATITUDE, UPDATE_TIME FROM POSITION) AS TEMP;

从我的应用程序的其余部分抽象myh数据库实现非常重要,因为不同的用户可能希望使用不同的数据库引擎。 (哎呀我用h2进行测试,mariadb用于本地制作......)

我一直在尝试使用Hibernate或JPA语法将此SQL转换为Java代码,但我无法弄清楚如何。

编辑 - 这与我能够使用JPA一样接近(参考:https://en.wikibooks.org/wiki/Java_Persistence/Criteria

public long getCountDistinctInFlightPositions() {
    Session session = sessionFactory.openSession();

    CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();

    CriteriaQuery<Tuple> innerQuery = criteriaBuilder.createTupleQuery();
    Root<Position> position = innerQuery.from(Position.class);
    innerQuery.multiselect(
        position.get("longitude"),
        position.get("latitude"),
        position.get("updateTime")
    );

    // The method countDistinct(Expression<?>) in the type CriteriaBuilder is not applicable for the arguments (CriteriaQuery<Tuple>)

    criteriaBuilder.countDistinct(innerQuery);

    return 1;
}

1 个答案:

答案 0 :(得分:1)

你可以这样做:

CriteriaQuery<Long> countQuery = cb.createQuery( Long.class );
Root<Position> root = countQuery.from( Position.class );

countQuery.select( cb.count( root.get( "id" ) ) );

Subquery<Integer> subQuery = countQuery.subquery( Integer.class );
Root<Position> subRoot = subQuery.from( Position.class );
subQuery.select( cb.min( subRoot.get( "id" ) ) );
subQuery.groupBy( subRoot.get( "longitude" ), 
  subRoot.get( "latitude" ), 
  subRoot.get( "updateTime" ) );

countQuery.where( root.get( "id" ).in( subQuery ) );

Long count = entityManager.createQuery( countQuery ).getSingleResult();

这有效地生成以下SQL:

SELECT COUNT( p0.id ) FROM Position p0
 WHERE p0.id IN (
   SELECT MIN( p1.id )
     FROM Position p1
    GROUP BY p1.longitude, p1.latitude, p1.updateTime )

在我有3行且其中2行具有相同的经度,纬度和更新时间元组的情况下,查询将返回2的结果。

确保在[Longtitude,Latitude,UpdateTime]上保持良好的索引,以便您可以利用更快的GROUP BY执行。 PK已经被b-tree索引,因此其他操作的COUNT / MIN应该很容易被该索引解释。