在联接表上使用LIMIT 1的MySQL JOIN


70

我想联接两个表,但是在table1上每个记录仅获得1条table2记录

例如:

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在产品字段中有一个列)。

我该怎么做呢?

Answers:


-38

我会尝试这样的事情:

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

20
这将引发错误,错误代码:1241。操作数应包含1列
kabirbaidhya

问题在join
康斯坦丁

89

我喜欢在类似问题中描述的另一种方法: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我敢打赌,将难度提高一度(子选择)。
ulkas

1
@BenoitDuffez-看到我的解决方案来回答您的问题对性能的影响
Gravy

1
不幸的是,太慢了:(
Arnes 19'9

26

@ 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;

2
有趣。通常,如果在单个查询中使用ORDER BY和GROUP BY,则GROUP BY将首先运行,而ORDER BY将对组进行排序。我认为这是使ORDER BY首先执行的方法。
Gherman

@德语-是的。这样,您可以选择group_concat确保结果按正确顺序排列。
重力1998年

谁能建议一个调整使其与MySQL 5.7一起使用?由于默认情况下启用ONLY_FULL_GROUP_BY,因此它抱怨未聚合的列。谢谢!
克里斯·巴特利

2
@ChrisBartley-只需在任何聚合列上的字段中添加缺少的分组。您也可以ANY_VALUE()按照dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
Gravy

14
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

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


1
分组似乎有很大的开销。如果我不运行它的查询(用限制)是快。我认为它正在忽略索引?
罗比·阿夫里尔

这将返回一个,但一定是第一个吗?
Yazgan

易于实施,必要时可以按多个字段分组。
caram

3

那这个呢?

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

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);

1

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

这是代码示例:

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仅出现一次


0

假设您希望列中的产品具有MIN()不道德的价值sort,它看起来应该像这样。

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

0

带有3个嵌套表的另一个示例:1 / User 2 / UserRoleCompanie 3 / Companie

  • 1个用户有多个UserRoleCompanie。
  • 1个UserRoleCompanie有1个用户和1个公司
  • 1个Companie有许多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           |

-3

用您的替换表:

SELECT * FROM works w 
LEFT JOIN 
(SELECT photoPath, photoUrl, videoUrl FROM workmedias LIMIT 1) AS wm ON wm.idWork = w.idWork
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.