为啥Mysql的左连接条件不被遵守?

Posted

技术标签:

【中文标题】为啥Mysql的左连接条件不被遵守?【英文标题】:Why is Mysql's left join condition not being honoured?为什么Mysql的左连接条件不被遵守? 【发布时间】:2020-12-13 23:40:07 【问题描述】:

查询返回意外结果:为什么?

订单表:

+----------------+
| Field          |
+----------------+
| orderNumber    |
| customerNumber |
+----------------+

订单明细表:

+-----------------+
| Field           |
+-----------------+
| orderNumber     |
| productCode     |
+-----------------+

考虑以下查询:

SELECT 
    o.orderNumber, 
    o.customerNumber, 
    d.orderNumber,
    d.productCode
FROM
    orders o
LEFT JOIN orderdetails d 
    ON ((o.orderNumber = d.orderNumber) AND (o.orderNumber = 10123)) ;

结果如下:

我希望看到 orderNumber 仅为 10123 的行

为什么我们会看到其他订单号?我会认为连接的条件(即o.orderNumber = 10123)应该排除那些其他行?这对我来说真的很奇怪,很奇怪,结果出乎意料:任何对正在发生的事情的澄清将不胜感激。

更新 - 将条件添加到 WHERE 子句

确实,我可以将o.orderNumber = 10123 添加到 WHERE 子句中,这样可以解决问题,但我担心的是,当包含在 ON 子句中时,它应该仍然可以正常工作,应该不是吗?为什么?因为在 o.orderNumber 不是 10123 的情况下条件会返回 false,因此应该从结果集中排除?

更新:可重现的示例:

这里是mysql数据库:https://sp.mysqltutorial.org/wp-content/uploads/2018/03/mysqlsampledatabase.zip

这是一个在线版本,您可以在其中输入命令:https://www.mysqltutorial.org/tryit/

谢谢!

【问题讨论】:

join有几种类型...使用前需要了解它们的用法... 您的语法有点奇怪。不要在您的 ON 子句中包含 (o.orderNumber = 10123)。加入后将其移动到 WHERE。 我会将o.orderNumber = 10123 移出JOIN 并移至WHERE,因为它与您要加入的表无关 您在 JOIN 本身上使用(o.orderNumber = 10123),而不是WHERE o.orderNumber = 10123 解释为什么你期望你所期望的,或者你只是要求另一个 LEFT JOIN 的演示,而我们不知道你的误解/误解在哪里。通过参考权威文档来证明您的期望,或者您没有进行适当的研究和解释。这是一个代码问题,所以请给minimal reproducible example。这包括前面的加上代码和输出作为文本,我们可以从您的帖子中剪切&粘贴&运行&差异&你剪切&粘贴&运行。 PS LEFT JOIN ON 返回 INNER JOIN ON 行 UNION ALL 由 NULL 扩展的不匹配的左表行。知道哪个 INNER JOIN ON。 【参考方案1】:

如果您想过滤给定订单号的结果集,您需要在WHERE 子句中找到该条件:

SELECT ...
FROM orders o
LEFT JOIN orderdetails d ON d.orderNumber = o.orderNumber
WHERE o.orderNumber = 10123;

这会带来给定数量的所有订单,以及相应的订单详细信息。如果没有订单详细信息,您仍然会获得给定数量的订单行。如果该数字没有顺序,则结果集为空。

【讨论】:

...要么这个,要么把LEFT JOIN改成INNER JOIN @Ergis:是的,但这会过滤掉没有详细信息的订单。 我已经知道了。我只是说了一个替代方案,这是他的选择! @GMB 感谢您的回答-我的困惑是“ON”子句中的条件仍应排除不是10123 的行,不是吗?这是因为该子句将返回一个 FALSE 值 Mysql 然后会省略该行吗?我想知道为什么我们必须使用 WHERE 子句进行过滤,而使用 ON 子句可以过滤掉相同的条件。澄清将不胜感激。克里斯 @BKSpureon:它排除了那些不是10123 在左连接中的行,而不是在外部查询中。为此,您需要一个 where 子句。或者,使用inner join,您不必担心。【参考方案2】:

"LEFT JOIN 子句允许您从多个表中查询数据。它返回左表中的所有行和右表中的匹配行。如果在右表中没有找到匹配的行,则使用 NULL。" (https://www.sqlservertutorial.net/sql-server-basics/sql-server-left-join/)。因此,确实对“ON”条件没有太多期望。对于 LEFT JOIN,它们只是关于第二张表。我建议作者更好地了解各种 JOIN

【讨论】:

这不是对左连接的明确解释。例如,与引用相反,返回的行都不是输入行。例如,返回的行包含作为子行的输入行,但您不说具体是哪个或如何。此外,此答案并未将您引用的内容与提问者的困惑或期望联系起来。你的写作也不清楚和模糊。另请参阅我对这个问题的评论。【参考方案3】:

如果您希望 JOIN 过滤第二个表中不匹配的结果,请使用 inner 连接:

SELECT o.orderNumber, o.customerNumber, d.orderNumber, d.productCode
FROM orders o JOIN
     orderdetails d 
     ON o.orderNumber = d.orderNumber 
WHERE o.orderNumber = 10123;

当您想要包含不匹配的行时使用外连接。

orderNumber 上的过滤可以在ON 子句或WHERE 子句中进行——它们等同于inner 连接。但是,我认为将这种过滤放在WHERE 子句中的单个表上更为“传统”。

【讨论】:

【参考方案4】:

简而言之,您正在请求 orders 表中的所有记录。只有当你有一个匹配的orderNumber 并且orderNumber10123 时,你的join 子句才会从orderdetails 获取数据,但你仍然会从orders 获取所有记录。

如果您只想在orderNumber10123 时从orders 获取记录,则应在orders 表上使用条件。执行此操作的常用方法是使用WHERE 子句。

当使用LEFT JOIN 时,您将从连接表中获得匹配的记录(基于条件),但您仍会从 LEFT 表中获得所有记录。

这应该可行。

SELECT 
    o.orderNumber, 
    o.customerNumber, 
    d.orderNumber,
    d.productCode
FROM
    orders o
LEFT JOIN orderdetails d 
    ON (o.orderNumber = d.orderNumber) 
WHERE o.orderNumber = 10123;

【讨论】:

这是一个改进。 (但仍然不清楚。例如,什​​么是“获取”?您在某些输入显示为输出行的子行的情况下使用它。没有人会从“获取”中知道这一点。其余的同样模糊。要清晰完整,必须使用足够的实际说出一个意思的词,而不仅仅是说出与其中一些模糊一致的不清楚的东西。另请参阅我评论过的另一个答案的评论时的版本。不清楚。不完整。)

以上是关于为啥Mysql的左连接条件不被遵守?的主要内容,如果未能解决你的问题,请参考以下文章

有条件的左连接

mysql中的左连接和右连接有啥区别[重复]

深入浅出:MySQL的左连接右连接等值连接

mysql连接查询

左外连接与右外连接区别?

为啥我可以从 NULL 列的左连接中选择一些东西?(用人为的例子在本地重现它,可能是一个错误!)