SQL 谓词简介

学习重点

  • 谓词就是返回值为真值的函数。

  • 掌握 LIKE 的三种使用方法(前方一致、中间一致、后方一致)。

  • 需要注意 BETWEEN 包含三个参数。

  • 想要取得 NULL 数据时必须使用 IS NULL

  • 可以将子查询作为 INEXISTS 的参数。

一、什么是谓词#

本文将会和大家一起学习 SQL 的抽出条件中不可或缺的工具——谓词(predicate)。虽然之前我们没有提及谓词这个该念,但其实大家已经使用过了。例如,=<><> 等比较运算符,其正式的名称就是比较谓词。

KEYWORD

  • 谓词

通俗来讲谓词就是 各种各样的函数 中介绍的函数中的一种,是需要满足特定条件的函数,该条件就是返回值是真值。对通常的函数来说,返回值有可能是数字、字符串或者日期等,但是谓词的返回值全都是真值(TRUE/FALSE/UNKNOWN)。这也是谓词和函数的最大区别。

本文将会介绍以下谓词。

  • LIKE

  • BETWEEN

  • IS NULL、IS NOT NULL

  • IN

  • EXISTS

二、LIKE 谓词——字符串的部分一致查询#

截至目前,我们使用字符串作为查询条件的例子中使用的都是 =。这里的 = 只有在字符串完全一致时才为真。与之相反,LIKE 谓词更加模糊一些,当需要进行字符串的部分一致查询时需要使用该谓词。

KEYWORD

  • LIKE 谓词

  • 部分一致查询

部分一致大体可以分为前方一致、中间一致和后方一致三种类型。接下来就让我们来看一看具体示例吧。

首先我们来创建一张表 1 那样的只有 1 列的表。

表 6-1 SampleLike

strcol(字符串)
abcddd
dddabc
abdddc
abcdd
ddabc
abddc

创建上表以及向其中插入数据的 SQL 语句请参考代码清单 21。

代码清单 21 创建 SampleLike

1
2
3
4
-- DDL :创建表
CREATE TABLE SampleLike
( strcol VARCHAR(6) NOT NULL,
  PRIMARY KEY (strcol));

SQL Server PostgreSQL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- DML :插入数据
BEGIN TRANSACTION; -------①

INSERT INTO SampleLike (strcol) VALUES ('abcddd');
INSERT INTO SampleLike (strcol) VALUES ('dddabc');
INSERT INTO SampleLike (strcol) VALUES ('abdddc');
INSERT INTO SampleLike (strcol) VALUES ('abcdd');
INSERT INTO SampleLike (strcol) VALUES ('ddabc');
INSERT INTO SampleLike (strcol) VALUES ('abddc');

COMMIT;

特定的 SQL

不同的 DBMS 事务处理的语法也不尽相同。代码清单 21 中的 DML 语句在 MySQL 中执行时,需要将 ① 部分更改为“START TRANSACTION;”,在 Oracle 和 DB2 中执行时,无需用到 ① 的部分(请删除)。

详细内容请大家参考 事务 中的“创建事务”。

想要从该表中读取出包含字符串“ddd”的记录时,可能会得到前方一致、中间一致和后方一致等不同的结果。

  • 前方一致:选取出“dddabc

    所谓前方一致,就是选取出作为查询条件的字符串(这里是“ddd”)与查询对象字符串起始部分相同的记录的查询方法。

  • 中间一致:选取出“abcddd”“dddabc”“abdddc

    所谓中间一致,就是选取出查询对象字符串中含有作为查询条件的字符串(这里是“ddd”)的记录的查询方法。无论该字符串出现在对象字符串的最后还是中间都没有关系。

  • 后方一致:选取出“abcddd

    后方一致与前方一致相反,也就是选取出作为查询条件的字符串(这里是“ddd”)与查询对象字符串的末尾部分相同的记录的查询方法。

KEYWORD

  • 前方一致

  • 中间一致

  • 后方一致

从本例中我们可以看出,查询条件最宽松,也就是能够取得最多记录的是中间一致。这是因为它同时包含前方一致和后方一致的查询结果。

像这样不使用“=”来指定条件字符串,而以字符串中是否包含该条件(本例中是“包含 ddd”)的规则为基础的查询称为模式匹配,其中的模式也就是前面提到的“规则”。

KEYWORD

  • 模式匹配

  • 模式

2.1 前方一致查询#

下面让我们来实际操作一下,对 SampleLike 表进行前方一致查询(代码清单 22)。

代码清单 22 使用 LIKE 进行前方一致查询

1
2
3
SELECT *
  FROM SampleLike
 WHERE strcol LIKE 'ddd%';

执行结果

 strcol
--------
 dddabc

其中的 % 是代表“0 字符以上的任意字符串”的特殊符号,本例中代表“以 ddd 开头的所有字符串”。

KEYWORD

  • %

这样我们就可以使用 LIKE 和模式匹配来进行查询了。

2.2 中间一致查询#

接下来让我们看一个中间一致查询的例子,查询出包含字符串“ddd”的记录(代码清单 23)。

代码清单 23 使用 LIKE 进行中间一致查询

1
2
3
SELECT *
  FROM SampleLike
 WHERE strcol LIKE '%ddd%';

执行结果

 strcol
--------
 abcddd
 dddabc
 abdddc

在字符串的起始和结束位置加上 %,就能取出“包含 ddd 的字符串”了。

2.3 后方一致查询#

最后我们来看一下后方一致查询,选取出以字符串“ddd”结尾的记录(代码清单 24)。

代码清单 24 使用 LIKE 进行后方一致查询

1
2
3
SELECT *
  FROM SampleLike
 WHERE strcol LIKE '%ddd';

执行结果

 strcol
--------
 abcddd

大家可以看到上述结果与前方一致正好相反。

此外,我们还可以使用 _(下划线)来代替 %,与 % 不同的是,它代表了“任意 1 个字符”。下面就让我们来尝试一下吧。

KEYWORD

  • _

使用代码清单 25 选取出 strcol 列的值为“abc + 任意 2 个字符”的记录。

代码清单 25 使用 LIKE_(下划线)进行后方一致查询

1
2
3
SELECT *
  FROM SampleLike
 WHERE strcol LIKE 'abc_ _';

执行结果

 strcol
--------
 abcdd

abcddd”也是以“abc”开头的字符串,但是其中“ddd”是 3 个字符,所以不满足 __ 所指定的 2 个字符的条件,因此该字符串并不在查询结果之中。相反,代码清单 26 中的 SQL 语句就只能取出“abcddd”这个结果。

代码清单 26 查询“abc + 任意 3 个字符”的字符串

1
2
3
SELECT *
  FROM SampleLike
 WHERE strcol LIKE 'abc___';

执行结果

 strcol
--------
 abcddd

三、BETWEEN 谓词——范围查询#

使用 BETWEEN 可以进行范围查询。该谓词与其他谓词或者函数的不同之处在于它使用了 3 个参数。例如,从 product(商品)表中读取出销售单价(sale_price)为 100 日元到 1000 日元之间的商品时,可以使用代码清单 27 中的 SQL 语句。

KEYWORD

  • BETWEEN 谓词

  • 范围查询

代码清单 27 选取销售单价为 100 ~ 1000 日元的商品

1
2
3
SELECT product_name, sale_price
  FROM Product
 WHERE sale_price BETWEEN 100 AND 1000;

执行结果

product_name | sale_price
-----------0-+-------------
 T恤衫       |       1000
 打孔器      |        500
 叉子        |        500
 擦菜板      |        880
 圆珠笔      |        100

BETWEEN 的特点就是结果中会包含 100 和 1000 这两个临界值。如果不想让结果中包含临界值,那就必须使用 <>(代码清单 28)。

KEYWORD

  • <

  • >

代码清单 28 选取出销售单价为 101 ~ 999 日元的商品

1
2
3
4
SELECT product_name, sale_price
  FROM Product
 WHERE sale_price > 100
   AND sale_price < 1000;

执行结果

product_name | sale_price
-------------+-------------
 打孔器      |         500
 叉子        |         500
 擦菜板      |         880

执行结果中不再包含 1000 日元和 100 日元的记录。

四、IS NULLIS NOT NULL——判断是否为 NULL#

为了选取出某些值为 NULL 的列的数据,不能使用 =,而只能使用特定的谓词 IS NULL(代码清单 29)。

KEYWORD

  • IS NULL 谓词

代码清单 29 选取出进货单价(purchase_price)为 NULL 的商品

1
2
3
SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price IS NULL;

执行结果

product_name | purchase_price
-------------+-------------
 叉子        |
 圆珠笔      |

与此相反,想要选取 NULL 以外的数据时,需要使用 IS NOT NULL(代码清单 30)。

KEYWORD

  • IS NOT NULL 谓词

代码清单 30 选取进货单价(purchase_price)不为 NULL 的商品

1
2
3
SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price IS NOT NULL;

执行结果

1
2
3
4
5
6
7
8
product_name | purchase_price
-------------+---------------
 T恤衫       |            500
 打孔器      |            320
 运动T恤     |           2800
 菜刀        |           2800
 高压锅      |           5000
 擦菜板      |            790

五、IN 谓词——OR 的简便用法#

接下来让我们思考一下如何选取出进货单价(purchase_price)为 320 日元、500 日元、5000 日元的商品。这里使用之前学过的 OR 的 SQL 语句,请参考代码清单 31。

代码清单 31 通过 OR 指定多个进货单价进行查询

1
2
3
4
5
SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price =  320
    OR purchase_price =  500
    OR purchase_price = 5000;

执行结果

product_name | purchase_price
-------------+---------------
 T恤衫       |            500
 打孔器      |            320
 高压锅      |           5000

虽然上述方法没有问题,但还是存在一点不足之处,那就是随着希望选取的对象越来越多,SQL 语句也会越来越长,阅读起来也会越来越困难。这时,我们就可以使用代码清单 32 中的 IN 谓词IN( 值,……)”来替换上述 SQL 语句。

KEYWORD

  • IN 谓词

代码清单 32 通过 IN 来指定多个进货单价进行查询

1
2
3
SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price IN (320, 500, 5000);

反之,希望选取出“进货单价不是 320 日元、500 日元、5000 日元”的商品时,可以使用否定形式 NOT IN 来实现(代码清单 33)。

KEYWORD

  • NOT IN 谓词

代码清单 33 使用 NOT IN 进行查询时指定多个排除的进货单价进行查询

1
2
3
SELECT product_name, purchase_price
  FROM Product
 WHERE purchase_price NOT IN (320, 500, 5000);

执行结果

product_name | purchase_price
-------------+---------------
 运动T恤     |           2800
 菜刀        |           2800
 擦菜板      |            790

但需要注意的是,在使用 INNOT IN 时是无法选取出 NULL 数据的。实际结果也是如此,上述两组结果中都不包含进货单价为 NULL 的叉子和圆珠笔。NULL 终究还是需要使用 IS NULLIS NOT NULL 来进行判断。

六、使用子查询作为 IN 谓词的参数#

6.1 IN 和子查询#

IN 谓词(NOT IN 谓词)具有其他谓词所没有的用法,那就是可以使用子查询作为其参数。我们已经在 子查询 中学习过了,子查询就是 SQL 内部生成的表,因此也可以说“能够将表作为 IN 的参数”。同理,我们还可以说“能够将 视图 作为 IN 的参数”。

为了掌握详细的使用方法,让我们再添加一张新表。之前我们使用的全都是显示商品库存清单的 Product(商品)表,但现实中这些商品可能只在个别的商店中进行销售。下面我们来创建表 2 ShopProduct(商店商品),显示出哪些商店销售哪些商品。

表 2 ShopProduct(商店商品)表

shop_id
(商店)
shop_name
(商店名称)
product_id
(商品编号)
quantity
(数量)
000A 东京 0001 30
000A 东京 0002 50
000A 东京 0003 15
000B 名古屋 0002 30
000B 名古屋 0003 120
000B 名古屋 0004 20
000B 名古屋 0006 10
000B 名古屋 0007 40
000C 大阪 0003 20
000C 大阪 0004 50
000C 大阪 0006 90
000C 大阪 0007 70
000D 福冈 0001 100

商店和商品组合成为一条记录。例如,该表显示出东京店销售的商品有 0001(T 恤衫)、0002(打孔器)、0003(运动 T 恤)三种。

创建该表的 SQL 语句请参考代码清单 34。

代码清单 34 创建 ShopProduct(商店商品)表的 CREATE TABLE 语句

1
2
3
4
5
6
CREATE TABLE ShopProduct
(shop_id    CHAR(4)      NOT NULL,
 shop_name  VARCHAR(200) NOT NULL,
 product_id CHAR(4)      NOT NULL,
 quantity   INTEGER      NOT NULL,
 PRIMARY KEY (shop_id, product_id));

CREATE TABLE 语句的特点是指定了 2 列作为主键(primary key)。这样做当然还是为了区分表中每一行数据,由于单独使用商店编号(shop_id)或者商品编号(product_id)不能满足要求,因此需要对商店和商品进行组合。

实际上如果只使用商店编号进行区分,那么指定“000A”作为条件能够查询出 3 行数据。而单独使用商品编号进行区分的话,“0001”也会查询出 2 行数据,都无法恰当区分每行数据。

下面让我们来看一下向 ShopProduct 表中插入数据的 INSERT 语句(代码清单 35)。

代码清单 35 向 ShopProduct 表中插入数据的 INSERT 语句

SQL Server PostgreSQL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
BEGIN TRANSACTION; --------①

INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0001', 30);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0002', 50);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0003', 15);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0002', 30);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0003', 120);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0004', 20);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0006', 10);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0007', 40);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0003', 20);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0004', 50);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0006', 90);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0007', 70);
INSERT INTO ShopProduct (shop_id, shop_name, product_id, quantity) VALUES ('000D', '福冈', '0001', 100);

COMMIT;

特定的 SQL

不同的 DBMS 事务处理的语法也不尽相同。代码清单 35 在 MySQL 中执行时,需要将 ① 部分更改为“START TRANSACTION;”,在 Oracle 和 DB2 中执行时,无需用到 ① 的部分(请删除)。

详细内容请大家参考 事务 中的“创建事务”。

这样我们就完成了全部准备工作,下面就让我们来看一看在 IN 谓词中使用子查询的 SQL 的写法吧。

首先读取出“大阪店(000C)在售商品(product_id)的销售单价(sale_price)”。

ShopProduct(商店商品)表中大阪店的在售商品很容易就能找出,有如下 4 种。

  • 运动 T 恤(商品编号 :0003)

  • 菜刀(商品编号 :0004)

  • 叉子(商品编号 :0006)

  • 擦菜板(商品编号 :0007)

结果自然也应该是下面这样。

 product_name | sale_price
--------------+------------
 运动T恤      |       4000
 菜刀         |       3000
 叉子         |        500
 擦菜板       |        880

得到上述结果时,我们应该已经完成了如下两个步骤。

  • ShopProduct 表中选取出在大阪店(shop_id = '000C')中销售的商品(product_id

  • Product 表中选取出上一步得到的商品(product_id)的销售单价(sale_price

SQL 也是如此,同样要分两步来完成。首先,第一步如下所示。

1
2
3
SELECT product_id
  FROM ShopProduct
 WHERE shop_id = '000C';

因为大阪店的商店编号(shop_id)是“000C”,所以我们可以将其作为条件写在 WHERE 子句中 1。接下来,我们就可以把上述 SELECT 语句作为第二步中的条件来使用了。最终得到的 SELECT 语句请参考代码清单 36。

代码清单 36 使用子查询作为 IN 的参数

1
2
3
4
5
6
-- 取得“在大阪店销售的商品的销售单价”
SELECT product_name, sale_price
  FROM Product
 WHERE product_id IN (SELECT product_id
                         FROM ShopProduct
                        WHERE shop_id = '000C');

执行结果

 product_name | sale_price
--------------+------------
 叉子         |        500
 运动T恤      |       4000
 菜刀         |       3000
 擦菜板       |        880

子查询 中的“法则 6”所述,子查询是从内层开始执行的。因此,该 SELECT 语句也是从内层的子查询开始执行,然后像下面这样展开。

1
2
3
4
-- 子查询展开后的结果
SELECT product_name, sale_price
  FROM Product
 WHERE product_id IN ('0003', '0004', '0006', '0007');

这样就转换成了之前我们学习过的 IN 的使用方法了吧。可能有些读者会产生这样的疑问:“既然子查询展开后得到的结果同样是(‘0003’,‘0004’,‘0006’,‘0007’),为什么一定要使用子查询呢?”

这是因为 ShopProduct(商店商品)表并不是一成不变的。实际上由于各个商店销售的商品都在不断发生变化,因此 ShopProduct 表内大阪店销售的商品也会发生变化。如果 SELECT 语句中没有使用子查询的话,一旦商品发生了改变,那么 SELECT 语句也不得不进行修改,而且这样的修改工作会变得没完没了。

反之,如果在 SELECT 语句中使用了子查询,那么即使数据发生了变更,还可以继续使用同样的 SELECT 语句。这样也就减少了我们的常规作业(单纯的重复操作)。

像这样可以完美应对数据变更的程序称为“易维护程序”,或者“免维护程序”。这也是系统开发中需要重点考虑的部分。希望大家在开始学习编程时,就能够有意识地编写易于维护的代码。

6.2 NOT IN 和子查询#

IN 的否定形式 NOT IN 同样可以使用子查询作为参数,其语法也和 IN 完全一样。请大家参考代码清单 37 中的例文。

代码清单 37 使用子查询作为 NOT IN 的参数

1
2
3
4
5
SELECT product_name, sale_price
  FROM Product
 WHERE product_id NOT IN (SELECT product_id
                             FROM ShopProduct
                           WHERE shop_id = '000A');

本例中的 SQL 语句是要选取出“在东京店(000A)以外销售的商品(product_id)的销售单价(sale_price)”,“NOT IN”代表了“以外”这样的否定含义。

我们也像之前那样来看一下该 SQL 的执行步骤。因为还是首先执行子查询,所以会得到如下结果。

1
2
3
4
-- 执行子查询
SELECT product_name, sale_price
  FROM Product
 WHERE product_id NOT IN ('0001', '0002', '0003');

之后就很简单了,上述语句应该会返回 0001 ~ 0003 “以外”的结果。

执行结果

 product_name | sale_price
--------------+-----------
 菜刀         |       3000
 高压锅       |       6800
 叉子         |        500
 擦菜板       |        880
 圆珠笔       |        100

七、EXIST 谓词#

本文最后将要给大家介绍的是 EXIST 谓词。将它放到最后进行学习的原因有以下 3 点。

KEYWORD

  • EXIST 谓词

EXIST 的使用方法与之前的都不相同

② 语法理解起来比较困难

③ 实际上即使不使用 EXIST,基本上也都可以使用 IN(或者 NOT IN)来代替

理由 ① 和 ② 都说明 EXIST 是使用方法特殊而难以理解的谓词。特别是使用否定形式 NOT EXISTSELECT 语句,即使是 DB 工程师也常常无法迅速理解。此外,如理由 ③ 所述,使用 IN 作为替代的情况非常多(尽管不能完全替代让人有些伤脑筋),很多读者虽然记住了使用方法但还是不能实际运用。

但是一旦能够熟练使用 EXIST 谓词,就能体会到它极大的便利性。因此,非常希望大家能够在达到 SQL 中级水平时掌握此工具。本文只简单介绍其基本使用方法 2

接下来就让我们赶快看一看 EXIST 吧。

7.1 EXIST 谓词的使用方法#

一言以蔽之,谓词的作用就是“判断是否存在满足某种条件的记录”。如果存在这样的记录就返回真(TRUE),如果不存在就返回假(FALSE)。 EXIST(存在)谓词的主语是“记录”。

我们继续使用前一节“IN 和子查询”中的示例,使用 EXIST 选取出“大阪店(000C)在售商品(product_id)的销售单价(sale_price)”。

SELECT 语句请参考代码清单 38。

代码清单 38 使用 EXIST 选取出“大阪店在售商品的销售单价”

SQL Server DB2 PostgreSQL MySQL

1
2
3
4
5
6
SELECT product_name, sale_price
  FROM Product AS P -----------------------①
 WHERE EXISTS (SELECT *
                  FROM ShopProduct AS SP --②
                 WHERE SP.shop_id = '000C'
                   AND SP.product_id = P.product_id);

特定的 SQL

Oracle 的 FROM 子句中不能使用 AS(会发生错误)。因此,在 Oracle 中执行代码清单 38 时,请将 ① 的部分修改为“FROM Product P”,将 ② 的部分修改为“FROM ShopProduct SP”(删除 FROM 子句中的 AS

执行结果

product_name | sale_price
-------------+-------------
 叉子        |        500
 运动T恤     |       4000
 菜刀        |       3000
 擦菜板      |        880

7.1.1 EXIST 的参数#

之前我们学过的谓词,基本上都是像“列 LIKE 字符串”或者“列 BETWEEN 值 1 AND 值 2”这样需要指定 2 个以上的参数,而 EXIST 的左侧并没有任何参数。很奇妙吧?这是因为 EXIST 是只有 1 个参数的谓词。EXIST 只需要在右侧书写 1 个参数,该参数通常都会是一个子查询。

1
2
3
4
(SELECT *
   FROM ShopProduct AS SP
  WHERE SP.shop_id = '000C'
    AND SP.product_id = P.product_id)

上面这样的子查询就是唯一的参数。确切地说,由于通过条件“SP.product_id = P.product_id”将 Product 表和 ShopProduct 表进行了联接,因此作为参数的是关联子查询。EXIST 通常都会使用关联子查询作为参数 3

法则 1

通常指定关联子查询作为 EXIST 的参数。

7.1.2 子查询中的 SELECT *#

可能大家会觉得子查询中的 SELECT * 稍微有些不同,就像我们之前学到的那样,由于 EXIST 只关心记录是否存在,因此返回哪些列都没有关系。EXIST 只会判断是否存在满足子查询中 WHERE 子句指定的条件“商店编号(shop_id)为 ‘000C’,商品(Product)表和商店商品(ShopProduct)表中商品编号(product_id)相同”的记录,只有存在这样的记录时才返回真(TRUE)。

因此,即使写成代码清单 39 那样,结果也不会发生改变。

代码清单 39 这样的写法也能得到与代码清单 38 相同的结果

SQL Server DB2 PostgreSQL MySQL

1
2
3
4
5
6
SELECT product_name, sale_price
  FROM Product AS P ------------------------------①
 WHERE EXISTS (SELECT 1 -- 这里可以书写适当的常数
                  FROM ShopProduct AS SP ---------②
                 WHERE SP.shop_id = '000C'
                   AND SP.product_id = P.product_id);

特定的 SQL

在 Oracle 中执行代码清单 39 时,请将 ① 的部分修改为“FROM Product P”,将 ② 的部分修改为“FROM ShopProduct SP”(删除 FROM 子句中的 AS)。

大家可以把在 EXIST 的子查询中书写 SELECT * 当作 SQL 的一种习惯。

法则 2

作为 EXIST 参数的子查询中经常会使用 SELECT *

7.1.3 使用 NOT EXIST 替换 NOT IN#

就像 EXIST 可以用来替换 IN 一样,NOT IN 也可以用 NOT EXIST 来替换。下面就让我们使用 NOT EXIST 来编写一条 SELECT 语句,读取出“东京店(000A)在售之外的商品(product_id)的销售单价(sale_price)”(代码清单 40)。

KEYWORD

  • NOT EXIST 谓词

代码清单 40 使用 NOT EXIST 读取出“东京店在售之外的商品的销售单价”

SQL Server DB2 PostgreSQL MySQL

1
2
3
4
5
6
SELECT product_name, sale_price
  FROM Product AS P ----------------------------①
 WHERE NOT EXISTS (SELECT *
                      FROM ShopProduct AS SP ---②
                     WHERE SP.shop_id = '000A'
                       AND SP.product_id = P.product_id);

特定的 SQL

在 Oracle 中执行代码清单 40 时,请将 ① 的部分修改为“FROM Product P”,将 ② 的部分修改为“FROM ShopProduct SP”(删除 FROM 子句中的 AS)。

执行结果

product_name | sale_price
-------------+------------
 菜刀        |       3000
 高压锅      |       6800
 叉子        |        500
 擦菜板      |        880
 圆珠笔      |        100

NOT EXISTEXIST 相反,当“不存在”满足子查询中指定条件的记录时返回真(TRUE)。

IN(代码清单 36)和 EXIST(代码清单 38)的 SELECT 语句进行比较,会得到怎样的结果呢?可能大多数读者会觉得 IN 理解起来要容易一些,笔者也认为没有必要勉强使用 EXIST。因为 EXIST 拥有 IN 所不具有的便利性,严格来说两者并不相同,所以希望大家能够在中级篇中掌握这两种谓词的使用方法。

请参阅#

(完)


  1. 虽然使用“shop_name='大阪'”作为条件可以得到同样的结果,但是通常情况下,指定数据库中的商店或者商品时,并不会直接使用商品名称。这是因为与编号比起来,名称更有可能发生改变。 ↩︎

  2. 希望了解 EXIST 谓词详细内容的读者,可以参考《SQL进阶教程》中 1-8 节的内容。 ↩︎

  3. 虽然严格来说语法上也可以使用非关联子查询作为参数,但实际应用中几乎没有这样的情况。 ↩︎

comments powered by Disqus