简体   繁体   English

LEFT JOIN 仅第一行

[英]LEFT JOIN only first row

I read many threads about getting only the first row of a left join, but, for some reason, this does not work for me.我读了很多关于只获取左连接的第一行的线程,但是,出于某种原因,这对我不起作用。

Here is my structure (simplified of course)这是我的结构(当然简化了)

Feeds提要

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

Artists艺术家

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

feeds_artists提要艺术家

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

Now i want to get the articles and join only the first Artist and I thought of something like this:现在我想获取文章并仅加入第一位艺术家,我想到了这样的事情:

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'

just to get only the first row of the feeds_artists, but already this does not work.只是为了只获取 feeds_artists 的第一行,但这已经行不通了。

I can not use TOP because of my database and I can't group the results by feeds_artists.artist_id as i need to sort them by date (I got results by grouping them this way, but the results where not the newest)由于我的数据库,我不能使用TOP并且我不能按feeds_artists.artist_id对结果进行分组,因为我需要按日期对它们进行排序(我通过这种方式对它们进行分组得到了结果,但结果不是最新的)

Tried something with OUTER APPLY as well - no success as well.也尝试了 OUTER APPLY 的一些东西 - 也没有成功。 To be honest i can not really imagine whats going on in those rows - probably the biggest reason why i cant get this to work.老实说,我真的无法想象那些行中发生了什么——这可能是我无法让它工作的最大原因。

SOLUTION:解决方案:

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'

If you can assume that artist IDs increment over time, then the MIN(artist_id) will be the earliest.如果您可以假设艺术家 ID 随时间增加,那么MIN(artist_id)将是最早的。

So try something like this (untested...)所以尝试这样的事情(未经测试......)

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

Version without subselect:没有子选择的版本:

   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

@Matt Dodges answer put me on the right track. @Matt Dodges 的回答让我走上了正轨。 Thanks again for all the answers, which helped a lot of guys in the mean time.再次感谢所有的答案,同时帮助了很多人。 Got it working like this:让它像这样工作:

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'

based on several answers here, i found something that worked for me and i wanted to generalize and explain what's going on.基于这里的几个答案,我发现了一些对我有用的东西,我想概括和解释发生了什么。

convert:转变:

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

to:到:

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

the condition that connects t1 and t2 is moved from the ON and into the inner query WHERE .连接 t1 和 t2 的条件从ON移到内部查询WHERE the MIN(primary key) or LIMIT 1 makes sure that only 1 row is returned by the inner query. MIN(primary key)LIMIT 1确保内部查询仅返回 1 行。

after selecting one specific row we need to tell the ON which row it is.选择一个特定的行后,我们需要告诉ON它是哪一行。 that's why the ON is comparing the primary key of the joined tabled.这就是为什么ON正在比较连接表的主键。

you can play with the inner query (ie order+limit) but it must return one primary key of the desired row that will tell the ON the exact row to join.您可以使用内部查询(即订单+限制),但它必须返回所需行的一个主键,该主键将告诉ON要加入的确切行。

Update - for MySQL 5.7+更新 - 适用于 MySQL 5.7+

another option relevant to MySQL 5.7+ is to use ANY_VALUE + GROUP BY .另一个与 MySQL 5.7+ 相关的选项是使用ANY_VALUE + GROUP BY it will select an artist name that is not necessarily the first one.它将选择一个不一定是第一个的艺术家姓名。

SELECT feeds.*,ANY_VALUE(feeds_artists.name) artist_name
    FROM feeds 
    LEFT JOIN feeds_artists ON feeds.id = feeds_artists.feed_id 
GROUP BY feeds.id

more info about ANY_VALUE: https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html关于 ANY_VALUE 的更多信息: https ://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

I've used something else (I think better...) and want to share it:我使用了其他东西(我认为更好......)并想分享它:

I created a VIEW that has a "group" clause我创建了一个具有“组”子句的 VIEW

CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code

SELECT * FROM client INNER JOIN vCountries on client_province = province_id

I want to say yet, that I think that we need to do this solution BECAUSE WE DID SOMETHING WRONG IN THE ANALYSIS... at least in my case... but sometimes it's cheaper to do this that to redesign everything...我还想说,我认为我们需要做这个解决方案,因为我们在分析中做错了......至少在我的情况下......但有时这样做会更便宜,重新设计一切......

I hope it helps!我希望它有帮助!

I want to give a more generalized answer .我想给出一个更笼统的答案 One that will handle any case when you want to select only the first item in a LEFT JOIN .当您只想选择 LEFT JOIN 中的第一项时,它将处理任何情况

You can use a subquery that GROUP_CONCATS what you want (sorted, too!), then just split the GROUP_CONCAT'd result and take only its first item, like so...您可以使用 GROUP_CONCATS 您想要的子查询(也已排序!),然后只拆分 GROUP_CONCAT 的结果并仅取其第一项,就像这样......

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

Since we have DESC as our ORDER BY option, this will return a Person id for someone like "Zack".因为我们有DESC作为我们的ORDER BY选项,这将返回一个像“Zack”这样的人的个人 ID。 If we wanted someone with the name like "Andy", we would change ORDER BY FirstName DESC to ORDER BY FirstName ASC .如果我们想要一个名字像“Andy”的人,我们会将ORDER BY FirstName DESC更改为ORDER BY FirstName ASC

This is nimble, as this places the power of ordering totally within your hands.这是灵活的,因为这将订购的力量完全掌握在您的手中。 But, after much testing, it will not scale well in a situation with lots of users and lots of data.但是,经过多次测试,在拥有大量用户和大量数据的情况下无法很好扩展

It is, however, useful in running data-intensive reports for admin.但是,它在为管理员运行数据密集型报告时很有用。

Here is my answer using the group by clause.这是我使用 group by 子句的答案。

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

For some database like DB2 and PostgreSQL, you have to use the key word LATERAL for specifying a sub query in the LEFT JOIN : (here, it's for DB2)对于某些数据库,如 DB2 和 PostgreSQL,您必须使用关键字LATERALLEFT JOIN指定子查询:(这里是针对 DB2)

SELECT f.*, a.*
FROM feeds f
LEFT JOIN LATERAL  
(
    SELECT artist_id, feed_id
    FROM feeds_artists sfa
    WHERE sfa.feed_id = f.id
    fetch first 1 rows only
) fa ON fa.feed_id = f.id
LEFT JOIN artists a ON a.artist_id = fa.artist_id

I know this is not a direct solution but as I've faced this and it's always a huge problem for me, and also using left join select etc. sometimes lead to a heavy process cost in database and server, I prefer doing this kind of left joins using array in php like this:我知道这不是一个直接的解决方案,但正如我所面对的那样,这对我来说一直是个大问题,而且使用左连接 select 等有时会导致数据库和服务器的处理成本过高,我更喜欢这样做像这样在 php 中使用数组左连接:

First get the data in range from second table and while you need just one row from second table, just save them with left join in-common column as key in result array.首先从第二个表中获取范围内的数据,虽然您只需要第二个表中的一行,但只需将它们与左连接 in-common 列一起保存为结果数组中的键。

SQL1: SQL1:

$sql = SELECT artist_id FROM feeds_artists fa WHERE fa.feed_id {...RANGE...}
    $res = $mysqli->query($sql);
if ($res->num_rows > 0) {
    while ($row = $res->fetch_assoc()) {
        $join_data[...$KEY...] = $row['artist_id'];
}

Then, get the base data and add detail of left join table from previous array while fetch them like this:然后,获取基础数据并从前一个数组中添加左连接表的详细信息,同时像这样获取它们:

SQL2: SQL2:

$sql = SELECT * FROM feeds f WHERE f.id {...RANGE...};
$res = $mysqli->query($sql);
if ($res->num_rows > 0) {
    while ($row = $res->fetch_assoc()) {
        $key = $row[in_common_col_value];
        $row['EXTRA_DATA'] = $join_data[$key];
        $final_data[] = $row;
}

Now, you'll have a $final_data array with desire extra data from $join_data array.现在,您将拥有一个 $final_data 数组,其中包含来自 $join_data 数组的额外数据。 this usually works good for date range data and like this.这通常适用于日期范围数据等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM