【问题标题】:Select active state of state history table in a given period选择给定时间段内状态历史表的活动状态
【发布时间】:2016-12-04 06:42:18
【问题描述】:

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

表格ReaderActivity 如下所示(仅作为示例,与上图不匹配):

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

当然,这个reader_id 映射到一个Reader 实体。

我需要对此进行 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',我们已经过滤掉了所有的活动状态,所以最后一个当然是活动的。我应该选择开始时间之前的最后一个状态,然后检查它是否处于活动状态。但是怎么做呢?

谁能带我走正确的路? 提前致谢!

【问题讨论】:

    标签: java database postgresql jpa


    【解决方案1】:

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

    要按照您的要求执行操作,您将必须恢复每个 reader_id 的最新状态更改,直到“startTimeStamp”时刻。然后,您只需从中选择当时列为“活动”的那些。

    有点像..

    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)。

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

    【讨论】:

    • 非常感谢卢克!我错过了使用...where in () ... [+1] 的可能性 - 我的完整查询现在是:SELECT DISTINCT a.reader FROM ReaderActivity a WHERE a.timestamp &gt;= :startTs AND a.timestamp &lt; :endTs AND a.state = 'ACTIVE' OR (a.timestamp IN (SELECT MAX(b.timestamp) FROM ReaderActivity b WHERE b.timestamp &lt; :startTs GROUP BY b.reader) AND a.state = 'ACTIVE') - 一个问题:你为什么将state 括在最后的方括号和 (... and a.[state] = ...) 中?
    • 我们不能解决你的重要提示 这个查询唯一需要注意的是,如果由于某种原因 reader_id 'a' 在精确的毫秒内被设置为“INACTIVE” ... 通过在子选择的 where 子句中添加 ...where b.reader_id = a.reader_id
    • 在回答您的第一个问题时,从状态中删除括号不会改变查询,也不会有它们。当一列由空格分隔的单词组成时,一列周围需要括号以将一列组合在一起。出于某种原因,我将状态理解为两个词(不确定如何)。回答你的第二个问题,是的!在 a.reader_id = b.reader_id 上添加 where 过滤器将阻止带回“不活跃”记录。我将更改查询以反映这一点。 @badera
    【解决方案2】:

    这个查询应该这样做:

    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 的第一部分选择在该时间段内激活的阅读器,而 UNION 的第二部分仅选择在该时间段之前激活的阅读器。

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

    【讨论】:

    • 也谢谢你,Victorqedu! [+1] 抱歉,我没有将您的答案标记为“已接受”;但是卢克的那个有点儿好。
    • 按时间戳选择是不安全的 - 正如卢克在他的回答末尾提到的那样。
    • 为什么不呢,如果子查询的过滤器仅限于父select的入口呢?从技术上讲,两个 readers 生成多个具有相同时间戳的条目是不可能的。我认为最好直接在相应的答案上讨论这个问题!
    • 你是对的,但这不是唯一的问题。据我所知,加入子查询不如表联接有效,而 IN 不如 = 有效。我可能错了,但我看不出其他解决方案有什么更好的地方。
    • 对我来说,另一个查询更加透明和方便。老实说,我不完全明白,你在做什么。这就是为什么我觉得另一个答案是 bether (是的,这是由于缺乏知识......)如果你做出一些解释,你可以改变它,你做什么:) - 就速度/资源使用而言担心,你可能是对的 - 如果我曾经有大型数据集,我可以观察它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    • 2012-08-05
    • 2012-07-27
    相关资源
    最近更新 更多