2014-04-07 76 views
0

我有表act_detail:SQL下一個最近的記錄

+------+------+--------+ 
| 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; 

但這SQL需要子查詢,需要更多的子查詢時需要得到更多的列在表b中。記錄很多,速度會很慢。
假設我們可以添加任何索引。 是否有任何有效的方法來解決這個問題與標準的SQL(也許一個左連接,一個臨時表和一個組聲明)?

+0

,它不僅是一個子查詢,讓你運行,這是一個相關子查詢,父查詢中每行的子查詢。但是沒有真正的解決辦法,因爲你的「更高的sleep_id」要求。 –

+0

什麼是'很多記錄'?請記住:計算機的速度通常對我們來說足夠快。對於我們來說,對於計算機來說常常是一點點。 – giorgio

+0

好像你可以在你的子查詢中使用'ORDER BY id'和'LIMIT 1',然後擺脫'MIN()'。正確的索引當然會有所幫助。 –

回答

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

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; 
+0

是的,謝謝。我已經添加了它。 –

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