Navicat 博客

一些最常见的 SQL 查询错误 - 第 3 部分 2022 年 5 月 6 日,由 Robert Gravelle 撰写

外部联接(Outer Join)和笛卡尔积(Cartesian Product)

在这个关于“常见的 SQL 查询错误”的系列中,我们一直在探索看似直观的 SQL 查询构造方法如何导致反模式,从而导致错误结果和/或性能降低。上周,我们暂停了这个系列,讨论了 SQL 中的谓词。在本期文竟中,我们将学习它们的位置如何对查询执行产生负面影响,尤其是在外部联接中。

什么是外部联接?

在链接相关的表和视图时,可使用四种基本的联接类型:内部联接、左联接、右联接和外部联接。内部联接不会返回任何一个表中在另一个表中不匹配的行。外部联接可以返回一个或两个表中不匹配的行。而最后三种连接类型都是外部联接的一种,其中:

  • LEFT JOIN 仅返回左表中不匹配的行。
  • RIGHT JOIN 仅返回右表中不匹配的行。
  • FULL OUTER JOIN 返回两个表中不匹配的行。

外部联接如何出错

虽然外部联接在数据库从业者中肯定占有一席之地,但即使在不需要它们的情况下,开发人员也倾向于使用它们。此外,外部联接查询可能会产生完全不同的结果,具体取决于你构建它的方式以及你在查询中放置谓词的位置。为了说明这一点,让我们看看一个示例。

我们想检索所有客户的列表(无论他们是否下过任何订单),以及自 2005 年 6 月开始以来他们下的订单总数。为此,我们将使用外部联接来链接 customersorders 表,如下所示:

SELECT C.customerName, count(O.customerNumber) AS 2005_orders
FROM customers AS C
LEFT OUTER JOIN orders AS O
  ON C.customerNumber = O.customerNumber
WHERE O.orderDate >= '2005-05-01'
GROUP BY C.customerName
ORDER BY 2005_orders DESC;

查询结果应包含第一个表和第二个表中所有可能的行组合,也称为笛卡尔积。不幸的是,当我们在 Navicat Premium 16 中运行查询时,仅返回 13 行,尽管表中有 122 个客户(未显示):

customer_orders_bad (74K)

为了理解哪里出了问题,让我们一步一步从列和外部联接开始重建查询:

outer_join_without_where_clause (121K)

现在我们得到了所有的客户。那些没有下任何订单的客户的 customerNumbers 为 NULL,因为它们来自 orders 表。

现在,让我们应用 WHERE 子句谓词:

outer_join_with_where_clause (90K)

突然之间,很多客户消失了!问题是 WHERE 子句中的谓词将外部联接变成了内部联接。

为了纠正这个问题,我们需要将 WHERE 谓词添加到联接条件:

outer_join_with_date (114K)

我们现在可以调整原本的查询以获取所有客户:

customer_orders_good (89K)

故事的重点

谨记要时刻注意筛选掉的行的位置。在上述示例中,WHERE 子句是问题所在。在一个更复杂的示例中,如果有多个联接,错误的筛选可能发生在后续表运算符(如联接到另一个表)而不是 WHERE 子句中。

Navicat 文章
频道条目
分享
文章归档