2

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

我的表有一个名为时间戳的列:以毫秒为单位的值。要按小时分组,我需要按 1000 -> 秒按 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 ?")

任何帮助表示赞赏。

4

2 回答 2

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
于 2013-08-07T13:13:53.860 回答
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);

希望这可以帮助某人。干杯。

于 2013-08-09T18:25:23.597 回答