2013-06-19 138 views
0

設置選擇從多個表中唯一的記錄在MySQL

我有兩個表的數據庫。其中一個用於收件箱郵件(inbox),另一個用於發件箱郵件(outbox)。他們都有相同的結構和一些記錄。

+------------------------------------------------------------+ 
| inbox              | 
+------------------------------------------------------------+ 
| messageID | from | to | messageText | timestamp  | 
+------------------------------------------------------------+ 
| 1   | userA | userB | sometext | 2013-06-19 10:30 | 
| 2   | userB | userC | sometext | 2013-06-19 10:40 | 
| 3   | userC | userA | sometext | 2013-06-19 10:50 | 
+------------------------------------------------------------+ 

+------------------------------------------------------------+ 
| outbox              | 
+------------------------------------------------------------+ 
| messageID | from | to | messageText | timestamp  | 
+------------------------------------------------------------+ 
| 1   | userA | userC | sometext | 2013-06-19 10:20 | 
| 2   | userC | userB | sometext | 2013-06-19 10:30 | 
| 3   | userB | userA | sometext | 2013-06-19 10:35 | 
+------------------------------------------------------------+ 

問題

我需要從一個特定用戶的每次談話每一個最後的消息(在這種情況下 - 我想要從每一個最後的消息在用戶A的對話中,無論消息是哪個表 - 收件箱或發件箱。我已成功地選擇從收件箱中每個會話的最後一條消息,並與下面的查詢發件箱:所以現在

SELECT * FROM 
(
    SELECT from, to, timestamp, messageText, messageID 
    FROM inbox 
    WHERE to = 'userA' 
    ORDER BY timestamp DESC 
) 
AS tmp_table GROUP BY from 
UNION 
SELECT * FROM 
(
    SELECT from, to, timestamp, messageText, messageID 
    FROM outbox 
    WHERE from = 'userA' 
    ORDER BY timestamp DESC 
) 
AS tmp_table GROUP BY to 
ORDER BY timestamp DESC 

我需要檢查哪些消息是新的 - 一個在收件箱或一個發件箱中(如果消息兩個用戶之間的這兩個表中都存在),並且只返回最新的消息。

或者,也許我的做法是徹頭徹尾的愚蠢 - 請評論:D謝謝。

+0

老實說,沒有理由將數據存儲在兩個單獨的表中。 – Jessica

+0

我同意@Jessica,一張桌子和擔心。嘗試添加'限制1' – amigura

+0

@Jessica - 是的,我現在 - 我肯定會使用不同的結構,但可惜我堅持這一個,因爲我不允許「客戶端」修改它。 – August

回答

1

根據什麼指標都建立在你的表有可能寫一個執行速度更快的查詢,但這應該工作:

select 
    sq.main_user, 
    sq.other_user, 
    coalesce(i.messageText, o.messageText) messageText 
from 
    (select 
     main_user, other_user, max(msg_time) last_msg 
    from 
     (select 
      `from` main_user, `to` other_user, `timestamp` msg_time 
     from outbox 
     where `from` = 'userA' 
     union all 
     select 
      `to` main_user, `from` other_user, `timestamp` msg_time 
     from inbox 
     where `to` = 'userA') uq 
     group by main_user, other_user) sq 
left join 
    outbox o on sq.main_user = o.`from` and sq.last_msg = o.`timestamp` 
left join 
    inbox i on sq.main_user = i.`to` and sq.last_msg = i.`timestamp` 

(從最裏面的子查詢,如果你想省略了where條款查看所有用戶的對話,而不僅僅是userA。)

+0

謝謝,像魅力一樣工作。可悲的是我自己從來沒有寫過這樣的東西。如果你也可以解釋這裏發生了什麼,因爲我不知道它是如何工作的:D – August

+0

@八卦:最內層的查詢(別名爲'uq')選擇所有來自'userA'的消息'timestamp', 'userA'作爲'main_user',另一個用戶作爲(意外的)'other_user'。中間子查詢(別名爲'sq')總結了'uq'的結果,以便爲'main_user'和'other_user'的每個組合獲得最大的(即最近的)'timestamp'。然後,主查詢可以從'main_user'的'sq'和最近的'timestamp'連接到收件箱和發件箱表 - 對於'main_user'和'other_user'的每個組合,只有兩個表中的一個應該匹配記錄。 –

+0

@八月:'coalesce'確保'messageText'是從哪個表的記錄中填入最近的消息中選擇的。 –

2

從每個表剛剛獲得前1記錄,那麼工會結果:

SELECT 'outbox', * 
FROM outbox 
WHERE `from` = 'userA' 
ORDER BY timestamp DESC 
LIMIT 1 

UNION ALL 

SELECT 'inbox', * 
FROM inbox 
WHERE `from` = 'userA' 
ORDER BY timestamp DESC 
LIMIT 1 

注:

1)注意周圍from反引號。 FROM是在太陽下的幾乎所有的sql數據庫中的保留字,所以如果沒有語法錯誤的禁止,你的查詢將無法工作
2)注意兩個子查詢中的硬編碼'inbox''outbox'字符串 - 他們在那裏告訴你發現的記錄來自哪個表。

+0

你確定這會工作嗎?現在看來,查詢會從每個表中選擇一行。而且可能有成千上萬的我需要的行(userA可以與數千個用戶進行對話)。 – August

+0

它將抓取所有userA是'from'人員的消息,但只返回最近的消息,因爲它的排序日期遞減,並且只返回第一行(限制1)。所以這個查詢最多隻會返回兩行。一個來自收件箱,另一個來自發件箱。 –

+0

對不起,但我認爲我的問題並不清楚。我想檢索用戶A擁有的所有對話的最新消息 - 我。即如果收件箱中有一條記錄,其中的時間戳爲2013-06-01,並且發件箱中的記錄位於from = userA並且時間戳爲2013-06-19的時間戳爲= userB,那麼查詢應該返回後面的記錄。如果有更多的對話(userA和userC) - 那麼查詢也應該返回這個對話中的最後一條消息。 – August