我有一个包含用户到用户消息的表。对话包含两个用户之间的所有消息。我正在尝试获取所有不同会话的列表,并仅显示列表中发送的最后一条消息。
我可以使用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
如果没有子查询,有没有办法做到这一点? (编写仅在'IN'中支持子查询的DQL)
答案 0 :(得分:1)
由于这是一种非常特殊的数据查询类型,它超出了常见的ORM用例,因此DQL并不适合这种情况 - 它针对行走良好定义的关系进行了优化。
但是对于你的情况,Doctrine完全支持native SQL with result set mapping。使用NativeQuery
和ResultSetMapping
这样的问题,您可以轻松使用此问题所需的子查询,并仍然将结果映射到本机Doctrine实体上,从而使您仍然可以从所有缓存,可用性和性能优势中获益。 / p>
答案 1 :(得分: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. 强>
答案 2 :(得分:1)
您似乎正在尝试使用type = 1从用户22获取消息的最后内容。您的方法明确无法保证可以正常工作,因为额外的列(不在group by
中)可以来从任意行。如[文档] [1]中所述:
MySQL扩展了GROUP BY的使用,以便选择列表可以引用 未在GROUP BY子句中命名的非聚合列。这意味着 前面的查询在MySQL中是合法的。您可以使用此功能 通过避免不必要的列排序来获得更好的性能 分组。但是,这主要适用于每个中的所有值 GROUP BY中未命名的非聚合列对于每个列都是相同的 组。服务器可以自由选择每个组中的任何值,所以 除非它们相同,否则所选择的值是不确定的。 此外,不能从每个组中选择值 受添加ORDER BY子句的影响。对结果集进行排序 选择值后发生,ORDER BY不影响 服务器选择的每个组中的值。
您想要的查询更符合这一点(假设您有id
的自动递增messages
列:
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)
上的索引有助于提高性能。
答案 3 :(得分: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