如何正确排除引用彼此的行?

时间:2013-12-08 10:17:17

标签: mysql sql query-optimization

这个问题是基于一个不是很微不足道的问题How to remove two duplicate column。我已经就这个问题提出了解决方案,但我认为有一些比我更合适和优雅的解决方案。

有一些包含msg_idfromto列的私信消息。
我们有这些数据:

msg_id from   to
----------------
1      46     0
2      46     18
3      46     50
4      46     39
5      46     11  
6      11     46
7      46     12
8      46     56
9      46     11 

我们需要排除包含对话的行,其中有多条消息(例如包含msg_id = 569的行),同时我们需要将这些行中的第一行留在输出中。通常,输出应该是这样的(注意:没有msg_id = 6msg_id = 9):

msg_id from   to
----------------
1      46     0
2      46     18
3      46     50
4      46     39
5      46     11  
7      46     12
8      46     56 

我的解决方案是:

select distinct pm.`from`, pm.`to`
from `tsk_private_message` pm
left join
    (select distinct pm.`from`, pm.`to`
     from `tsk_private_message` pm
     inner join `tsk_private_message` pm2
     on (pm.`to` = pm2.`from`) and (pm2.`to` <> pm.`from`)) a
     using (`from`, `to`)
where a.`from` is null;

我只是通过子查询在这些对话中搜索不必要的行,并从主表中“减去”结果。你怎么看?有更优雅,更简单的解决方案吗?我真的不喜欢这个棘手的代码。

这是SQL Fiddle

2 个答案:

答案 0 :(得分:1)

SELECT mx.msg_id, pm.ffrom, pm.tto
FROM tsk_private_message pm
WHERE NOT EXISTS (
    SELECT * FROM tsk_private_message nx1
    WHERE nx1.ffrom = pm.ffrom AND nx1.tto = pm.tto
    AND nx1.msg_id < pm.msg_id
    )
AND NOT EXISTS (
    SELECT * FROM tsk_private_message nx2
    WHERE nx2.ffrom = pm.tto AND nx2.tto = pm.ffrom
    AND nx2.msg_id < pm.msg_id
    );

注意:我将tofrom列重命名为ttoffrom,因为来自两者都是SQL中的关键字,我不喜欢引用标识符。

额外:sqlfiddle (courtesy of Alexander Myshov)

答案 1 :(得分:1)

SELECT *
FROM
  tsk_private_message INNER JOIN (
    SELECT MIN(id) min_id
    FROM tsk_private_message
    GROUP BY
      LEAST(`from`, `to`),
      GREATEST(`from`, `to`)) min_msg
  ON tsk_private_message.id = min_msg.min_id
ORDER BY
  id

请参阅小提琴here