为什么我的内容字段与MySQL中的MAX(id)字段不匹配?

时间:2013-07-21 20:41:04

标签: mysql sql

我正在尝试根据最新的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和最大日期相关联的内容。

我是否需要进行某种子选择来解决这个问题?

3 个答案:

答案 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”操作来获取分组的行。包含iddate列,因此可以完全从索引页面满足内联视图查询,而无需访问表中的页面。 (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没有反映最新的日期,那么返回的日期将不是最新的日期。