Navicat 博客

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

NOT IN 与 NOT EXISTS

在编程界,有一个常用的术语称为“反模式(anti-pattern)”。它指的是解决反复出现的问题的方法,这种方法不仅无效,而且有可能适得其反。这个术语最初是由计算机程序员 Andrew Koenig 于 1995 年在他的《设计模式》一书中创造的,作为被认为既可靠又有效的设计模式的相反词。

尽管 SQL 并不是真正的编程语言,但事实证明它同样容易受到反模式的影响,尤其是当问题的查询相当复杂时。有时错误很难发现,直到查询用于生产环境时才会暴露出来。

为了尽早发现 SQL 错误,接下来的几篇文章将重点介绍一些最常见的错误。我们将使用 MySQL 来作为今天的示例,但这些概念在任何类型的 SQL中都同样有效。

NOT IN 与 NOT EXISTS

一种常见类型的 SELECT 查询可以检索未包含在值列表中的数据。为了说明,这里有两个在 Navicat for MySQL 16 中创建的非常简单的表。第一个表包含颜色数据:

colors (24K)

第二个表包含产品数据:

products (15K)

我们想要做的是选择所有尚未与任何产品关联的颜色。换句话说,我们需要构造一个查询,它只返回那些产品没有的颜色。我们可以尝试使用 NOT IN 谓词来获取相关记录。

我们预计以下查询返回两行(“black”和“green”),而实际上查询返回的是一个空白结果集:

not_in (27K)

哪里有问题?products 表的 color 列中存在 NULL 值,由 NOT IN 谓词转换为:

color NOT IN (Red, Blue, NULL)

NOT(color=Red OR color=Blue OR color=NULL)

表达式“color=NULL”的计算结果为 UNKNOWN,许多数据库开发人员忽略的是,NOT UNKNOWN 的计算结果也为 UNKNOWN!结果,所有行都被过滤掉,查询会返回零行。

如果需求发生变化,这个问题也会浮出水面,并且不可为 NULL 的列会被更新为允许 NULL。因此,即使列在初始设计中不允许使用 NULL,以防万一你也应该确保查询在使用 NULL 时可继续正常工作。

最简单的解决方案是使用 EXISTS 谓词而不是 IN:

not_exists (39K)

问题解决了!

那么为什么会这样呢?IN 关键字将比较相应子查询列中的所有值,而 EXISTS 则评估真或假。因此,使用 IN 运算符时,SQL 引擎将扫描从内部查询获取的所有记录。另一方面,如果我们使用 EXISTS,SQL 引擎将在找到匹配项后立即停止扫描过程。

总结

在有关常见的 SQL 查询错误的第一部分中,我们了解了 SELECT 查询中如何出现反模式,首先是错误使用 NOT IN 谓词。

如果你想试用 Navicat 16 for MySQL,你可以在这里下载 14 天试用版。

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