【问题标题】:How can I fetch two sets of results in a single MySQL query from the same table?如何在同一个表的单个 MySQL 查询中获取两组结果?
【发布时间】:2015-06-11 02:18:16
【问题描述】:

我有一个 MySQL 的 cmets 表,如下所示:

+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| userid     | int(11)          | NO   |     | 0       |                |
| comment    | char(255)        | NO   |     | NULL    |                |
| content    | int(11)          | NO   | MUL | 0       |                |
| ratings    | int(11)          | NO   |     | 0       |                |
| datetime   | datetime         | NO   |     | NULL    |                |
| ip         | int(10) unsigned | NO   |     | NULL    |                |
| is_updated | tinyint(2)       | NO   |     | 0       |                |
| record_num | int(11)          | NO   | PRI | NULL    | auto_increment |
+------------+------------------+------+-----+---------+----------------+

现在我可以使用这样的INNER JOIN 查询从此表中获取 cmets 并从另一个表中获取用户名。

我可以得到前三个 cmets ORDER BY cmets.ratings DESC:

SELECT comments.userid, users.username, comments.comment, comments.ratings, comments.datetime, comments.record_num , content.uploader , content.anonymous
FROM comments
LEFT JOIN users ON  comments.userid = users.record_num
LEFT JOIN content ON comments.content = content.record_num
WHERE comments.content = ? ORDER BY comments.ratings DESC limit 3

获取常规 cmets ORDER BY cmets.datetime DESC

SELECT comments.userid, users.username, comments.comment, comments.ratings, comments.datetime, comments.record_num , content.uploader , content.anonymous
FROM comments
LEFT JOIN users ON  comments.userid = users.record_num
LEFT JOIN content ON comments.content = content.record_num
WHERE comments.content = ? ORDER BY comments.datetime DESC limit ?, ?

我正在尝试首先按评分向用户展示前三名 cmets,然后按 cmets.datetime DESC 向常规 cmets 排序。

现在如何将以上两个 MySQL 查询合二为一?

【问题讨论】:

    标签: php mysql join


    【解决方案1】:

    正如另一个答案所说 - 您可以使用 union 合并结果 - 这只是意味着将两个结果合并在一起。然而,需要注意的重要一点是,你不能简单地将这两个查询 union 直接放在一起,因为它们使用 order by,所以我们需要将它们包装在外部查询中,并使用排名变量来确保我们可以按照我们想要的顺序重建联合:

    select * from (
        SELECT 1 as tbl, 
               comments.userid, 
               users.username, 
               comments.comment, 
               comments.ratings, 
               comments.datetime, 
               comments.record_num, 
               content.uploader, 
               content.anonymous,
               @rank := @rank + 1 as rank
        FROM comments 
          LEFT JOIN users ON  comments.userid = users.record_num 
          LEFT JOIN content ON comments.content = content.record_num
          CROSS JOIN (select @rank := 0 ) q
        WHERE comments.content = ? ORDER BY comments.ratings DESC limit 3
    ) q1    
    UNION ALL
    select * from (    
        SELECT 2 as tbl, 
               comments.userid, 
               users.username, 
               comments.comment, 
               comments.ratings, 
               comments.datetime, 
               comments.record_num, 
               content.uploader, 
               content.anonymous,
               @rank := @rank + 1 as rank
        FROM comments 
          LEFT JOIN users ON  comments.userid = users.record_num 
          LEFT JOIN content ON comments.content = content.record_num
          CROSS JOIN (select @rank := 0 ) q
        WHERE comments.content = ? ORDER BY comments.datetime DESC limit ?, ?
    ) q2
    ORDER BY tbl asc, rank asc;
    

    union 默认为distinct,这意味着它不会重复在两个结果集中找到的行,但是也不能保证这些行将按照您期望的顺序返回,因此需要标记每个具有自己的 tbl 值的表,然后是 order by 该字段。

    如果您确定没有重复项,您可以使用union all 而不是union 来消除重复检查

    【讨论】:

    • @AMB 他们是您的两个查询 - 我只是对它们进行了不同的格式化以便于阅读。没有太多额外的工作进入它
    • 有没有办法,我什至可以显示重复的值,我的意思是如果只有 3 个 cmets,那么它应该返回 6 个 cmets,首先由 cmets.ratings DESC 设置顺序,另一组 ORDER BY cmets.datetime DESC
    • 最后一行查询有错误,应该是ORDER BY rating desc, datetime desc
    • order by tbl 是确保您的前 3 个 cmets 首先出现所必需的,因为 tbl 表示它来自哪个查询
    • 然后出了点问题,因为第一个查询不是按 cmets.ratings DESC 排序的,我已经独立尝试了两个查询,它们工作得很好,但是使用 union all,第一个查询按 datetime desc 排序
    【解决方案2】:

    要回答您的问题,是的,您可以使用UNION 组合您的查询,但是

    我正在尝试首先通过他们的评分向用户展示前三名 cmets,然后通过 cmets.datetime DESC 向用户展示常规 cmets 顺序

    如果您正在尝试实现类似 Stack Overflow 的 cmets 的功能,其中最重要的 cmets(或带有 upvotes 的 cmets)首先显示,那么我认为 单独的查询 很好够了

    如果您的查询是合并的,则用户需要加载所有顶级和常规 cmets。此外,对于具有 UNION 的大查询,很难制作或实现分页(根据我的经验,因为我仍然是编码新手)。

    免责声明:这是困难,但并非不可能

    如果我要编写该问题的代码,首先我的程序将加载前三个 cmets。之后,有一个“显示更多 cmets”按钮,它将使用 Ajax 发布到我的服务器,并将结果(常规 cmets)附加到显示的 cmets 列表中。有了这个,查询需要像现在这样分开。

    【讨论】:

      【解决方案3】:

      要将结果集彼此堆叠成一个集合,您需要使用“UNION” - https://dev.mysql.com/doc/refman/5.0/en/union.html

      【讨论】:

        猜你喜欢
        • 2018-04-24
        • 1970-01-01
        • 2013-10-28
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-17
        相关资源
        最近更新 更多