我有表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的问题(可能是一个左连接,一个临时表和一个按语句分组)?
答案 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在这里小提琴: -
答案 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
。