MySQL: 从最近的组中获取最旧的记录

MySQL: get oldest record from most recent group

抱歉标题令人困惑,但这是解释它的最佳方式。这不是一个常见的 "most recent from group" 问题,我在网上找不到任何类似的问题。

我有一个状态 table 可以跟踪人们在不同工作地点所做的事情。它包含 link 人、状态和位置的记录。

ID, start_date, person_ID, location_ID, status
1,  2014-10-12, 1,         1,           job a
2,  2014-10-13, 2,         2,           job b
3,  2014-10-15, 1,         3,           job c
4,  2014-10-21, 1,         3,           job d
5,  2014-10-22, 2,         4,           job a
6,  2014-10-26, 2,         2,           job d

我需要能够确定每个人在当前站点停留了多长时间 - 我希望得到这样的结果:

person_ID, location_ID, since
1,         3,           2014-10-15
2,         2,           2014-10-26

通过加入 max(start_date),获取当前工作的开始时间相对容易,但我需要从最近位置完成的工作中获取 min(start_date)。

我一直在尝试将 min(start_date) 加入与当前位置匹配的记录中(来自最近的记录),并且在我找到一个人(比如人 2)之前效果很好谁多次访问当前位置...您可以在我想要的结果中看到我想要 10-26 日期,而不是 10-13,这是他们第一次访问该站点。

我需要一些方法来匹配给定人员的工作记录,然后迭代返回直到位置不匹配。我想必须有一些方法可以通过一些 sub-queries 和一些巧妙的连接来做到这一点,但我还没有找到它,所以我会很感激一些帮助。

我认为最简单的方法是使用变量来跟踪您需要的信息:

select person_id, location_id, min(start_date) as since
from (select s.*,
             (@rn := if(@p <> person_id, if(@p:=person_id, 1, 1),
                        if(@l = location_id, @rn,
                           if(@l:=location_d, @rn + 1, @rn + 1)
                          )
                        )
             ) as location_counter
      from status s cross join
           (select @p := 0, @l := 0, @rn := 0) vars
      order by person_id, start_date desc
     ) s
where location_counter = 1
group by person_id, location_id;

变量的奇怪逻辑是(试图)枚举每个人的位置。它应该仅在位置更改时递增 @rn 并为新人将值重置为 1

其实很简单

SELECT g.person_ID,
  (SELECT l.location_ID
   FROM status l
   WHERE l.person_ID = g.person_ID
   AND l.start_date = MAX(g.start_date)) AS location,
  MAX(g.start_date) AS since
FROM status g
GROUP BY g.person_ID

这使用 person_ID 上的分组,并使用 SELECT 作为位置列表达式。

唯一的问题是你的意思是不是 MIN i.o。 MAX 在你的例子中你产生了最年轻的日期,而不是最老的。

如果我正确理解您的问题,您可以使用 EXISTS 消除除每个人最近的位置之外的所有位置,并从结果行中获取最短日期。

SELECT person_id, location_id, MIN(start_date) since
FROM status s
WHERE NOT EXISTS (
  SELECT 1 FROM status 
  WHERE s.person_id = person_id 
    AND s.location_id <> location_id
    AND s.start_date < start_date)
GROUP BY person_id

An SQLfiddle to test with.

基本上,它消除了同一个人最近访问过另一个位置的所有位置和时间。例如;

1,  2014-10-12, 1,         1,           job a

...被淘汰,因为人 1 最近访问了位置 3,而;

3,  2014-10-15, 1,         3,           job c

...保留,因为同一个人最近才访问过同一地点。

然后它只选择每个人最近最少的时间。由于只保留最后一个位置的行,因此它将是距离最近位置最近的时间。