jpa条件组按聚合值

时间:2013-08-02 20:32:26

标签: mysql jpa group-by eclipselink criteria

我需要一个查询来将时间(milisec)转换为每小时分布。

我的表有一个名为timestamp的列:milisec中的值。 按小时分组,我需要做下面的事情 偏离1000 - > sec以3600为单位 - >小时 - > mod 24 - >得到记录创建的一天中的小时。

时间戳很长:

    public static volatile SingularAttribute<VersionJpa, Long> timestamp;

然后我可以在当天分发中显示结果。但是,这不起作用,该组不工作,我得到的完整列表没有分组,也没有计算。

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Tuple> cq = cb.createTupleQuery();
    Root<VersionJpa> root = cq.from(VersionJpa.class);
    cq.where(p);
    Expression<Integer> group = cb.mod(cb.toInteger(cb.quot(root.get(VersionJpa_.timestamp), 3600000)),24);
    cq.multiselect(group, cb.count(root));
    cq.groupBy(group);
    TypedQuery<Tuple> tq = em.createQuery(cq);
    return tq.getResultList();

这个SQL(mysql)生成正确的结果。在JPA中有相同的楼层吗? 试过。(整数)|| toInteger()

    select mod(floor(stamp/3600000), 24) , count(*) from versions group by mod(floor(stamp/3600000), 24);

我发现在单元测试期间(在Derby上)运行良好,但是在更多的生产中,例如setup(MySQL),它不会将最内部聚合转换为整数,因此该组是在浮点值上完成的,而不是在24(0,1,...... 23)潜在值(返回千组)。

    Expression<Integer> hours = cb.mod(cb.quot(root.get(VersionJpa_.timestamp).as(Integer.class), 3600000).as(Integer.class),24).as(Integer.class);
    cq.multiselect(hours, cb.count(root));
    cq.groupBy(hours);

添加PostgreSQL支持和测试导致另一个错误强制我认为我的查询错误:

    RuntimeException Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: org.postgresql.util.PSQLException: ERROR: column "versions.stamp" must appear in the GROUP BY clause or be used in an aggregate function Position: 13 Error Code: 0 Call: SELECT MOD((STAMP / ?), ?), COUNT(ID) FROM VERSIONS GROUP BY MOD((STAMP / ?), ?) LIMIT ? OFFSET ? bind => [6 parameters bound] Query: TupleQuery(referenceClass=VersionJpa sql="SELECT MOD((STAMP / ?), ?), COUNT(ID) FROM VERSIONS GROUP BY MOD((STAMP / ?), ?) LIMIT ? OFFSET ?")

感谢任何帮助。

2 个答案:

答案 0 :(得分:2)

您可以使用function()方法调用任何数据库函数。

请参阅, http://java-persistence-performance.blogspot.com/2012/05/jpql-vs-sql-have-both-with-eclipselink.html

Postgres似乎更抱怨在组中使用函数,在JPQL中你可以试试,

Select mod((v.timestamp / 3600000), 24) h, count(v) from Version v group by h

答案 1 :(得分:0)

对于任何有这个问题的人,我找到了一个基于数据库列而不是复杂查询的解决方法。提醒一下,问题是不保证不同的供应商支持。它适用于Derby但在MySQL上失败。

我根据我感兴趣的每个指标创建了一个新列:

  • 星期几
  • 日期
  • 每小时(24)

然后使用使用本地化设置和系统时间初始化的日历填充它。

    Calendar now = Calendar.getInstance();
    now.setTimeInMillis(timestamp);
    this.year = now.get(Calendar.YEAR);
    logger.trace("Year : [{}]", year);
    this.month = now.get(Calendar.MONTH);
    logger.trace("Month : [{}]", month);
    this.weekOfYear = now.get(Calendar.WEEK_OF_YEAR);
    logger.trace("Week of Year : [{}]", weekOfYear);
    this.dayOfMonth = now.get(Calendar.DAY_OF_MONTH);
    logger.trace("Day of Month : [{}]", dayOfMonth);
    this.dayOfWeek = now.get(Calendar.DAY_OF_WEEK);
    logger.trace("Day of Week : [{}]", dayOfWeek);
    this.hourOfDay = now.get(Calendar.HOUR_OF_DAY);
    logger.trace("Hour of Day : [{}]", hourOfDay);
希望这有助于某人。 欢呼声。