要回答标题中的问题,“为什么我的内容字段与我的 MAX(id) 字段不匹配”,这是因为不能保证为非聚合字段返回的值将来自 MAX 值所在的行被发现。这是记录在案的行为,也是我们所期望的。
其他 DBMS 会在语句上抛出错误,MySQL 只是更宽松,并且您从一行获取值,但不能保证是找到 MAX 值(id 或 date)中的任何一个的行。
您有两个单独的聚合表达式MAX(m.id)
和MAX(m.date)
. 请注意,不能保证这些值将来自同一行。
其他数据库中的规则是 SELECT 列表中的每个非聚合表达式都需要出现在 GROUP BY 中。(MySQL 对此更加宽松,并且没有要求。)
“修复”查询以使其确实从具有 MAX 值的行返回值的一种方法是使用内联视图(查询),该视图MAX(id)
按您想要 GROUP BY 的内容进行分组,然后将 JOIN 返回到原始表以获取该行的其他值。
从您的陈述中不清楚您想要返回什么结果集。如果您想要具有最大 id 的行并且您还想要具有最大日期的行,那么您可以这样:
SELECT m.id
, m.sender_id
, m.receiver_id
, m.date
, m.content
, l.username
, p.gender
FROM ( SELECT t.sender_id
, t.receiver_id
, MAX(t.id) AS max_id
, MAX(t.date) AS max_date
FROM messages t
WHERE t.receiver_id=3
GROUP
BY t.sender_id
, t.receiver_id
) s
JOIN messages m
ON m.sender_id = s.sender_id
AND m.receiver_id = s.receiver_id
AND ( m.id = s.max_id OR m.date = s.max_date)
LEFT
JOIN login_users l on l.user_id = m.sender_id
LEFT
JOIN profiles p ON p.user_id = l.user_id
ORDER BY m.date DESC LIMIT 0, 7
别名为“s”的内联视图返回最大值,然后将其连接回消息表,别名为“m”。
笔记
在大多数情况下,我们发现 aJOIN (query)
会比 a 执行得更好IN (query)
,因为访问计划不同。您可以通过 EXPLAIN 查看计划中的差异。
为了性能,您需要一个索引
... ON messages (`receiver_id`, `sender_id`, `id`, `date`)
在receiver_id 上有一个相等谓词,所以它应该是前导列,以获得范围扫描(而不是完整扫描)。您需要sender_id
下一个列,因为这应该允许 MySQL 避免“使用文件排序”操作来对行进行分组。包括id
和date
列,因此可以完全从索引页面满足内联视图查询,而无需访问表中的页面。(解释应该显示“ Using where; Using index
”。)
相同的索引也应该适用于外部查询,尽管它确实需要content
从表页面访问“”列,因此 EXPLAIN 不会显示该步骤的“使用索引”。(很可能“ content
”列比我们在索引中想要的要长得多。)