如何对多列进行countDistinct



我如何使用JPA标准API来执行以下操作:

select count(distinct column1, column2) from table

使用CriteriaBuilder.countDistinct在一列/路径上执行此操作很简单,但如何在两个路径/列上执行此任务?

这是一个迟到的答案:-),但我不确定情况是否发生了变化。

最近,我遇到了同样的需求,并使用concat解决了这个问题,即通过将列连接到伪列中,然后在伪列上使用countDistinct

但我不能使用criteriaBuilder.concat,因为它使用||生成JPQL进行连接,而Hibernate在这方面遇到了麻烦。

幸运的是,有@Formula,因此,我将伪列映射到具有@Formula:的字段

@Entity
public class MyEntity {
  @Column(name="col_a")
  private String colA;
  @Column(name="col_b")
  private String colB;
  @Formula("concat(col_a, col_b)") // <= THE TRICK
  private String concated;
}

通过这种方式,我最终可以将concated字段用于CriteriaBuilder.countDistinct:

//...
Expression<?> exp = criteriaBuilder.countDistinct(entity.get("concated"));
criteriaQuery.select(exp);
TypedQuery<Long> query = entityManager.createQuery(criteriaQuery);
return query.getSingleResult();

我希望JPA能够(或者希望已经)支持具有多个列的countDistinct,那么所有这些混乱都可以避免。

似乎没有办法用JPA2 来做到这一点

您可以使用hibernate方言执行此任务。要做到这一点,请创建您自己的方言,它扩展了DB使用的方言(所有方言的列表),然后注册新函数。例如,我使用MySQL 5和InnoDB引擎:

public final class MyDialect extends MySQL5InnoDBDialect {
    public MyDialect() {
        super();
        registerFunction("pairCountDistinct", new SQLFunctionTemplate(LongType.INSTANCE, "count(distinct ?1, ?2)"));
    }
}

然后在persistence.xml中添加新属性:

<property name="hibernate.dialect" value="com.example.dialect.MyDialect" />

现在你可以使用这个功能:

// some init actions
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Long> criteria = builder.createQuery(Long.class);
final Root<SomeEntity> root = criteria.from(SomeEntity.class);
criteria.select(builder.function("pairCountDistinct", Long.class, root.get(SomeEntity_.field1), root.get(SomeEntity_.field2)));
final long result = entityManager.createQuery(criteria).getSingleResult();
// some close actions

正如其他人所指出的,使用concat方法构建一个由所有所需列组成的表达式,然后您可以将该表达式传递给countDistinct方法。

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<MyEntity> root = cb.from(MyEntity.class);
// build up an expression with multiple columns
Expression exp = root.get("column1");
exp = cb.concat(exp, root.get("column2");
exp = cb.concat(exp, root.get("column3");
cq.select(cb.contDistinct(exp));
var result = em.createQuery(cq).getSingleResult();
var count = Math.toIntExact(result);

最新更新