【问题标题】:MySQL JOIN with LIMIT 1 on joined tableMySQL JOIN with LIMIT 1 on join table
【发布时间】:2011-10-16 07:15:02
【问题描述】:

我想加入两个表,但表 1 上的每条记录只能得到表 2 的 1 条记录

例如:

SELECT c.id, c.title, p.id AS product_id, p.title
FROM categories AS c
JOIN products AS p ON c.id = p.category_id

这将使我获得products 中的所有记录,这不是我想要的。我希望每个类别有 1 个 [第一个] 产品(我在产品字段中有一个 sort 列)。

我该怎么做?

【问题讨论】:

    标签: mysql join limit


    【解决方案1】:

    假设您想要在sort 列中具有MIN()imial 值的产品,它看起来像这样。

    SELECT 
      c.id, c.title, p.id AS product_id, p.title
    FROM 
      categories AS c
    INNER JOIN (
      SELECT
        p.id, p.category_id, p.title
      FROM
        products AS p
      CROSS JOIN (
        SELECT p.category_id, MIN(sort) AS sort
        FROM products
        GROUP BY category_id
      ) AS sq USING (category_id)
    ) AS p ON c.id = p.category_id
    

    【讨论】:

      【解决方案2】:

      这个怎么样?

      SELECT c.id, c.title, (SELECT id from products AS p 
                                  WHERE c.id = p.category_id 
                                  ORDER BY ... 
                                  LIMIT 1)
         FROM categories AS c;
      

      【讨论】:

        【解决方案3】:

        我会尝试这样的:

        SELECT C.*,
              (SELECT P.id, P.title 
               FROM products as P
               WHERE P.category_id = C.id
               LIMIT 1)
        FROM categories C
        

        【讨论】:

        • 这会引发错误,错误代码:1241。操作数应包含 1 列
        • 问题在join
        【解决方案4】:

        我更喜欢类似问题中描述的另一种方法:https://stackoverflow.com/a/11885521/2215679

        这种方法更好,尤其是在您需要在 SELECT 中显示多个字段的情况下。避免Error Code: 1241. Operand should contain 1 column(s) 或对每列进行双重子选择。

        对于您的情况,查询应如下所示:

        SELECT
         c.id,
         c.title,
         p.id AS product_id,
         p.title AS product_title
        FROM categories AS c
        JOIN products AS p ON
         p.id = (                                 --- the PRIMARY KEY
          SELECT p1.id FROM products AS p1
          WHERE c.id=p1.category_id
          ORDER BY p1.id LIMIT 1
         )
        

        【讨论】:

        • @BenoitDuffez 我敢打赌,它的难度正好是一级(子选择)。
        • @BenoitDuffez - 查看我的解决方案来回答您对性能影响的问题
        • 很遗憾,太慢了:(
        • 这比@Gravy 的回答慢。
        【解决方案5】:
        SELECT c.id, c.title, p.id AS product_id, p.title
        FROM categories AS c
        JOIN products AS p ON c.id = p.category_id
        GROUP BY c.id
        

        这将返回 products 中的第一个数据(等于限制 1)

        【讨论】:

        • 分组似乎有很大的开销。如果我在没有它的情况下运行查询(有限制),它会快得多。我认为它忽略了索引?
        • 这将返回一个,但这一定是第一个吗?
        • 易于实施,如有必要,您可以按多个字段分组。
        【解决方案6】:

        @goggin13 接受的答案看起来是错误的。迄今为止提供的其他解决方案也可以使用,但会遇到 n+1 问题,因此会影响性能。

        n+1 问题:如果有 100 个类别,那么我们必须执行 1 次选择来获取类别,那么对于返回的 100 个类别中的每一个,我们都需要执行一次选择来获取该类别中的产品.因此将执行 101 次 SELECT 查询。

        我的替代解决方案解决了 n+1 问题,因此性能应该显着提高,因为只执行了 2 次选择。

        SELECT
          *
        FROM
            (SELECT c.id, c.title, p.id AS product_id, p.title
            FROM categories AS c
            JOIN products AS p ON c.id = p.category_id
            ORDER BY c.id ASC) AS a 
        GROUP BY id;
        

        【讨论】:

        • 有趣。通常,如果您在单个查询中使用 ORDER BY 和 GROUP BY,则 GROUP BY 将首先运行,ORDER BY 将对组进行排序。我认为这是让 ORDER BY 先行的方法。
        • @German - 没错。这样,您可以选择group_concat,确保您的结果顺序正确。
        • 任何人都可以提出一些调整以使其适用于 MySQL 5.7 吗?它抱怨非聚合列,因为默认情况下启用了 ONLY_FULL_GROUP_BY。谢谢!
        • @ChrisBartley - 只需在任何聚合列上添加缺少的 group by 字段。您也可以按照dev.mysql.com/doc/refman/5.7/en/group-by-handling.html 使用ANY_VALUE()
        【解决方案7】:

        用你的替换表格:

        SELECT * FROM works w 
        LEFT JOIN 
        (SELECT photoPath, photoUrl, videoUrl FROM workmedias LIMIT 1) AS wm ON wm.idWork = w.idWork
        

        【讨论】:

          【解决方案8】:

          With 子句可以解决问题。像这样的:

          WITH SELECTION AS (SELECT id FROM products LIMIT 1)
          SELECT a.id, c.id, c.title FROM selection a JOIN categories c ON (c.id = a.id);
          

          【讨论】:

          • 附带说明,这不适用于 MySQL 5.7 或更低版本...但是,“WITH ... AS”语法在 MySQL 8 中可用。
          【解决方案9】:

          使用 postgres 时,您可以使用 DISTINCT ON 语法来限制从任一表返回的列数。

          下面是代码示例:

          SELECT c.id, c.title, p.id AS product_id, p.title FROM categories AS c JOIN ( SELECT DISTINCT ON(p1.id) id, p1.title, p1.category_id FROM products p1 ) p ON (c.id = p.category_id)
          诀窍是不要直接在 id 出现多次的表上加入,而是首先创建一个表,每个 id 只出现一次

          【讨论】:

          • 感谢分享。不幸的是,MySQL 5.7 没有很多特性可以让 Postgres 或 SQL Server 轻松解决这个问题。但是,MySQL 8 确实如此,因为它具有“WITH ... AS”语法,可以按照另一个答案的建议应用。如果您找到适用于 MySQL 5.7+ 的方法,请务必告诉我们。
          【解决方案10】:

          另一个包含 3 个嵌套表的示例: 1/ 用户 2/ 用户角色公司 3/ 公司

          • 1 个用户拥有多个 UserRoleCompanie。
          • 1 UserRoleCompanie 有 1 个用户和 1 个公司
          • 1 公司有很多 UserRoleCompanie
          SELECT 
          u.id as userId, 
          u.firstName,
          u.lastName,
          u.email,
          urc.id ,
          urc.companieRole,
          c.id as companieId,
          c.name as companieName
          FROM User as u 
          JOIN UserRoleCompanie as urc ON u.id = urc.userId
              AND urc.id = (
                  SELECT urc2.id
                  FROM UserRoleCompanie urc2 
                  JOIN Companie ON urc2.companieId = Companie.id
                  AND urc2.userId = u.id 
                  AND Companie.isPersonal = false
                  order by Companie.createdAt DESC
                  
                  limit 1
              )
              
          LEFT JOIN Companie as c ON urc.companieId = c.id
          
          +---------------------------+-----------+--------------------+---------------------------+---------------------------+--------------+---------------------------+-------------------+
          | userId                    | firstName | lastName           | email                     | id                        | companieRole | companieId                | companieName      |
          +---------------------------+-----------+--------------------+---------------------------+---------------------------+--------------+---------------------------+-------------------+
          | cjjt9s9iw037f0748raxmnnde | henry     | pierrot            | henry@gmail.com           | cjtuflye81dwt0748e4hnkiv0 | OWNER        | cjtuflye71dws0748r7vtuqmg | leclerc           |
          

          【讨论】:

            【解决方案11】:

            在我看来,这是最好的答案(使其通用):

            SELECT  
            TB1.Id  
            FROM Table1 AS TB1  
            INNER JOIN Table2 AS TB2 ON (TB1.Id = TB2.Id_TB1)  
                AND TB2.Id = (  
                    SELECT Id  
                    FROM Table2  
                    WHERE TB1.Id = Id_TB1  
                    ORDER BY Table2.Id DESC  
                    LIMIT 1  
                )  
            

            【讨论】:

            • 您正在回答一个 10 年前的问题。这个答案不会限制结果,因为 WHERE 子句似乎没有做任何事情来使 JOIN 选择唯一。
            猜你喜欢
            • 1970-01-01
            • 2017-04-25
            • 1970-01-01
            • 1970-01-01
            • 2022-10-06
            • 2023-03-12
            • 2011-10-06
            • 1970-01-01
            • 2017-02-26
            相关资源
            最近更新 更多