在某些情况下,不要在“WHERE”子句中放置过滤条件

原文:towardsdatascience.com/do-not-put-filtering-conditions-in-the-where-clause-in-some-scenarios-744829da6248

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/e35aa8d6345f4a55eb528ad097bdb46d.png

图片由 NoName_13 来自 Pixabay

只要你点击进入这篇文章,我相当确信你应该已经理解了 SQL。你也必须理解,在 SELECT 查询中,我们应该将条件放在 WHERE 子句中。然而,让我问你一个问题,看看你能否立即回答。

如果我们在 LEFT JOIN … ON …子句中放置过滤条件会发生什么?

SELECT *
FROM Employee e LEFT JOIN Department d
  ON e.dept_id = d.id
  AND e.name = 'Chris'

如果您对上述查询的行为不确定,或者认为它与以下查询等效,请阅读我的文章,我会告诉您它们为什么不同。

SELECT *
FROM Employee e LEFT JOIN Department d
  ON e.dept_id = d.id
WHERE e.name = 'Chris'

1. 验证结果

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c0bde4156fd23d91ccbdf6a60b75805e.png

图片由 NoName_13 来自 Pixabay

为了演示目的,我创建了两个具有简单虚拟数据的表,如下所示。

员工表

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c1f8b69a025dfc618742357b7287cc00.png

部门表

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a58b930cfc2caadb54a8496ea23ab999.png

现在,让我们运行介绍中的第一个查询。为了方便参考,我再次将查询内容放在这里。

SELECT *
FROM Employee e LEFT JOIN Department d
  ON e.dept_id = d.id
  AND e.name = 'Chris'

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9a10beccd5ed7d528e6bad195a17c967.png

这就是答案。你答对了吗? 🙂

仅为了比较目的,我还会将第二个查询及其结果放在这里。当然,没有什么值得惊讶的,过滤条件最终会过滤结果。

SELECT *
FROM Employee e LEFT JOIN Department d
  ON e.dept_id = d.id
WHERE e.name = 'Chris';

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/7b5615047facf26895096d387b6d6a63.png

为什么会是这种情况?让我们在下一节深入探讨。

2. 连接的逻辑

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/58769f98b124c57920c1dd8b74365108.png

图片由 Gerd Altmann 来自 Pixabay

在我们能够理解为什么行为是这样的之前,我们需要回顾 LEFT JOIN 和 RIGHT JOIN 的确切逻辑,并确保我们完全理解它。

让我向您展示带有条件的 RIGHT JOIN 是什么样的。与上面的查询相同,但使用 RIGHT JOIN 会导致以下结果。

SELECT *
FROM Employee e RIGHT JOIN Department d
  ON e.dept_id = d.id
  AND e.name = 'Chris'

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/99c7a43c8d29dcf3c286bec30880fe33.png

现在,让我们看看工作流程,以便我们理解为什么会发生这种情况。

带条件的 LEFT JOIN

SQL 中的 JOIN 操作用于将两个表(左表右表)结合起来。当我们使用 LEFT JOIN 时,将保证左表中的所有行都被保留。只有右表将根据连接条件进行过滤。

以下示例适用于我们只有 e.dept_id = d.id 作为连接条件的场景。

SELECT *
FROM Employee e LEFT JOIN Department d
  ON e.dept_id = d.id

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/19250de1566154fd3f3dcfb290eef0a3.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/0f53c7707b45b1fb2bf8a96f0907f810.png

在上述查询中,我们可以这样想。左表 Employee 已经写在纸上,然后我们逐行查看它。对于左表上的每一行,我们将逐行遍历右表 Department,以查看哪一行符合条件。如果它符合条件,我们将在左边的行后面写下右边的行。

例如,当我们查看 “Alice” 时,dept_id=1。右边的 “Sales” 部门的第一个行具有相同的 id。因此,此行将跟随左边的 “Alice” 行写下。

现在,让我们看看带有额外条件 e.name = 'Chris' 的查询。

SELECT *
FROM Employee e LEFT JOIN Department d
  ON e.dept_id = d.id
  AND e.name = 'Chris'

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/6d825b16008f53086f225c52bfe05ea7.png

我仍然使用相同的箭头。然而,这次虚线表示只满足第一个条件 e.dept_id = d.id,但不满足第二个条件 e.name = 'Chris'。例如,下面的图示显示,尽管两个 ID 都是 1,但 e.name 是 Alice,而不是 Chris。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2b9755e0bd99785eebc6fb2884e4c4dd.png

当然,右侧的其他两行也无法满足这两个条件。因此:

左侧的行将被保留,而右侧的行将不会选择与之连接。

在此示例中,唯一匹配的配对如下。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/f8454ded802f16e28da90fc88df531d8.png

因此,我们只会看到具有 “Chris” 的行有一个有效的从右表连接的行。因为需要右表中的 idname 列,所以所有其他行都将显示 null 值来填充这两个列的空缺。

带条件的 RIGHT JOIN

现在,让我们看看 RIGHT JOIN 的场景。它将正好相反。右表将保证被保留,并且它将尝试根据条件从左表中找到匹配的行。

因此,具有上述条件的 RIGHT JOIN 将如下所示。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c46429e0467936c129a3ba04da27003c.png

由于左表中唯一满足两个条件的行是 “Chris” 这一行,结果将如下所示。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/1ae8df8fe13b94a2e8fe3298b39b6d2e.png

3. 何时使用它?

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/f5a562511487562578fa4b6d3423255e.png

图片由Rudy and Peter SkitteriansPixabay提供

基本上,我们很少会在 ON 子句中放置条件,因为它实际上并不过滤结果。然而,这也意味着当我们想要保留左表(或右表)的所有记录,无论条件如何时,我们可以使用它。

实际应用案例:活动销售报告

假设我们有两个表:活动和销售。

“活动”表包含了所有的活动,每个活动只对应一个产品。当然,每个活动也有开始和结束日期。

CREATE TABLE Campaigns (
    campaign_id INT PRIMARY KEY,
    product_id INT,
    start_date DATE,
    end_date DATE
);

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/e86be68f75d2b3906016193170b4d3ee.png

“销售”表包含了所有的交易。每笔交易包含一个或多个产品以及销售日期。

CREATE TABLE Sales (
    sale_id INT,
    product_id INT,
    quantity INT,
    sale_date DATE,
    PRIMARY KEY (sale_id, product_id)
);

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/74cf9f0a12fd11f50b2a6fc50b3618fa.png

现在,我们需要解决的问题是要列出所有的活动,以及在这些活动期间售出的产品数量。同时,我们不想错过任何活动,因为即使在这些活动期间没有售出任何产品,我们仍然想了解它。

SQL 查询如下。我们可以将销售日期过滤条件放入 LEFT JOIN … ON 子句中。这确保了连接的有效性,以及即使没有售出任何产品,我们也不会丢失任何活动。

SELECT C.campaign_id,
       C.product_id,
       C.start_date, 
       C.end_date,
       SUM(S.quantity) AS TotalCampaignSales
FROM Campaigns C LEFT JOIN Sales S 
  ON C.product_id = S.product_id 
  AND S.sale_date >= C.start_date 
  AND S.sale_date <= C.end_date 
GROUP BY C.campaign_id, 
         C.product_id, 
         C.start_date, 
         C.end_date  
ORDER BY SUM(S.quantity) DESC;

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2f2dc93fd732c160b6a096bf9d25c570.png

结果显示,活动编号#1 是最成功的,而活动编号#4 则完全没有效果。

如果我们将条件放在 WHERE 子句中会怎样呢?

结果仍然有效,但我们无法看到活动编号#4。

SELECT C.campaign_id,
       C.product_id,
       C.start_date, 
       C.end_date,
       SUM(S.quantity) AS TotalCampaignSales
FROM Campaigns C LEFT JOIN Sales S 
  ON C.product_id = S.product_id 
WHERE S.sale_date >= C.start_date 
  AND S.sale_date <= C.end_date 
GROUP BY C.campaign_id, 
         C.product_id, 
         C.start_date, 
         C.end_date  
ORDER BY SUM(S.quantity) DESC;

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/43a11bcdaa55f1282cbd96ab6ca19fe3.png

因此,我们根本看不到活动编号#4,因为在活动中没有售出任何产品。假设我们在“活动”表中还有更多的列,比如活动预算,如果我们能返回所有活动,那就更有用了。例如,我们在没有售出任何产品的活动中浪费了多少预算?

摘要

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/1919a743e3abf2d77190c2083ff01da5.png

图片由SplitShirePixabay提供

在这篇文章中,我专注于 SQL 中的一个非常小的语法陷阱,即 LEFT/RIGHT JOIN … ON 子句中的条件。通常,我们会将过滤条件放在 WHERE 子句中。然而,ON 子句中的条件不会减少行数,但它会影响另一边的表是否会被连接。如果条件不满足,它们将保留为 null 值。

我已尽最大努力解释了这种行为。此外,还提供了实际的应用案例。希望这会有所帮助。

除非另有说明,所有图片均为作者所有

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值