SQL是下一个最近的记录

时间:2014-04-07 13:56:45

标签: mysql sql record next

我有表act_detail:

+------+------+--------+
| id   | name | action |
+------+------+--------+
|    1 | Tom  | eat    |
|    2 | Jack | eat    |
|    3 | Tom  | play   |
|    4 | Tom  | sleep  |
|    5 | Tom  | eat    |
|    6 | Jack | sleep  |
|    7 | Tom  | sleep  |
|    8 | Tom  | eat    |
+------+------+--------+

我希望得到“吃”的总结和下一个最近的“睡眠”信息同名:

+------+--------+----------+
| name | eat_id | sleep_id |
+------+--------+----------+
| Tom  |      1 |        4 |
| Jack |      2 |        6 |
| Tom  |      5 |        7 |
| Tom  |      8 |     NULL |
+------+--------+----------+

我发现我可以使用下面的SQL获得结果:

SELECT 
    a.name, 
    a.id AS eat_id, 
    (SELECT MIN(id) FROM act_detail b WHERE a.name = b.name AND b.id > a.id AND b.action = 'sleep') AS sleep_id 
FROM act_detail a 
WHERE a.action = 'eat'
ORDER BY a.id;

但是当需要在表b中获得更多列时,此SQL需要子查询并需要更多子查询。有很多记录会很慢 假设我们可以添加任何索引。 是否有任何有效的方法来解决标准SQL的问题(可能是一个左连接,一个临时表和一个按语句分组)?

3 个答案:

答案 0 :(得分:1)

首先获取所有进食动作和所有睡眠动作。加入两者,以便名字匹配,并在进食后发生睡眠。然后找到最小距离并添加该距离。

select eat.name, eat.id as eat_id, eat.id + min(sleep.id - eat.id) as sleep_id
from
(
  select id 
  from act_detail
  where action = 'eat'
) eat
left join
(
  select id 
  from act_detail
  where action = 'sleep'
) sleep on sleep.name = eat.name and sleep.id > eat.id
group by eat.name, eat.id;

答案 1 :(得分:1)

没有子查询: -

SELECT a.name, a.id AS eat_id, MIN(b.id) AS sleep_id
FROM act_detail a
LEFT OUTER JOIN act_detail b 
ON a.name = b.name
AND b.action = 'sleep'
AND b.id > a.id
WHERE a.action = 'eat'
GROUP BY a.name, eat_id
ORDER BY a.id;

SQL在这里小提琴: -

http://www.sqlfiddle.com/#!2/11834/2

答案 2 :(得分:0)

尝试稍微更改以避免聚合函数(每个记录扫描多个子查询记录):

SELECT 
    a.name, 
    a.id AS eat_id, 
    (SELECT b.id FROM act_detail b
     WHERE b.action = 'sleep'
       AND b.name = a.name
       AND b.id > a.id
     ORDER BY b.id
     LIMIT 1) AS sleep_id 
FROM act_detail a 
WHERE a.action = 'eat'
ORDER BY a.id;

通过以下"覆盖"指数:

act_detail(action, name, id)

如果这是InnoDB且id是您的主键,那么您可以从上面的索引中删除id