QueryDsl 按星期几聚合为地图
QueryDsl aggregate by day of week as map
我想按星期几按类型获取预订数量,例如:
| DoW | Type | Count |
|----- |--------|-------|
| 1 | Store | 3 |
| 1 | Remove | 2 |
结果应该是 Map<Integer, Map<String, Long>>
。我已经尝试了一些变体:
LocalDateTime monday = LocalDate.now().with(DayOfWeek.MONDAY).atStartOfDay();
LocalDateTime nextMonday = monday.plusDays(7);
var dayOfWeek = booking.bookingDate.dayOfWeek().subtract(1);
new JPAQuery<>(entityManager)
.select(dayOfWeek, booking.bookingType, booking.bookingType.count())
.from(booking)
.where(booking.bookingDate.between(monday, nextMonday))
.groupBy(booking.bookingDate, booking.bookingType)
.transform(groupBy(dayOfWeek)
.as(map(booking.bookingType, booking.bookingType.count())));
虽然它没有给我正确的计数(一切都是 1)。我考虑过将 dayofweek
放在 groupby 子句中,bc。我担心按 LocalDateTime 分组时,“时间”部分会把事情搞砸,但这没有用。
此 postgresql 查询按预期工作:
select
extract(DOW from b.book_date)-1 as dow,
b.dtype as type,
count(b.dtype) as cnt
from
booking b
where
b.book_date between '2020-05-11 0:0:0.000000' and '2020-05-18 0:0:0.000000'
group by
b.book_date,
b.dtype
编辑:
将“dayOfWeek”添加到 groupBy 子句时,如下所示:
.groupBy(dayOfWeek, buchung.buchungType)
然后我得到一个异常:
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
[...]
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Feld
"BOOKING0_.BOOKING_DATE" muss in der GROUP BY Liste sein
Column "BOOKING0_.BOOKING_DATE" must be in the GROUP BY list; SQL statement:
/* select dayofweek(booking.gebuchtDate) - ?1, booking.bookingType, count(booking.bookingType)
from Buchung booking
where booking.bookingDate between ?2 and ?3
group by dayofweek(booking.gebuchtDate) - ?1, booking.bookingType */ select dayofweek(booking0_.booking_date)-? as col_0_0_, booking0_.dtype as col_1_0_, count(booking0_.dtype) as col_2_0_ from booking booking0_ where booking0_.booking_date between ? and ? group by dayofweek(booking0_.booking_date)-? , booking0_.dtype [90016-200]
编辑 2:
Jan-Willem Gmelig Meyling 在下面的回答修复了查询,但我将星期几从 Sun-Sat 转换为 Mon-Sun 的逻辑当然是垃圾。一起来看看:
Postgresql will return Sunday (0) to Saturday (6) (which seems to be the ISO SQL way), whereas java.time returns ISO-8601 标准格式从星期一 (1) 到星期日 (7)。所以基本上一切都匹配,除了星期天。
更糟糕的是,我了解到您可以在 MS SQL.
上更改一周的开始日期
由于我不想为这种头痛而烦恼,我已将其更改为一年中的某一天,并且我正在 Java 中进行转换。由于结果集最多有 7 个条目,所以这还不错:
var dayOfYear = booking.bookingDate.dayOfYear();
var res = new JPAQuery<>(entityManager)
.from(booking)
.where(booking.bookingDate.between(monday, nextMonday))
.groupBy(dayOfYear, booking.bookingType)
.transform(groupBy(dayOfYear)
.as(map(booking.bookingType, booking.bookingType.count())));
return res.entrySet().stream()
.collect(toMap(
e -> Year.now().atDay(e.getKey()).getDayOfWeek().getValue(),
Map.Entry::getValue
));
“1”已参数化,因此在查询执行计划时,group by 表达式与 select 表达式不“相等”。你必须为 1 使用常量。即.subtact(Expressions.numberTemplate(Integer.class, "1"))
.
此外,我认为 QueryDSL 将 DayOfWeek 添加 1 以使其与 Java 的星期几而不是 ANSI SQL 中定义的 DayOfWeek 一致。您可以考虑使用 JPQLTemplates
的自定义实例,它为 DAY_OF_WEEK
运算符定义不同的标准模板(并在那里呈现常量)。这可能是您应用程序中更安全的默认设置。
或者,当使用 Hibernate 5.4.10 或更新版本时,您还可以使用“按别名分组”:在 select 表达式中使用 dayOfWeek.as("dayOfWeekAlias")
,然后在中使用 Expressions.numberPath(Integer.class, "dayOfWeekAlias")
你的分组条款:
new JPAQuery<>(entityManager)
.from(booking)
.where(booking.bookingDate.between(monday, nextMonday))
.groupBy(Expressions.numberPath(Integer.class, "dayOfWeekAlias"), booking.bookingType)
.transform(groupBy(dayOfWeek).as("dayOfWeekAlias")
.as(map(booking.bookingType, booking.bookingType.count())));
我想按星期几按类型获取预订数量,例如:
| DoW | Type | Count |
|----- |--------|-------|
| 1 | Store | 3 |
| 1 | Remove | 2 |
结果应该是 Map<Integer, Map<String, Long>>
。我已经尝试了一些变体:
LocalDateTime monday = LocalDate.now().with(DayOfWeek.MONDAY).atStartOfDay();
LocalDateTime nextMonday = monday.plusDays(7);
var dayOfWeek = booking.bookingDate.dayOfWeek().subtract(1);
new JPAQuery<>(entityManager)
.select(dayOfWeek, booking.bookingType, booking.bookingType.count())
.from(booking)
.where(booking.bookingDate.between(monday, nextMonday))
.groupBy(booking.bookingDate, booking.bookingType)
.transform(groupBy(dayOfWeek)
.as(map(booking.bookingType, booking.bookingType.count())));
虽然它没有给我正确的计数(一切都是 1)。我考虑过将 dayofweek
放在 groupby 子句中,bc。我担心按 LocalDateTime 分组时,“时间”部分会把事情搞砸,但这没有用。
此 postgresql 查询按预期工作:
select
extract(DOW from b.book_date)-1 as dow,
b.dtype as type,
count(b.dtype) as cnt
from
booking b
where
b.book_date between '2020-05-11 0:0:0.000000' and '2020-05-18 0:0:0.000000'
group by
b.book_date,
b.dtype
编辑:
将“dayOfWeek”添加到 groupBy 子句时,如下所示:
.groupBy(dayOfWeek, buchung.buchungType)
然后我得到一个异常:
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
[...]
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Feld
"BOOKING0_.BOOKING_DATE" muss in der GROUP BY Liste sein
Column "BOOKING0_.BOOKING_DATE" must be in the GROUP BY list; SQL statement:
/* select dayofweek(booking.gebuchtDate) - ?1, booking.bookingType, count(booking.bookingType)
from Buchung booking
where booking.bookingDate between ?2 and ?3
group by dayofweek(booking.gebuchtDate) - ?1, booking.bookingType */ select dayofweek(booking0_.booking_date)-? as col_0_0_, booking0_.dtype as col_1_0_, count(booking0_.dtype) as col_2_0_ from booking booking0_ where booking0_.booking_date between ? and ? group by dayofweek(booking0_.booking_date)-? , booking0_.dtype [90016-200]
编辑 2:
Jan-Willem Gmelig Meyling 在下面的回答修复了查询,但我将星期几从 Sun-Sat 转换为 Mon-Sun 的逻辑当然是垃圾。一起来看看:
Postgresql will return Sunday (0) to Saturday (6) (which seems to be the ISO SQL way), whereas java.time returns ISO-8601 标准格式从星期一 (1) 到星期日 (7)。所以基本上一切都匹配,除了星期天。
更糟糕的是,我了解到您可以在 MS SQL.
由于我不想为这种头痛而烦恼,我已将其更改为一年中的某一天,并且我正在 Java 中进行转换。由于结果集最多有 7 个条目,所以这还不错:
var dayOfYear = booking.bookingDate.dayOfYear();
var res = new JPAQuery<>(entityManager)
.from(booking)
.where(booking.bookingDate.between(monday, nextMonday))
.groupBy(dayOfYear, booking.bookingType)
.transform(groupBy(dayOfYear)
.as(map(booking.bookingType, booking.bookingType.count())));
return res.entrySet().stream()
.collect(toMap(
e -> Year.now().atDay(e.getKey()).getDayOfWeek().getValue(),
Map.Entry::getValue
));
“1”已参数化,因此在查询执行计划时,group by 表达式与 select 表达式不“相等”。你必须为 1 使用常量。即.subtact(Expressions.numberTemplate(Integer.class, "1"))
.
此外,我认为 QueryDSL 将 DayOfWeek 添加 1 以使其与 Java 的星期几而不是 ANSI SQL 中定义的 DayOfWeek 一致。您可以考虑使用 JPQLTemplates
的自定义实例,它为 DAY_OF_WEEK
运算符定义不同的标准模板(并在那里呈现常量)。这可能是您应用程序中更安全的默认设置。
或者,当使用 Hibernate 5.4.10 或更新版本时,您还可以使用“按别名分组”:在 select 表达式中使用 dayOfWeek.as("dayOfWeekAlias")
,然后在中使用 Expressions.numberPath(Integer.class, "dayOfWeekAlias")
你的分组条款:
new JPAQuery<>(entityManager)
.from(booking)
.where(booking.bookingDate.between(monday, nextMonday))
.groupBy(Expressions.numberPath(Integer.class, "dayOfWeekAlias"), booking.bookingType)
.transform(groupBy(dayOfWeek).as("dayOfWeekAlias")
.as(map(booking.bookingType, booking.bookingType.count())));