有条件地GROUP BY不同的列

时间:2015-10-06 08:07:15

标签: mysql sql

我想显示聊天消息列表。

为此,我有一个SELECT声明:

SELECT id, `from`, sent, message, recd FROM chat 
WHERE id IN(
  SELECT MAX(id) FROM chat
  WHERE `to` = ? GROUP BY `from`
)
ORDER BY id DESC

问题是,我想有条件地选择to =玛丽亚和from =玛丽亚的邮件。

也就是说,如果to =玛丽亚我想按from分组,如果from =玛丽亚,我想按to进行分组。

如何动态更改此GROUP BY

4 个答案:

答案 0 :(得分:2)

I would got for a UNION.

(SELECT id, 'from' as direction, m_from AS fromto, sent, message, recd
FROM chat WHERE id IN (SELECT MAX(id) FROM chat WHERE to = 'Maria' GROUP BY m_from))
UNION
(SELECT id, 'to' as direction, m_to AS fromto, sent, message, recd FROM chat
WHERE id IN (SELECT MAX(id) FROM chat WHERE m_from = 'Maria' GROUP BY to))

If you select MAX(id) an ORDER BY id statement is not necessary.

You can add extra fields in the SELECT clause as I did with 'direction'.

答案 1 :(得分:2)

Use a CASE construct for conditional grouping:

SELECT id, `from`, sent, message, recd 
FROM chat 
WHERE id IN
(
  SELECT MAX(id) FROM chat
  WHERE 'Maria' in (`to`, `from`)
  GROUP BY CASE WHEN 'Maria' = `to` THEN `from` ELSE `to` END
)
ORDER BY id DESC;

答案 2 :(得分:1)

I think all the proposed queries are fine - meaning they will return the expected results (one row for every different user that has sent or received a message from 'maria') - but efficiency may not be great. MySQL does not optimize well queries with WHERE column IN (complex subquery). The optimizer has some improvements in 5.6 and 5.7 version but still it's much better to help it in getting a better plan and using indexes.

Your question and comments are confusing though. it's not clear if you want one row per different user (recipient, sender) or just one row in the results.

If you want the second, my suggestion is to add two indexes, on (to) and (from) if you haven't already (I assume that the table is InnoDB and the primary key is (id) so the (to) and (from) indexes are equivalent to (to, id) and (from, id) indexes, which are what you really need for the query.)

and use the following query:

  ( SELECT id, `from`, `to`, sent, message, recd 
    FROM chat 
    WHERE `to` = ?
    ORDER BY id DESC
    LIMIT 1
  )
UNION ALL
  ( SELECT id, `from`, `to`, sent, message, recd 
    FROM chat 
    WHERE `from` = ?
    ORDER BY id DESC
    LIMIT 1
  )
ORDER BY id DESC
LIMIT 1 ;

which is equivalent (thank you @Thorsten Kettner) to:

SELECT id, `from`, `to`, sent, message, recd 
FROM chat 
WHERE ? IN (`to`, `from`) 
ORDER BY id DESC 
LIMIT 1 ;

Try this last version, too, and if it is equally efficient, prefer it. There is no reason to complicate the query, unless the efficiency is not good. With the given indexes, this last version will often use both of them, and the index merge union algorithm.

答案 3 :(得分:0)

Use a CASE within the GROUP BY

SELECT id, `from`, sent, message, recd 
FROM chat 
WHERE id IN(SELECT MAX(id) 
            FROM chat
            WHERE `to` = ? OR `from` = ?
            GROUP BY CASE 
                     WHEN `to` = ? THEN  GROUP BY `from`
                     ELSE `from` = ? THEN  GROUP BY `to`
                     END)
ORDER BY id DESC