2014-01-14 44 views
1

我有一個包含用戶到用戶消息的表。對話具有兩個用戶之間的所有消息。我正在嘗試獲取所有不同會話的列表,並只顯示列表中發送的最後一條消息。SQL中替代子查詢的替代方法

我可以用FROM中的SQL子查詢來做到這一點。

CREATE TABLE `messages` (
`id` bigint(20) NOT NULL AUTO_INCREMENT, 
`from_user_id` bigint(20) DEFAULT NULL, 
`to_user_id` bigint(20) DEFAULT NULL, 
`type` smallint(6) NOT NULL, 
`is_read` tinyint(1) NOT NULL, 
`is_deleted` tinyint(1) NOT NULL, 
`text` longtext COLLATE utf8_unicode_ci NOT NULL, 
`heading` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
`created_at_utc` datetime DEFAULT NULL, 
`read_at_utc` datetime DEFAULT NULL, 
PRIMARY KEY (`id`) 
); 


SELECT * FROM 
(SELECT * FROM `messages` WHERE TYPE = 1 AND 
(from_user_id = 22 OR to_user_id = 22) 
ORDER BY created_at_utc DESC 
) tb 
GROUP BY from_user_id, to_user_id; 

SQL小提琴:
http://www.sqlfiddle.com/#!2/845275/2

有沒有辦法做到這一點沒有一個子查詢?
(寫DQL僅支持在「IN」的子查詢)

+1

不,這需要一個子查詢。 –

+0

子查詢有什麼問題? – JohnFx

回答

1

你似乎在試圖獲取消息的最後內容,或從用戶22型= 1。你的方法是明確不能保證工作,因爲額外的列(而不是在group by)可以來自任意行。作爲[文件]中的解釋[1]:

MySQL的擴展使用GROUP BY的,這樣的選擇列表可參考 在GROUP BY子句中未命名的非聚合列。這意味着 前面的查詢在MySQL中是合法的。您可以使用此功能 以避免不必要的列排序和 分組以獲得更好的性能。但是,這對於每個 組中未在GROUP BY中指定的每個 非聚合列中的所有值都相同時都很有用。服務器可以自由選擇每組中的任何值,因此除非它們相同,否則所選的值是不確定的。 此外,每個組的值的選擇不能受到添加ORDER BY子句影響的 的影響。結果集 的排序發生在選擇了值之後,並且ORDER BY不影響 服務器選擇的每個組內的值。

您要查詢是沿此線以上(假設你有一個自動遞增idmessages):

select m.* 
from (select m.from_user_id, m.to_user_id, max(m.id) as max_id 
     from message m 
     where m.type = 1 and (m.from_user_id = 22 or m.to_user_id = 22) 
    ) lm join 
    messages m 
    on lm.max_id = m.id; 

或者這樣:

select m.* 
from message m 
where m.type = 1 and (m.from_user_id = 22 or m.to_user_id = 22) and 
     not exists (select 1 
        from messages m2 
        where m2.type = m.type and m2.from_user_id = m.from_user_id and 
         m2.to_user_id = m.to_user_id and 
         m2.created_at_utc > m.created_at_utc 
       ); 

對於後者查詢,messages(type, from_user_id, to_user_id, created_at_utc)上的索引將有助於提高性能。

+0

p.s. DQL支持使用「不存在」的第二個示例。 – nod

0

我不認爲你原來的查詢是正確的,即使這樣做。不知道GROUP BY被用於除了可能只嘗試返回單一(不可預知)的結果之外。

只需添加一個限制條款:

SELECT * FROM `messages` 
WHERE `type` = 1 AND 
(`from_user_id` = 22 OR `to_user_id` = 22) 
ORDER BY `created_at_utc` DESC 
LIMIT 1 

對於需要在以下領域指標最優的查詢性能:

type 
from_user_id 
to_user_id 
created_at_utc 
+0

結果與op不同。 – xdazz

+0

他希望*所有*會話和*他們所有*最後的消息。 –

1

由於這是去外面的公共數據查詢的相當具體的類型ORM使用案例,DQL並不適合這一點 - 它針對行之有效的關係進行了優化。

但是,對於您的情況,原則完全支持native SQL with result set mapping。使用NativeQueryResultSetMapping這樣你可以很容易地使用這個問題需要的子查詢,並且仍然在本地Doctrine實體上映射結果,使您仍然可以從所有緩存,可用性和性能優勢中獲益。

Samples found here

1

如果你的意思是要獲得所有的談話和他們最後的消息,那麼子查詢是必要的。

SELECT a.* FROM messages a 
INNER JOIN (
    SELECT 
     MAX(created_at_utc) as max_created, 
     from_user_id, 
     to_user_id 
    FROM messages 
    GROUP BY from_user_id, to_user_id 
) b ON a.created_at_utc = b.max_created 
    AND a.from_user_id = b.from_user_id 
    AND a.to_user_id = b.to_user_id 

而且您可以隨意追加where條件。

THE SQL FIDDLE.