-
SQL 谓词简介
一、什么是谓词
本文将会和大家一起学习 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
表
|
-- DDL :创建表 |
|
CREATE TABLE SampleLike |
|
( strcol VARCHAR(6) NOT NULL, |
|
PRIMARY KEY (strcol)); |
SQL Server PostgreSQL
|
-- 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
进行前方一致查询
|
SELECT * |
|
FROM SampleLike |
|
WHERE strcol LIKE 'ddd%'; |
执行结果
|
strcol |
|
-------- |
|
dddabc |
其中的 % 是代表“0 字符以上的任意字符串”的特殊符号,本例中代表“以 ddd
开头的所有字符串”。
KEYWORD
%
这样我们就可以使用 LIKE
和模式匹配来进行查询了。
2.2 中间一致查询
接下来让我们看一个中间一致查询的例子,查询出包含字符串“ddd
”的记录(代码清单 23)。
代码清单 23 使用 LIKE
进行中间一致查询
|
SELECT * |
|
FROM SampleLike |
|
WHERE strcol LIKE '%ddd%'; |
执行结果
|
strcol |
|
-------- |
|
abcddd |
|
dddabc |
|
abdddc |
在字符串的起始和结束位置加上 %
,就能取出“包含 ddd
的字符串”了。
2.3 后方一致查询
最后我们来看一下后方一致查询,选取出以字符串“ddd
”结尾的记录(代码清单 24)。
代码清单 24 使用 LIKE
进行后方一致查询
|
SELECT * |
|
FROM SampleLike |
|
WHERE strcol LIKE '%ddd'; |
执行结果
|
strcol |
|
-------- |
|
abcddd |
大家可以看到上述结果与前方一致正好相反。
此外,我们还可以使用 _(下划线)来代替 %
,与 %
不同的是,它代表了“任意 1 个字符”。下面就让我们来尝试一下吧。
KEYWORD
_
使用代码清单 25 选取出 strcol
列的值为“abc
+ 任意 2 个字符”的记录。
代码清单 25 使用 LIKE
和 _
(下划线)进行前方一致查询
|
SELECT * |
|
FROM SampleLike |
|
WHERE strcol LIKE 'abc_ _'; |
执行结果
|
strcol |
|
-------- |
|
abcdd |
“abcddd
”也是以“abc
”开头的字符串,但是其中“ddd
”是 3 个字符,所以不满足 __
所指定的 2 个字符的条件,因此该字符串并不在查询结果之中。相反,代码清单 26 中的 SQL 语句就只能取出“abcddd
”这个结果。
代码清单 26 查询“abc
+ 任意 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 日元的商品
|
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 日元的商品
|
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 NULL
、IS NOT NULL
——判断是否为 NULL
为了选取出某些值为 NULL
的列的数据,不能使用 =
,而只能使用特定的谓词 IS NULL(代码清单 29)。
KEYWORD
IS NULL
谓词
代码清单 29 选取出进货单价(purchase_price
)为 NULL
的商品
|
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
的商品
|
SELECT product_name, purchase_price |
|
FROM Product |
|
WHERE purchase_price IS NOT NULL; |
执行结果
|
product_name | purchase_price |
|
-------------+--------------- |
|
T恤衫 | 500 |
|
打孔器 | 320 |
|
运动T恤 | 2800 |
|
菜刀 | 2800 |
|
高压锅 | 5000 |
|
擦菜板 | 790 |
五、IN
谓词——OR
的简便用法
接下来让我们思考一下如何选取出进货单价(purchase_price
)为 320 日元、500 日元、5000 日元的商品。这里使用之前学过的 OR
的 SQL 语句,请参考代码清单 31。
代码清单 31 通过 OR
指定多个进货单价进行查询
|
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
来指定多个进货单价进行查询
|
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
进行查询时指定多个排除的进货单价进行查询
|
SELECT product_name, purchase_price |
|
FROM Product |
|
WHERE purchase_price NOT IN (320, 500, 5000); |
执行结果
|
product_name | purchase_price |
|
-------------+--------------- |
|
运动T恤 | 2800 |
|
菜刀 | 2800 |
|
擦菜板 | 790 |
但需要注意的是,在使用 IN
和 NOT IN
时是无法选取出 NULL
数据的。实际结果也是如此,上述两组结果中都不包含进货单价为 NULL
的叉子和圆珠笔。NULL
终究还是需要使用 IS NULL
和 IS 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
语句
|
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
|
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 也是如此,同样要分两步来完成。首先,第一步如下所示。
|
SELECT product_id |
|
FROM ShopProduct |
|
WHERE shop_id = '000C'; |
因为大阪店的商店编号(shop_id
)是“000C”,所以我们可以将其作为条件写在 WHERE
子句中 [1]。接下来,我们就可以把上述 SELECT
语句作为第二步中的条件来使用了。最终得到的 SELECT
语句请参考代码清单 36。
代码清单 36 使用子查询作为 IN
的参数
|
-- 取得“在大阪店销售的商品的销售单价” |
|
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
语句也是从内层的子查询开始执行,然后像下面这样展开。
|
-- 子查询展开后的结果 |
|
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
的参数
|
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 的执行步骤。因为还是首先执行子查询,所以会得到如下结果。
|
-- 执行子查询 |
|
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 |
七、EXISTS
谓词
本文最后将要给大家介绍的是 EXISTS
谓词。将它放到最后进行学习的原因有以下 3 点。
KEYWORD
EXISTS
谓词
① EXISTS
的使用方法与之前的都不相同
② 语法理解起来比较困难
③ 实际上即使不使用 EXISTS
,基本上也都可以使用 IN
(或者 NOT IN
)来代替
理由 ① 和 ② 都说明 EXISTS
是使用方法特殊而难以理解的谓词。特别是使用否定形式 NOT EXISTS
的 SELECT
语句,即使是 DB 工程师也常常无法迅速理解。此外,如理由 ③ 所述,使用 IN
作为替代的情况非常多(尽管不能完全替代让人有些伤脑筋),很多读者虽然记住了使用方法但还是不能实际运用。
但是一旦能够熟练使用 EXISTS
谓词,就能体会到它极大的便利性。因此,非常希望大家能够在达到 SQL 中级水平时掌握此工具。本文只简单介绍其基本使用方法 [2]。
接下来就让我们赶快看一看 EXISTS
吧。
7.1 EXISTS
谓词的使用方法
一言以蔽之,谓词的作用就是“判断是否存在满足某种条件的记录”。如果存在这样的记录就返回真(TRUE
),如果不存在就返回假(FALSE
)。 EXISTS
(存在)谓词的主语是“记录”。
我们继续使用前一节“IN
和子查询”中的示例,使用 EXISTS
选取出“大阪店(000C)在售商品(product_id
)的销售单价(sale_price
)”。
SELECT
语句请参考代码清单 38。
代码清单 38 使用 EXISTS
选取出“大阪店在售商品的销售单价”
SQL Server DB2 PostgreSQL MySQL
|
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 EXISTS
的参数
之前我们学过的谓词,基本上都是像“列 LIKE
字符串”或者“列 BETWEEN
值 1 AND
值 2”这样需要指定 2 个以上的参数,而 EXISTS
的左侧并没有任何参数。很奇妙吧?这是因为 EXISTS
是只有 1 个参数的谓词。EXISTS
只需要在右侧书写 1 个参数,该参数通常都会是一个子查询。
|
(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
表进行了联接,因此作为参数的是关联子查询。EXISTS
通常都会使用关联子查询作为参数 [3]。
法则 1
通常指定关联子查询作为
EXISTS
的参数。
7.1.2 子查询中的 SELECT *
可能大家会觉得子查询中的 SELECT *
稍微有些不同,就像我们之前学到的那样,由于 EXISTS
只关心记录是否存在,因此返回哪些列都没有关系。EXISTS
只会判断是否存在满足子查询中 WHERE
子句指定的条件“商店编号(shop_id
)为 '000C',商品(Product
)表和商店商品(ShopProduct
)表中商品编号(product_id
)相同”的记录,只有存在这样的记录时才返回真(TRUE
)。
因此,即使写成代码清单 39 那样,结果也不会发生改变。
代码清单 39 这样的写法也能得到与代码清单 38 相同的结果
SQL Server DB2 PostgreSQL MySQL
|
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
)。
大家可以把在 EXISTS
的子查询中书写 SELECT *
当作 SQL 的一种习惯。
法则 2
作为
EXISTS
参数的子查询中经常会使用SELECT *
。
7.1.3 使用 NOT EXISTS
替换 NOT IN
就像 EXISTS
可以用来替换 IN
一样,NOT IN
也可以用 NOT EXISTS
来替换。下面就让我们使用 NOT EXISTS
来编写一条 SELECT
语句,读取出“东京店(000A)在售之外的商品(product_id
)的销售单价(sale_price
)”(代码清单 40)。
KEYWORD
NOT EXISTS
谓词
代码清单 40 使用 NOT EXISTS
读取出“东京店在售之外的商品的销售单价”
SQL Server DB2 PostgreSQL MySQL
|
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 EXISTS
与 EXISTS
相反,当“不存在”满足子查询中指定条件的记录时返回真(TRUE
)。
将 IN
(代码清单 36)和 EXISTS
(代码清单 38)的 SELECT
语句进行比较,会得到怎样的结果呢?可能大多数读者会觉得 IN
理解起来要容易一些,笔者也认为没有必要勉强使用 EXISTS
。因为 EXISTS
拥有 IN
所不具有的便利性,严格来说两者并不相同,所以希望大家能够在中级篇中掌握这两种谓词的使用
原文:https://www.cnblogs.com/vin-c/p/15601572.html