SQL 谓词简介
- 一、什么是谓词
- 二、LIKE 谓词——字符串的部分一致查询
- 2.1 前方一致查询
- 2.2 中间一致查询
- 2.3 后方一致查询
- 三、BETWEEN 谓词——范围查询
- 四、IS NULL、IS NOT NULL——判断是否为 NULL
- 五、IN 谓词——OR 的简便用法
- 六、使用子查询作为 IN 谓词的参数
- 6.1 IN 和子查询
- 6.2 NOT IN 和子查询
- 七、EXISTS 谓词
- 7.1 EXISTS 谓词的使用方法
- 7.1.1 EXISTS 的参数
- 7.1.2 子查询中的 SELECT *
- 7.1.3 使用 NOT EXISTS 替换 NOT IN
- 7.1 EXISTS 谓词的使用方法
- 请参阅
学习重点
谓词就是返回值为真值的函数。
掌握
LIKE
的三种使用方法(前方一致、中间一致、后方一致)。需要注意
BETWEEN
包含三个参数。想要取得
NULL
数据时必须使用IS NULL
。可以将子查询作为
IN
和EXISTS
的参数。
一、什么是谓词
本文将会和大家一起学习 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
所不具有的便利性,严格来说两者并不相同,所以希望大家能够在中级篇中掌握这两种谓词的使用方法。
请参阅
- 各种各样的函数
- SQL 谓词
- CASE 表达式
(完)
虽然使用“
shop_name="大阪"
”作为条件可以得到同样的结果,但是通常情况下,指定数据库中的商店或者商品时,并不会直接使用商品名称。这是因为与编号比起来,名称更有可能发生改变。 ↩︎希望了解
EXISTS
谓词详细内容的读者,可以参考《SQL进阶教程》中 1-8 节的内容。 ↩︎虽然严格来说语法上也可以使用非关联子查询作为参数,但实际应用中几乎没有这样的情况。 ↩︎
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341