MySQL使用UNION从子查询中选择最新记录

时间:2015-04-18 13:08:39

标签: mysql join group-by union

我有下表

+--------+-----------+---------+----------------------------+---------------------+--------------------+
| msg_id | user_from | user_to |          msg_text          |      msg_time       |      msg_read      |
+--------+-----------+---------+----------------------------+---------------------+--------------------+
|      1 |         1 |      72 | Hello Mark from Andy       | 2014-09-18 12:44:09 | 2014-09-20 12:44:09|
|      2 |        72 |       1 | Hello Andy from Mark       | 2014-09-22 12:45:26 | 2014-09-28 12:45:26| 
|      3 |         1 |      72 | Back to you Mark from Andy | 2014-10-18 12:46:01 |                    |
|      4 |     12388 |       1 | Hello Andy from Graham     | 2014-09-20 12:45:37 | 2014-09-20 12:46:37|
|      5 |         1 |   12388 | Hello Graham from Andy     | 2014-09-20 12:51:08 |                    |
|      6 |       106 |       1 | Hello Andy from Carol      | 2015-04-18 12:47:04 |                    |
+--------+-----------+---------+----------------------------+---------------------+--------------------+

由于SQLFiddle目前处于关闭状态,这是查询。

-- ----------------------------
-- Table structure for `messages`
-- ----------------------------
DROP TABLE IF EXISTS `messages`;
CREATE TABLE `messages` (
  `msg_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_from` int(11) NOT NULL,
  `user_to` int(11) NOT NULL,
  `msg_text` text,
  `msg_time` datetime DEFAULT NULL,
  `msg_read` datetime DEFAULT NULL,
  PRIMARY KEY (`msg_id`),
  KEY `IX_MESSAGES` (`user_from`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;

-- ----------------------------
-- Records of messages
-- ----------------------------
INSERT INTO `messages` VALUES ('1', '1', '72', 'Hello Mark from Andy', '2014-09-18 12:44:09', '2014-09-20 12:44:09');
INSERT INTO `messages` VALUES ('2', '72', '1', 'Hello Andy from Mark', '2014-09-22 12:45:26', '2014-09-28 12:45:26');
INSERT INTO `messages` VALUES ('3', '1', '72', 'Back to you Mark from Andy', '2014-10-18 12:46:01', null);
INSERT INTO `messages` VALUES ('4', '12388', '1', 'Hello Andy from Graham', '2014-09-20 12:45:37', '2014-09-20 12:46:37');
INSERT INTO `messages` VALUES ('5', '1', '12388', 'Hello Graham from Andy', '2014-09-20 12:51:08', null);
INSERT INTO `messages` VALUES ('6', '106', '1', 'Hello Andy from Carol', '2015-04-18 12:47:04', null);

正如您可能已经猜到的,这是一个消息传递系统。为了在Facebook风格的收件箱中显示,我想提取一个结果集,该结果集显示特定用户与之通信的不同用户,但我还想包括最新消息以及消息时间以及是否已读取,请记住,最新消息可能来自发件人或收件人。

获得不同的用户非常容易。我只是按如下方式使用UNION:

SELECT
    t.user_id,
    t.msg_read,
    t.msg_time,
    t.msg_text
FROM
    (
        (
            SELECT
                m.user_from AS user_id,
                m.msg_time,
                m.msg_text,
                m.msg_read
            FROM
                messages m
            WHERE
                m.user_to = 1
        )
        UNION
            (
                SELECT
                    m.user_to AS user_id,
                    m.msg_time AS msg_time,
                    m.msg_text,
                    m.msg_read
                FROM
                    messages m
                WHERE
                    m.user_from = 1
            )
    ) t
GROUP BY user_id

这会产生:

+---------+--------------------+---------------------+------------------------+
| user_id |      msg_read      |      msg_time       |        msg_text        |
+---------+--------------------+---------------------+------------------------+
|      72 | 2014-09-28 12:45:26| 2014-09-22 12:45:26 | Hello Andy from Mark   |
|     106 |                    | 2015-04-18 12:47:04 | Hello Andy from Carol  |
|   12388 | 2014-09-20 12:46:37| 2014-09-20 12:45:37 | Hello Andy from Graham |
+---------+--------------------+---------------------+------------------------+

获取最新消息虽然证明是棘手的。在过去,我只是将JOIN用于另一个子查询,但在尝试对此进行相同操作时,它(当然)无法识别t表。

SELECT
    t.user_id,
    t.msg_read,
    t.msg_time AS msg_time,
    t.msg_text
FROM
    (
        (
            SELECT
                m.user_from AS user_id,
                m.msg_time AS msg_time,
                m.msg_text,
                m.msg_read
            FROM
                messages m
            WHERE
                m.user_to = 1
        )
        UNION
            (
                SELECT
                    m.user_to AS user_id,
                    m.msg_time AS msg_time,
                    m.msg_text,
                    m.msg_read
                FROM
                    messages m
                WHERE
                    m.user_from = 1
            )
    ) t
INNER JOIN (SELECT MAX(msg_time) AS msg_time, user_id FROM t GROUP BY user_id) t2 ON (t.user_id=t2.user_id AND t.msg_time=t2.msg_time)
GROUP BY user_id

Table 't' doesn't exist

我意识到我可以简单地加入另一个包含UNION的查询,但这似乎是一种效率很低的工作方式。

我也希望我可以创建一个临时表,但似乎主机提供商禁止这样做。

有没有人有任何建议?我很高兴考虑UNION概念的替代品。

作为参考,预期结果应为:

+---------+--------------------+---------------------+----------------------------+
| user_id |      msg_read      |      msg_time       |          msg_text          |
+---------+--------------------+---------------------+----------------------------+
|     106 |                    | 2015-04-18 12:47:04 | Hello Andy from Carol      |
|      72 |                    | 2014-10-18 12:46:01 | Back to you Mark from Andy |
|   12388 | 2014-09-20 12:46:37| 2014-09-20 12:51:08 | Hello Graham from Andy     |
+---------+--------------------+---------------------+----------------------------+

1 个答案:

答案 0 :(得分:2)

首先,您不需要union。以下查询获取所有消息:

SELECT (case when m.user_to = 1 then m.user_from else m.user_to end) AS user_id,
       m.msg_time, m.msg_text, m.msg_read
FROM messages m
WHERE 1 in (m.user_to, m.user_from);

如果您想要每个用户使用最新的一个,只需使用聚合来获取最新消息并使用join进行过滤:

SELECT m.*
FROM (SELECT (case when m.user_to = 1 then m.user_from else m.user_to end) AS user_id,
             m.msg_time, m.msg_text, m.msg_read
      FROM messages m
      WHERE 1 in (m.user_to, m.user_from)
     ) m JOIN
     (SELECT (case when m.user_to = 1 then m.user_from else m.user_to end) AS user_id,
             MAX(m.msg_time) as maxt
      FROM messages m
      WHERE 1 in (m.user_to, m.user_from)
      GROUP BY (case when m.user_to = 1 then m.user_from else m.user_to end) 
     ) mm
     ON m.user_id = mm.user_id and
        m.msg_time = mm.maxt;