为啥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
并且orderNumber
是10123
时,你的join 子句才会从orderdetails
获取数据,但你仍然会从orders
获取所有记录。
如果您只想在orderNumber
为10123
时从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的左连接条件不被遵守?的主要内容,如果未能解决你的问题,请参考以下文章