我正在尝试根据最新的ID和日期获取数据的子集。似乎在选择表格中的其他字段时,它们与最大ID和返回的日期不同步。
知道如何解决这个问题吗?
MySQL的:
SELECT MAX(m.id) as id, m.sender_id, m.receiver_id, MAX(m.date) as date, m.content, l.username, p.gender
FROM messages m
LEFT JOIN login_users l on l.user_id = m.sender_id
LEFT JOIN profiles p ON p.user_id = l.user_id
WHERE m.receiver_id=3
GROUP BY m.sender_id ORDER BY date DESC LIMIT 0, 7
内容数据不正确。它似乎是返回随机内容,而不是与最大ID和最大日期相关联的内容。
我是否需要进行某种子选择来解决这个问题?
答案 0 :(得分:2)
要回答标题中的问题,“为什么我的内容字段与我的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”。
注意强>
在大多数情况下,由于访问计划不同,我们发现JOIN (query)
的效果优于IN (query)
。您可以使用EXPLAIN查看计划的差异。
为了提高性能,你需要一个索引
... ON messages (`receiver_id`, `sender_id`, `id`, `date`)
在receiver_id上有一个等式谓词,因此应该是前导列,以获得范围扫描(而不是完整扫描)。您希望接下来是sender_id
列,因为这应该允许MySQL避免“使用filesort”操作来获取分组的行。包含id
和date
列,因此可以完全从索引页面满足内联视图查询,而无需访问表中的页面。 (EXPLAIN应显示“Using where; Using index
”。)
同样的索引也应该适用于外部查询,尽管它确实需要从表页面访问“content
”列,因此EXPLAIN不会为该步骤显示“使用索引”。 (“content
”列很可能比我们在索引中想要的长得多。)
答案 1 :(得分:0)
嗯,你可能可能在没有子选择的情况下解决它,但做一个是相当直接的。这样的东西应该可以工作,只需让subselect返回消息中感兴趣的行的id,然后只获取它们的数据。
SELECT m.id as id, m.sender_id, m.receiver_id, m.date as date,
m.content, l.username, p.gender
FROM messages m
LEFT JOIN login_users l on l.user_id = m.sender_id
LEFT JOIN profiles p ON p.user_id = l.user_id
WHERE m.id IN (
SELECT max(id) FROM messages
WHERE receiver_id=3
GROUP BY sender_id
)
ORDER BY date DESC
LIMIT 0, 7
原始查询与字段不匹配的原因是GROUP BY
确实需要应用于您选择的每个字段的聚合函数(如MAX / MIN / SUM / ...)通过。查询甚至运行的原因是MySQL没有强制执行,而是从匹配的任何行返回不确定的字段。 Afaik,所有其他SQL RDBMS'拒绝运行查询。
编辑:至于性能,一些可能有帮助的索引是;
CREATE INDEX ix_inner ON messages(receiver_id, sender_id, id);
CREATE INDEX ix_login_users ON login_users(user_id);
CREATE INDEX ix_profiles ON profiles(user_id);
答案 2 :(得分:0)
使用联接
SELECT LatestM.id, m.sender_id, m.receiver_id, m.date, m.content, l.username, p.gender
(
SELECT sender_id, MAX(id) AS id
FROM messages
WHERE receiver_id=3
GROUP BY sender_id
) LatestM
INNER JOIN messages m
ON LatestM.sender_id = m.sender_id AND LatestM.id = m.id
LEFT JOIN login_users l on l.user_id = m.sender_id
LEFT JOIN profiles p ON p.user_id = l.user_id
WHERE m.receiver_id = 3
ORDER BY date DESC
LIMIT 0, 7
问题是如果最新的ID没有反映最新的日期,那么返回的日期将不是最新的日期。