左联接仅第一行


138

我读了许多关于仅获得左联接的第一行的主题,但是由于某种原因,这对我不起作用。

这是我的结构(当然是简化的)

提要

id |  title | content
----------------------
1  | Feed 1 | ...

艺人

artist_id | artist_name
-----------------------
1         | Artist 1
2         | Artist 2

feeds_artists

rel_id | artist_id | feed_id
----------------------------
1      |     1     |    1 
2      |     2     |    1 
...

现在我想获取文章并仅加入第一位艺术家,我想到了这样的事情:

SELECT *
    FROM feeds 
    LEFT JOIN feeds_artists ON wp_feeds.id = (
        SELECT feeds_artists.feed_id FROM feeds_artists
        WHERE feeds_artists.feed_id = feeds.id 
    LIMIT 1
    )
WHERE feeds.id = '13815'

只是只获取feeds_artists的第一行,但已经行不通了。

TOP由于数据库原因,我无法使用,也无法按结果feeds_artists.artist_id对结果进行分组(因为我需要按日期对它们进行排序(我通过这种方式对结果进行分组,但是结果不是最新的))

也尝试使用OUTER APPLY进行尝试-也不成功。老实说,我无法想象这些行中发生了什么-可能是我无法使它起作用的最大原因。

解:

SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
    SELECT artist_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.id
    LIMIT 1
)
WHERE f.id = '13815'



您应该将解决方案作为答案,以便我们对其进行投票,并且对于以后的访问者来说更可见
fguillen

我想您是对的-我也将其添加为答案。
KddC

Answers:


97

如果您可以假设演出者ID随时间增加,那么MIN(artist_id)最早的将是。

因此,尝试这样的事情(未经测试...)

SELECT *
  FROM feeds f
  LEFT JOIN artists a ON a.artist_id = (
    SELECT
      MIN(fa.artist_id) a_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.feed_id
  ) a

感谢您的快速回复。这不是确切的答案,但完全可以帮助我找到正确的方法。我总是试图在同一个级别上同时加入这两个角色,而不是使一个依赖于另一个。非常感谢您带领我走上正确的道路。编辑了第一篇文章
KddC

@KddC而不是修改问题以添加答案,编辑此答案或做出自己的答案。
PhoneixS

4
此时,简单的子查询会更好吗?因为现在您有一个联接和一个子查询。只是问原因,我正在寻找解决同一问题的方法:)
galdikas

2
子查询太慢。
Sinux

1
@Sinux定义“太慢”。它取决于记录数和给定的要求。
观察者

53

没有子选择的版本:

   SELECT f.title,
          f.content,
          MIN(a.artist_name) artist_name
     FROM feeds f
LEFT JOIN feeds_artists fa ON fa.feed_id = f.id
LEFT JOIN artists a ON fa.artist_id = a.artist_id
 GROUP BY f.id

2
我不认为这将是第一行(第一个id或其他东西的第一个),它只是在左连接行中随机选择一个。
Sinux

26
该查询是错误的。它将选择“最低”艺术家名称而不是集合中第一位艺术家的名称。
Glapa

5
这个问题错了……但正是我想要的。就我而言,我只是想从我加盟表中的第一个ID。
德鲁·斯蒂芬斯

2
这里的问题是,如果您决定进行COUNT或SUM运算,则会搞乱数据。Subselect的问题在于,在创建临时表时,它会更重于获取数据...我希望MySql在LEFT JOIN级别上可以有一个LIMIT。
Shadoweb '17

该解决方案存在性能问题。请改用最小/最大解决方案。
萨迪(Sadegh),

25

@Matt Dodges的回答使我走上了正确的轨道。再次感谢您提供所有答案,同时也为很多人带来了帮助。像这样工作:

SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
    SELECT artist_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.id
    LIMIT 1
)
WHERE f.id = '13815'

这是仅适用于第一行的答案。没有f.id条件时中断。
Tajni

2

我使用了其他东西(我认为更好...)并希望与他人分享:

我创建了一个具有“ group”子句的VIEW

CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code

SELECT * FROM client INNER JOIN vCountries on client_province = province_id

我想说的是,我认为我们需要执行此解决方案,因为我们在分析中确实出错了……至少在我的情况下……但是有时候重新设计所有内容这样做会更便宜……

希望对您有所帮助!


假设每个有多个行(省?)country_code,那GROUP BY是不正确的。请参阅ONLY_FULL_GROUP_BY
瑞克·詹姆斯

您不必创建仅用于LEFT / INNER JOIN的视图吗?!可以使用子查询或WITH x AS (子句吗?
kiradotee

2

根据这里的几个答案,我发现了一些对我有用的东西,我想对此进行概括和解释。

兑换:

LEFT JOIN table2 t2 ON (t2.thing = t1.thing)

至:

LEFT JOIN table2 t2 ON (t2.p_key = (SELECT MIN(t2_.p_key) 
    FROM table2 t2_ WHERE (t2_.thing = t1.thing) LIMIT 1))

连接t1和t2的条件从ON和移动到内部查询中WHERE。在MIN(primary key)LIMIT 1确保只有1行由内部查询返回。

选择一个特定的行后,我们需要告诉ON它是哪一行。这就是为什么ON比较连接表的主键。

您可以使用内部查询(即order + limit),但它必须返回所需行的一个主键,该主键将告诉ON要连接的确切行。


注意:它也只能与LIMIT或一起使用MIN。该ON条件必须在连接表的主键,以避免性能的影响。
Oriadam

1

我想给出一个更笼统的答案当您只想选择LEFT JOIN中的第一项时,它将处理任何情况

您可以使用想要的GROUP_CONCATS子查询(也进行排序!),然后仅拆分GROUP_CONCAT的结果并只取其第一项,就像这样...

LEFT JOIN Person ON Person.id = (
    SELECT SUBSTRING_INDEX(
        GROUP_CONCAT(FirstName ORDER BY FirstName DESC SEPARATOR "_" ), '_', 1)
    ) FROM Person
);

由于我们将DESC作为ORDER BY选项,因此这将返回诸如“ Zack”之类的人的Person ID。如果我们想要一个名字像“ Andy”的人,我们将ORDER BY FirstName DESC更改为ORDER BY FirstName ASC

这很灵活,因为这使订购的力量完全掌握在您的手中。但是,经过大量测试,在拥有大量用户和大量数据的情况下,它无法很好扩展

但是,在为管理员运行数据密集型报告时很有用。


GROUP_CONCAT只要值的数量受到限制,此技巧就很好。默认限制是1024个字符。
瑞克·詹姆斯
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.