SQL:从历史表中获取最新条目

时间:2011-12-13 20:41:35

标签: mysql sql group-by

我有3张桌子

person (id, name)
area (id, number)
history (id, person_id, area_id, type, datetime)

在此表中,我存储了某个人在特定时间具有哪个区域的信息。这就像一个推销员在一个地区旅行了一段时间,然后他得到了另一个区域。他一次也可以有多个区域。

历史类型='I'用于CheckIn或'O'用于Checkout。 例如:

id    person_id    area_id    type    datetime
1     2            5          'O'     '2011-12-01' 
2     2            5          'I'     '2011-12-31' 
A person started traveling in area 5 at 2011-12-01 and gave it back on 2011-12-31.

现在我想列出所有人现在拥有的所有领域。

person1.name, area1.number, area2.number, area6.name
person2.name, area5.number, area9.number
....

输出也可能是这样(没关系):

person1.name, area1.number
person1.name, area2.number
person1.name, area6.number
person2.name, area5.number
....

我该怎么做?

3 个答案:

答案 0 :(得分:1)

这个问题确实非常棘手。您需要一个历史记录中的条目列表,对于给定的用户和区域,存在一个没有后续“I”记录的“O”记录。仅使用历史表,转换为:

SELECT ho.person_id, ho.area_id, ho.type, MAX(ho.datetime)
  FROM History AS ho
 WHERE ho.type = 'O'
   AND NOT EXISTS(SELECT *
                    FROM History AS hi
                   WHERE hi.person_id = ho.person_id
                     AND hi.area_id   = ho.area_id
                     AND hi.type = 'I'
                     AND hi.datetime > ho.datetime
                 )
 GROUP BY ho.person_id, ho.area_id, ho.type;

然后,因为你真的只是在这个人的名字和地区的号码之后(虽然为什么区号不能和它的ID相同我不确定),你需要稍微适应,加入额外的两张桌子:

SELECT p.name, a.number
  FROM History AS ho
  JOIN Person  AS p  ON ho.person_id = p.id
  JOIN Area    AS a  ON ho.area_id   = a.id
 WHERE ho.type = 'O'
   AND NOT EXISTS(SELECT *
                    FROM History AS hi
                   WHERE hi.person_id = ho.person_id
                     AND hi.area_id   = ho.area_id
                     AND hi.type = 'I'
                     AND hi.datetime > ho.datetime
                 );

NOT EXISTS子句是一个相关的子查询;这往往效率低下。您可以使用适当的连接和过滤条件将其重铸为LEFT OUTER JOIN:

SELECT p.name, a.number
  FROM History AS ho
  JOIN Person  AS p  ON ho.person_id = p.id
  JOIN Area    AS a  ON ho.area_id   = a.id
  LEFT OUTER JOIN History AS hi
    ON hi.person_id = ho.person_id
   AND hi.area_id   = ho.area_id
   AND hi.type = 'I'
   AND hi.datetime > ho.datetime
 WHERE ho.type = 'O'
   AND hi.person_id IS NULL;

所有SQL未经验证。

答案 1 :(得分:0)

您正在寻找每行可能有不同列数的结果吗?我想你可能想看看GROUP_CONCAT()

SELECT p.`id`, GROUP_CONCAT(a.`number`, ',') AS `areas` FROM `person` a LEFT JOIN `history` h ON h.`person_id` = p.`id` LEFT JOIN `area` a ON a.`id` = h.`area_id`

我没有测试过这个查询,但之前我以类似的方式使用了group concat。当然,您需要根据自己的需要量身定制。当然,group concat将返回一个字符串,因此需要进行后期处理才能使用数据。

编辑自从我开始回复以来,我的问题已被编辑。我的查询不再适合您的请求了......

答案 2 :(得分:0)

试试这个:

select *
from person p
inner join history h on h.person_id = p.id
left outer join history h2 on h2.person_id = p.id and h2.area_id = h.area_id and h2.type = 'O'
inner join areas on a.id = h.area_id
where h2.person_id is null and h.type = 'I'