2016-07-29 59 views
0

我有一个简单的表,其中包含一个'reader'对象的状态历史记录。该状态可以是ACTIVE或INACTIVE。现在我想要查询一下,它会挑选所有读者,这些读者在给定的时期内处于ACTIVE状态。下图显示了我的意思:在显示的时间段内,带有绿色“生命线”的'读者'处于活动状态;所以它们将在查询中返回。在给定的时间段内选择状态历史表的活动状态

enter image description here

的TABEL ReaderActivity看起来像这样(仅作为示例,而不是上面的匹配图像):

reader_id | state | timestamp 
1  | ACTIVE | 1467331201089 
2  | ACTIVE | 1467332454545 
1  | INACTIVE | 1467348875254 
3  | ACTIVE | 1467350416546 
1  | ACTIVE | 1467351871123 
2  | INACTIVE | 1467352111545 

当然,有一个Reader实体向这个reader_id被映射到哪个。

我需要对此有一个JPA查询,但是我有问题,包括读者进入结果,它没有'上升沿'(状态从INACTIVE变为ACTIVE)我们期待的期间)。

我看基本上也查询的部分:

  • 首先,选择所有具有上升边缘。这很简单:SELECT DISTINCT a.reader FROM ReaderActivity a WHERE a.timestamp >= :startTimestamp AND a.timestamp < :endTimestamp AND a.state = 'ACTIVE'
  • 其次,它应该追加到所有读者上面的列表中,它在期间开始之前具有最后状态ACTIVE。这是我没有正确地得到:我想(合并成拳头查询):SELECT DISTINCT a.reader FROM ReaderActivity a WHERE a.timestamp >= :startTimestamp AND a.timestamp < :endTimestamp AND a.state = 'ACTIVE' or (SELECT aa.reader FROM ReaderActivity aa where aa.reader = a.reader AND aa.timestamp < :startTimestamp AND aa.state = 'ACTIVE' GROUP BY aa.reader ORDER BY max(aa.timestamp)) != null

这将返回也是红色标记的图像中的阅读器上面,因为在子查询中AND aa.state = 'ACTIVE',我们已经筛选出所有ACTIVE状态,所以最后一个当然是ACTIVE。我应该选择开始时间段之前的最后一个状态,然后检查是否为ACTIVE。但是如何?

任何人都可以让我以正确的方式吗? 在此先感谢!

回答

1

查询的第一部分将带回其状态在该时间段内根据需要更改为活动状态的记录。查询的第二部分需要一些改进。

要做到你的要求,你将不得不带回最近的状态变化,每reader_id直到“startTimeStamp”的时刻。从这些,你会简单地选择当时列为“ACTIVE”的那些。

喜欢的东西..

Select a.reader_id 
From ReaderActivity a 
Where a.timeStamp in 
( 
    Select max(b.timeStamp) 
    From ReaderActivity b 
    Where b.timestamp < :startTimeStamp 
    And b.reader_id = a.reader_id 
    Group By b.reader_id 
) 
and a.state = "ACTIVE" 

这个查询将返回所有[reader_id]的那名活跃在指定的时间周期的开始。您可以在原始查询的where子句中运行此操作,也可以使用UNION将其附加到查询的末尾,并在整个混乱中运行Select Distinct(reader_id)。

从表中某个组的最大值中选择对我来说总是有点古怪。但我希望这个查询有帮助!

+0

非常感谢卢克!我错过了使用'... where in()...'[[]]的可能性 - 我的完整查询现在是:'SELECT DISTINCT a.reader FROM ReaderActivity a WHERE a.timestamp> =:startTs AND a.timestamp <:endTs AND a.state ='ACTIVE'OR(a.timestamp IN(SELECT MAX(b.timestamp)FROM ReaderActivity b WHERE b.timestamp <:startTs GROUP BY b.r​​eader)AND a.state ='ACTIVE') ' - 一个问题:为什么你在最后的方括号中加上'state',('...和a。[state] = ...')? – badera

+1

我们不能解决您的重要提示*唯一需要注意的是,如果由于某种原因,reader_id'a'在毫秒级被设置为“INACTIVE”... *通过添加...在子选择的where子句中where where b.reader_id = a.reader_id'? – badera

+0

为了回应你的第一个问题,从状态中删除括号不会改变查询,也不会在那里。当列由空格分隔的单词组成时,需要在列周围使用括号将一列分组在一起。出于某种原因,我读状态为两个单词(不知道如何)。回答你的第二个问题,是的!在a.reader_id = b.reader_id上添加where过滤器将防止带回'INACTIVE'记录。我会改变查询来反映这一点。 @badera – Luke

1

这个查询应该这样做:

SELECT r.reader_id, r.timestamp 
FROM ReaderActivity r 
WHERE r.timestamp>=:start AND 
     r.timestamp<=:stop AND 
     r.state='ACTIVE' 

UNION ALL 

SELECT r1.reader_id, r1.timestamp 
FROM ReaderActivity r1 
LEFT JOIN ReaderActivity r2 ON (r2.reader_id = r1.reader_id AND 
           r2.timestamp<:start AND 
           r2.timestamp>r1.timestamp) 
WHERE r1.timestamp<:start AND 
     r1.state='ACTIVE' AND 
     r2.id IS NULL 

UNION的最前一页部分选择时间内激活的和联合的第二部分只选择该时间段之前激活读者的读者。

我没有测试过这个查询,你可能需要稍微调整一下。

+0

谢谢你,Victorqedu! [+1]抱歉,我没有将您的答案标记为“已接受”;但来自路加的那个人有点不爽。 – badera

+0

按照时间戳选择并不安全 - 正如卢克在答复结束时提到的那样。 – Victorqedu

+0

为什么不,如果子查询的筛选器仅限于父项的输入?这在技术上是不可能的,两个*阅读器*产生具有相同时间戳的多个条目。我认为直接在相应的答案上讨论这个问题会很有趣! – badera