简体   繁体   English

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

[英]SQL: Get latest entries from history table

I have 3 tables 我有3张桌子

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

In this tables I store the info which person had which area at a specific time. 在此表中,我存储了在特定时间哪个人拥有哪个区域的信息。 It is like a salesman travels in an area for a while and then he gets another area. 就像推销员在某个区域旅行了一段时间,然后又到达另一个区域。 He can also have multiple areas at a time. 他也可以一次拥有多个区域。

history type = 'I' for CheckIn or 'O' for Checkout. 历史记录类型=“ I”表示签入,“ O”表示签出。 Example: 例:

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.

Now I want to have a list of all the areas all persons have right now. 现在,我想列出所有人现在拥有的所有领域的清单。

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

The output could be like this too (it doesn't matter): 输出也可能是这样(没关系):

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

How can I do that? 我怎样才能做到这一点?

This question is, indeed, quite tricky. 这个问题确实很棘手。 You need a list of the entries in history where, for a given user and area, there is an 'O' record with no subsequent 'I' record. 您需要历史记录中的条目列表,对于给定的用户和区域,其中存在一个“ O”记录,没有后续的“ I”记录。 Working with just the history table, that translates to: 仅使用历史记录表,可以将其转换为:

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;

Then, since you're really only after the person's name and the area's number (though why the area number can't be the same as its ID I am not sure), you need to adapt slightly, joining with the extra two tables: 然后,由于您实际上只需要输入此人的姓名和区域编号(尽管我不确定为什么区域号不能与其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
                 );

The NOT EXISTS clause is a correlated sub-query; NOT EXISTS子句是一个相关的子查询; that tends to be inefficient. 往往效率低下。 You might be able to recast it as a LEFT OUTER JOIN with appropriate join and filter conditions: 您也许可以使用适当的联接和过滤条件将其重铸为左外联接:

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;

All SQL unverified. 所有SQL未经验证。

You're looking for results where each row may have a different number of columns? 您正在寻找每行可能具有不同列数的结果吗? I think you may want to look into GROUP_CONCAT() 我认为您可能想研究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`

I haven't tested this query, but I have used group concat in similar ways before. 我没有测试过该查询,但是之前我以类似的方式使用了group concat。 Naturally, you will want to tailor this to fit your needs. 自然,您将需要定制此功能以满足您的需求。 Of course, group concat will return a string so it will require post processing to use the data. 当然,组concat将返回一个字符串,因此需要后期处理才能使用该数据。

EDIT I thikn your question has been edited since I began responding. 编辑自您开始回答以来,您的问题已被编辑。 My query does not really fit your request anymore... 我的查询不再符合您的要求...

Try this: 尝试这个:

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'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM