dig*_*oel 22 java sql jpa jpql jpa-2.0
假设我有一个会议实体.每次会议都有一位与会者和一个会面日期.在我的会议桌内,我可能会为每位与会者举行多次会议,每次会议的日期各不相同.我需要一个JPA查询,它只为所有与会者选择最新的会议.例如,如果我的表看起来像这样
Meeting ID | Attendee ID | Meeting Date
1 | 1 | 6/1/2011
2 | 2 | 6/1/2011
3 | 1 | 6/6/2011
4 | 3 | 6/6/2011
Run Code Online (Sandbox Code Playgroud)
我的结果应该是
Meeting ID | Attendee ID | Meeting Date
2 | 2 | 6/1/2011
3 | 1 | 6/6/2011
4 | 3 | 6/6/2011
Run Code Online (Sandbox Code Playgroud)
使用JPA 2对抗postgres.会议有1-1参加者和一个简单的时间戳日期.我怀疑我需要做一个小组by和max(blah),也许是一个加入我自己,但我不确定最好的方法来解决这个问题.
更新: 在晚上玩这个之后,我仍然没有可接受的JPQL解决方案.这是我到目前为止:
select m from Meeting m
where m.meetingDate in
( select max(meet.meetingDate)
from Meeting meet group by meet.attendee )
Run Code Online (Sandbox Code Playgroud)
我有其他与此问题无关的其他条件,例如由与会部门过滤等等.这有效的唯一原因是因为我们将会议日期跟踪到第二(或更精细),并且在同一时间召开两次会议的可能性很小.我们正在为它们添加一些Java内容,以便只为每个与会者提供最后一次会议,以防我们同时获得两个会议,但这是一个非常糟糕的解决方案.在查询中获取所有内容真的不应该太难,但我还没弄明白.
Update2:添加sql标签,因为如果我需要使用sql创建一个视图并创建一个JPA对象来映射到视图我就可以了.
小智 15
在SQL中,解决方案非常简单 - 将表与子查询连接起来,为每个与会者提供最近的会议:
select * from Meeting ALL
join ( select max(meetingDate) as newest, attendee
from Meeting group by attendee ) LATEST
on ALL.meetingDate = LATEST.newest AND ALL.attendee = LATEST.attendee
Run Code Online (Sandbox Code Playgroud)
这工作,并且工作得很快!
JPA的问题在于它(或大多数实现)不允许连接的子查询.花了几个小时尝试了首先编译的东西,然后,它有多慢,我决定我讨厌JPA.像上面那样的解决方案 - 比如EXISTS(SELECT ..)或IN(SELECT ..) - 需要花费很长时间才能执行,比它们要慢几个数量级.
拥有一个有效的解决方案意味着我只需要从JPA访问该解决方案.SQL中有两个神奇的单词可以帮助您做到这一点:
CREATE VIEW
Run Code Online (Sandbox Code Playgroud)
生活变得如此简单......只需定义这样的实体并使用它.注意:它是只读的.
当然,任何JPA纯粹主义者都会在你这样做时瞧不起你,所以如果有人有纯粹的JPA解决方案,请让我们都知道!
dig*_*oel 14
我想我已经得到了这个查询.
select m from Meeting m
where m.meetingDate =
(select max(m1.meetingDate)
from Meeting m1
where m1.attendee = m.attendee )
and not exists
(select m2 from Meeting m2
where m2.attendee = m.attendee
and m2.meetingDate > m.meetingDate)
Run Code Online (Sandbox Code Playgroud)
在SQL中,我认为这很简单,所以我假设可以映射到JPA:
SELECT m.AttendeeId, MAX(m.MeetingDate) from Meeting m GROUP BY m.AttendeeId
Run Code Online (Sandbox Code Playgroud)
编辑:如果您还需要messageId本身,您可以使用一个简单的子查询来执行此操作,该子查询返回其他两个值相等的消息的messageId.只要确保你处理同一个参与者和日期有多个messageId的情况(例如选择第一个结果,因为他们都应该同样好 - 尽管我怀疑这些数据甚至对会议有意义)
正如Bulba所说,适当的方式是加入子查询与group by.
问题是您无法加入子查询.
这是一个解决方法.
让我们看看你在子查询中得到的是什么.你得到一对配对清单(attendee_id, max(meeting_date))
.这一对就像是一个新的唯一ID,用于您希望加入的最大日期.然后请注意表中的每一行都形成一对(attendee_id, meeting_date)
.所以每一行都有一对id (attendee_id, meeting_date)
.如果只有它形成属于子查询中收到的列表的id,则让我们连续.
为简单起见,我们将此id对表示为attendee_id
和的连接meeting_date
:concat(attendee_id, meeting_date)
.
然后SQL中的查询(类似于JPQL和JPA CriteriaBuilder)将如下所示:
SELECT * FROM meetings
WHERE concat(attendee_id, meeting_date) IN
(SELECT concat(attendee_id, max(meeting_date)) FROM meetings GROUP BY attendee_id)
Run Code Online (Sandbox Code Playgroud)
请注意,每个查询只有一个子查询,而不是像某些答案中的每行一个子查询.
我们为您提供特别优惠!
让我们将id-pair编码为数字.这将是的总和attendee_id
,并meeting_date
而是修改,以确保代码的唯一性.我们可以将日期的数字表示作为Unix时间.我们将修复代码可以捕获的最大日期值,因为最终代码具有最大值限制(例如bigint(int8)<2 63).让我们方便最大日期为2149-06-07 03:00:00.它等于5662310400秒,65536天.我将在这里假设我们需要精确的日期(因此我们忽略小时和以下).为了构造唯一代码,我们可以将其解释为数字系统中的数字,其基数为65536.这样的数字系统中的最后一个符号(数字从0到2 16 -1)或代码是天数.其他符号将被捕获attendee_id
.在这样的解释代码看起来XXXX
,其中每个X在范围[0,2 16 -1](更准确,第一个X在范围[0,2 15 -1],因为1位用于符号),前三个X代表attendee_id
,最后X代表meeting_date
.因此attendee_id
我们的代码可以捕获的最大值是2 47 -1.代码可以计算为attendee_id
*65536 +"以天为单位的日期".
在postgresql中它将是:
attendee_id*65536 + date_part('epoch', meeting_date)/(60*60*24)
Run Code Online (Sandbox Code Playgroud)
凡date_part
在此我们通过把在不断转换为天秒返回的日期.
最后查询以获得所有与会者的最新会议:
SELECT * FROM meetings
WHERE attendee_id*65536 + date_part('epoch', meeting_date)/(60*60*24)
IN (SELECT attendee_id*65536 + date_part('epoch', max(meeting_date))/(60*60*24) from meetings GROUP BY attendee_id);
Run Code Online (Sandbox Code Playgroud)
我已经在问题中创建了一个带有结构的表,并使用attendee_id
从[1,10000]中随机选择的100000行和来自范围[1970-01-01,2017-09-16]的随机日期填充它.我使用以下技术对(使用EXPLAIN ANALYZE)查询进行了基准测试:
相关子查询
SELECT * FROM meetings m1 WHERE m1.meeting_date=
(SELECT max(m2.meeting_date) FROM meetings m2 WHERE m2.attendee_id=m1.attendee_id);
Run Code Online (Sandbox Code Playgroud)
执行时间:873260.878 ms
使用group by加入子查询
SELECT * FROM meetings m
JOIN (SELECT attendee_id, max(meeting_date) from meetings GROUP BY attendee_id) attendee_max_date
ON attendee_max_date.attendee_id = m.attendee_id;</code>
Run Code Online (Sandbox Code Playgroud)
执行时间:103.427毫秒
使用pair (attendee_id, date)
作为键
Concat attendee_id
和meeting_date
作为字符串
SELECT * FROM meetings WHERE concat(attendee_id, meeting_date) IN
(SELECT concat(attendee_id, max(meeting_date)) from meetings GROUP BY attendee_id);
Run Code Online (Sandbox Code Playgroud)
执行时间:207.720毫秒
编码attendee_id
和meeting_date
单个数字(代码)
SELECT * FROM meetings
WHERE attendee_id*65536 + date_part('epoch',meeting_date)/(60*60*24)
IN (SELECT attendee_id*65536 + date_part('epoch',max(meeting_date))/(60*60*24) from meetings GROUP BY attendee_id);
Run Code Online (Sandbox Code Playgroud)
执行时间:127.595毫秒
这是一个带有表格方案的git,表格数据(作为csv),用于填充表格的代码和查询.
归档时间: |
|
查看次数: |
28564 次 |
最近记录: |