【问题标题】:LEFT JOIN only first rowLEFT JOIN 仅第一行
【发布时间】:2013-03-15 14:58:42
【问题描述】:

我阅读了很多关于只获取左连接的第一行的帖子,但由于某种原因,这对我不起作用。

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

供稿

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'

【问题讨论】:

标签: mysql join left-join groupwise-maximum


【解决方案1】:

如果您可以假设艺术家 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

【讨论】:

  • 感谢您的快速回复。这不是确切的答案,但完全让我走上了正确的道路。我总是试图在同一水平上加入两者,而不是让一个依赖于另一个。非常感谢你带领我走上正轨。编辑了第一篇文章
  • 此时不是一个简单的子查询更好吗?因为现在你有一个连接和一个子查询。只是问因为我正在寻找相同问题的解决方案:)
  • 子查询太慢了。
  • @Sinux 定义“太慢”。这取决于记录的数量和给定的要求。
  • 这行不通!子查询不允许从父查询传递字段!!!
【解决方案2】:

没有子选择的版本:

   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

【讨论】:

  • 我不认为这将是第一行(第一个 id 或其他第一行),它只是在左连接行中随机选择一个。
  • 这个查询是错误的。它将选择“最低”的艺术家名称而不是集合中第一位艺术家的名称。
  • 这个问题错了……但正是我想要的。就我而言,我只想要我要加入的表中的第一个 ID。
  • 这里的问题是,如果您决定执行 COUNT 或 SUM,您将搞砸数据。 Subselect 的问题在于它在创建临时表时获取数据更重......我希望 MySql 可以在 LEFT JOIN 级别上有一个 LIMIT。
  • 此解决方案存在性能问题。请改用最小/最大解决方案。
【解决方案3】:

我用过别的东西(我觉得更好......)并想分享它:

我创建了一个具有“组”子句的 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 ( 子句吗?
【解决方案4】:

我想给出一个更笼统的答案当您只想选择左连接中的第一项时,它可以处理任何情况

您可以使用 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 个字符。
  • 不错的 hack,但在许多情况下性能会很糟糕。它需要所有可能的值,将它们连接起来,然后拆分第一个。它也会破坏名称中有"_",所以我建议使用一些不可打印的字符作为分隔符。
【解决方案5】:

@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 条件时中断。
【解决方案6】:

根据这里的几个答案,我发现了一些对我有用的东西,我想概括并解释发生了什么。

转换:

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 移动到内部查询WHEREMIN(primary key)LIMIT 1 确保内部查询只返回 1 行。

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

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

更新 - 适用于 MySQL 5.7+

与 MySQL 5.7+ 相关的另一个选项是使用 ANY_VALUE+GROUP BY。它会选择一个不一定是第一个艺术家的名字。

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

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

【讨论】:

  • 注意:它也仅适用于 LIMIT 或仅适用于 MINON 条件必须在连接表的主键上以避免性能影响。
【解决方案7】:

这是我使用 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

【讨论】:

    【解决方案8】:

    对于像 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-04
      • 2013-11-18
      • 2017-04-29
      • 2020-08-02
      • 2014-05-01
      • 1970-01-01
      • 2017-03-16
      相关资源
      最近更新 更多