SQL 逻辑运算符

学习重点

  • 通过使用逻辑运算符,可以将多个查询条件进行组合。

  • 通过 NOT 运算符可以生成“不是~”这样的查询条件。

  • 两边条件都成立时,使用 AND 运算符的查询条件才成立。

  • 只要两边的条件中有一个成立,使用 OR 运算符的查询条件就可以成立。

  • 值可以归结为真(TRUE)和假(FALSE)其中之一的值称为真值。比较运算符在比较成立时返回真,不成立时返回假。但是,在 SQL 中还存在另外一个特定的真值——不确定(UNKNOWN)。

  • 将根据逻辑运算符对真值进行的操作及其结果汇总成的表称为真值表。

  • SQL 中的逻辑运算是包含对真、假和不确定进行运算的三值逻辑。

一、NOT 运算符#

算术运算符和比较运算符 中我们介绍过,想要指定“不是~”这样的否定条件时,需要使用 <> 运算符。除此之外还存在另外一个表示否定,并且使用范围更广的运算符 NOT

KEYWORD

  • NOT 运算符

NOT 不能单独使用,必须和其他查询条件组合起来使用。例如,选取出销售单价(sale_price)大于等于 1000 日元的记录的 SELECT 语句如下所示(代码清单 30)。

代码清单 30 选取出销售单价大于等于 1000 日元的记录

1
2
3
SELECT product_name, product_type, sale_price
  FROM Product
 WHERE sale_price >= 1000;

执行结果

 product_name  | product_type | sale_price
---------------+--------------+------------
 T恤衫         | 衣服         |       1000
 运动T恤       | 衣服         |       4000
 菜刀          | 厨房用具     |       3000
 高压锅        | 厨房用具     |       6800

向上述 SELECT 语句的查询条件中添加 NOT 运算符之后的结果如下所示(代码清单 31)。

代码清单 31 向代码清单 30 的查询条件中添加 NOT 运算符

1
2
3
SELECT product_name, product_type, sale_price
  FROM Product
 WHERE NOT sale_price >= 1000;

执行结果

 product_name  | product_type | sale_price
---------------+--------------+-------------
 打孔器        | 办公用品     |          500
 叉子          | 厨房用具     |          500
 擦菜板        | 厨房用具     |          880
 圆珠笔        | 办公用品     |          100

明白了吗?通过否定销售单价大于等于 1000 日元(sale_price >= 1000)这个查询条件,就可以选取出销售单价小于 1000 日元的商品。也就是说,代码清单 31 中 WHERE 子句指定的查询条件,与代码清单 32 中 WHERE 子句指定的查询条件(sale_price < 1000)是等价的 1(图 5)。

代码清单 32 WHERE 子句的查询条件和代码清单 31 中的查询条件是等价的

1
2
3
SELECT product_name, product_type
  FROM Product
 WHERE sale_price < 1000;
使用 NOT 运算符时查询条件的变化

图 5 使用 NOT 运算符时查询条件的变化

通过以上的例子大家可以发现,不使用 NOT 运算符也可以编写出效果相同的查询条件。不仅如此,不使用 NOT 运算符的查询条件更容易让人理解。使用 NOT 运算符时,我们不得不每次都在脑海中进行“大于等于 1000 日元以上这个条件的否定就是小于 1000 日元”这样的转换。

虽然如此,但是也不能完全否定 NOT 运算符的作用。在编写复杂的 SQL 语句时,经常会看到 NOT 的身影。这里只是希望大家了解 NOT 运算符的书写方法和工作原理,同时提醒大家不要滥用该运算符。

法则 10

NOT 运算符用来否定某一条件,但是不能滥用。

二、AND 运算符和 OR 运算符#

到目前为止,我们看到的每条 SQL 语句中都只有一个查询条件。但在实际使用当中,往往都是同时指定多个查询条件对数据进行查询的。例如,想要查询“商品种类为厨房用具、销售单价大于等于 3000 日元”或“进货单价大于等于 5000 日元或小于 1000 日元”的商品等情况。

WHERE 子句中使用 AND 运算符或者 OR 运算符,可以对多个查询条件进行组合。

KEYWORD

  • AND 运算符

  • OR 运算符

AND 运算符在其两侧的查询条件都成立时整个查询条件才成立,其意思相当于“并且”。

OR 运算符在其两侧的查询条件有一个成立时整个查询条件都成立,其意思相当于“或者” 2

例如,从 Product 表中选取出“商品种类为厨房用具(product_type = '厨房用具'),并且销售单价大于等于 3000 日元(sale_price >= 3000)的商品”的查询条件中就使用了 AND 运算符(代码清单 33)。

代码清单 33 在 WHERE 子句的查询条件中使用 AND 运算符

1
2
3
4
SELECT product_name, purchase_price
  FROM Product
 WHERE product_type = '厨房用具'
   AND sale_price >= 3000;

执行结果

 product_name  | purchase_price
---------------+---------------
 菜刀          |           2800
 高压锅        |           5000

该查询条件的文氏图如图 6 所示。左侧的圆圈代表符合查询条件“商品种类为厨房用具”的商品,右侧的圆圈代表符合查询条件“销售单价大于等于 3000 日元”的商品。两个圆重合的部分(同时满足两个查询条件的商品)就是通过 AND 运算符能够选取出的记录。

KEYWORD

  • 文氏图

    将集合(事物的聚集)的关系通过更加容易理解的图形进行可视化展示。

AND 运算符的工作效果图

图 6 AND 运算符的工作效果图

选取出“商品种类为厨房用具(product_type = '厨房用具'),或者销售单价大于等于 3000 日元(sale_price >= 3000)的商品”的查询条件中使用了 OR 运算符(代码清单 34)。

代码清单 34 在 WHERE 子句的查询条件中使用 OR 运算符

1
2
3
4
SELECT product_name, purchase_price
  FROM Product
 WHERE product_type = '厨房用具'
    OR sale_price >= 3000;

执行结果

 product_name  | purchase_price
---------------+---------------
 运动T恤       |           2800
 菜刀          |           2800
 高压锅        |           5000
 叉子          |
 擦菜板        |            790

还是让我们来看看查询条件的文氏图吧(图 7)。包含在左侧的圆圈(商品种类为厨房用具的商品)或者右侧的圆圈(销售单价大于等于 3000 日元的商品)中的部分(两个查询条件中满足任何一个的商品)就是通过 OR 运算符能够取出的记录。

OR 运算符的工作效果图

图 7 OR 运算符的工作效果图

通过文氏图可以方便地确认由多个条件组合而成的复杂的 SQL 语句的查询条件,大家可以多多加以利用。

法则 11

多个查询条件进行组合时,需要使用 AND 运算符或者 OR 运算符。

 

法则 12

文氏图很方便。

三、通过括号强化处理#

接下来我们尝试书写稍微复杂一些的查询条件。例如,使用下面的查询条件对 Product 表进行查询的 SELECT 语句,其 WHERE 子句的条件表达式该怎么写呢?

“商品种类为办公用品”

并且

“登记日期是 2009 年 9 月 11 日或者 2009 年 9 月 20 日”

满足上述查询条件的商品(product_name)只有“打孔器”。

把上述查询条件原封不动地写入 WHERE 子句中,得到的 SELECT 语句似乎就可以满足需求了(代码清单 35)。

代码清单 35 将查询条件原封不动地写入条件表达式

1
2
3
4
5
SELECT product_name, product_type, regist_date
  FROM Product
 WHERE product_type = '办公用品'
   AND regist_date = '2009-09-11'
    OR regist_date = '2009-09-20';

让我们马上执行上述 SELECT 语句试试看,会得到下面这样的错误结果。

执行结果

 product_name  | product_type | regist_date
---------------+--------------+------------
 T恤衫         | 衣服         | 2009-09-20
 打孔器        | 办公用品     | 2009-09-11
 菜刀          | 厨房用具     | 2009-09-20
 叉子          | 厨房用具     | 2009-09-20

不想要的 T 恤衫、菜刀和叉子也被选出来了,真是头疼呀。到底为什么会得到这样的结果呢?

这是 AND 运算符优先于 OR 运算符所造成的。代码清单 35 中的条件表达式会被解释成下面这样。

「product_type = '办公用品' AND regist_date = '2009-09-11'」
 OR
 「regist_date = '2009-09-20'」

也就是,

“商品种类为办公用品,并且登记日期是 2009 年 9 月 11 日”

或者

“登记日期是 2009 年 9 月 20 日”

这和想要指定的查询条件并不相符。想要优先执行 OR 运算符时,可以像代码清单 36 那样使用半角括号 ()OR 运算符及其两侧的查询条件括起来。

KEYWORD

  • ()

代码清单 36 通过使用括号让 OR 运算符先于 AND 运算符执行

1
2
3
4
5
SELECT product_name, product_type, regist_date
  FROM Product
 WHERE product_type = '办公用品'
   AND (   regist_date = '2009-09-11'
        OR regist_date = '2009-09-20');

执行结果

 product_name  | product_type | regist_date
---------------+--------------+------------
 打孔器         | 办公用品     | 2009-09-11

这样就选取出了想要得到的“打孔器”。

法则 13

AND 运算符的优先级高于 OR 运算符。想要优先执行 OR 运算符时可以使用括号。

四、逻辑运算符和真值#

本节介绍的三个运算符 NOTANDOR 称为逻辑运算符。这里所说的逻辑就是对真值进行操作的意思。真值就是值为 真(TRUE假(FALSE 其中之一的值 3

KEYWORD

  • 逻辑运算符

  • 真值

  • 真(TRUE

  • 假(FALSE

算术运算符和比较运算符 中介绍的比较运算符会把运算结果以真值的形式进行返回。比较结果成立时返回真(TRUE),比较结果不成立时返回假(FALSE4。例如,对于 sale_price >= 3000 这个查询条件来说,由于 product_name 列为 '运动 T 恤' 的记录的 sale_price 列的值是 2800,因此会返回假(FALSE),而 product_name 列为 '高压锅' 的记录的 sale_price 列的值是 5000,所以返回真(TRUE)。

逻辑运算符对比较运算符等返回的真值进行操作。AND 运算符两侧的真值都为真时返回真,除此之外都返回假。OR 运算符两侧的真值只要有一个不为假就返回真,只有当其两侧的真值都为假时才返回假。NOT 运算符只是单纯的将真转换为假,将假转换为真。真值表(truth table)就是对这类操作及其结果进行的总结(表 4)。

KEYWORD

  • 真值表

表 4 真值表

AND

P Q P AND Q

OR

P Q P OR Q

NOT

P NOT P

请将表 4 中的 P 和 Q 想象为“销售单价为 500 日元”这样的条件。逻辑运算的结果只有真和假两种,对其进行排列组合将会得到 2 × 2 = 4 种结果。

SELECT 语句的 WHERE 子句中,通过 AND 运算符将两个查询条件连接起来时,会查询出这两个查询条件都为真的记录。通过 OR 运算符将两个查询条件连接起来时,会查询出某一个查询条件为真或者两个查询条件都为真的记录。在条件表达式中使用 NOT 运算符时,会选取出查询条件为假的记录(反过来为真)。

虽然表 4 中的真值表只是使用一个逻辑运算符时得到的结果,但即使使用两个以上的逻辑运算符连接三个以上的查询条件,通过反复进行逻辑运算求出真值,不论多复杂的条件也可以得到相应的结果。

表 5 就是根据之前例子中的查询条件“商品种类为办公用品”,并

且“登记日期是 2009 年 9 月 11 日或者 2009 年 9 月 20 日”(product_type = ' 办公用品 ' AND (regist_date = '2009-09-11' OR regist_date = '2009-09-20'))做成的真值表。

表 5 查询条件为 P AND(Q OR R)的真值表

P AND (Q OR R)

P Q R Q OR R P AND (Q OR R)

P :商品种类为办公用品

Q :登记日期是 2009 年 9 月 11 日

R :登记日期是 2009 年 9 月 20 日

Q OR R :登记日期是 2009 年 9 月 11 日或者 2009年9月20日

P AND (Q OR R):商品种类为办公用品,并且,登记日期是 2009 年 9 月 11 日或者 2009 年 9 月 20 日

代码清单 36 中的 SELECT 语句,查询出了唯一满足 P AND(Q OR R) 为真的记录“打孔器”。

法则 14

通过创建真值表,无论多复杂的条件,都会更容易理解。

专栏

逻辑积与逻辑和

将表 4 的真值表中的真变为 1、假变为 0,意外地得到了下述规则。

表 A 真为 1、假为 0 的真值表

AND(逻辑积)

P Q P AND Q
1 1 1×1 1
1 0 1×0 0
0 1 0×1 0
0 0 0×0 0

OR(逻辑和)

P Q P OR Q
1 1 1+1 1
1 0 1+0 1
0 1 0+1 1
0 0 0+0 0

NOT

P 反转 NOT P
1 1 → 0 0
0 0 → 1 1

NOT 运算符并没有什么特别的改变,但是 AND 运算的结果与乘法运算(积),OR 运算的结果与加法运算(和)的结果却是一样的 5。因此,使用 AND 运算符进行的逻辑运算称为逻辑积,使用 OR 运算符进行的逻辑运算称为逻辑和

KEYWORD

  • 逻辑积

  • 逻辑和

五、含有 NULL 时的真值#

算术运算符和比较运算符 中我们介绍了查询 NULL 时不能使用比较运算符(= 或者 <>),需要使用 IS NULL 运算符或者 IS NOT NULL 运算符。实际上,使用逻辑运算符时也需要特别对待 NULL

我们来看一下 Product(商品)表,商品“叉子”和“圆珠笔”的进货单价(purchase_price)为 NULL。那么,对这两条记录使用查询条件 purchase_price = 2800(进货单价为 2800 日元)会得到什么样的真值呢?如果结果为真,则通过该条件表达式就可以选取出“叉子”和“圆珠笔”这两条记录。但是在之前介绍“不能对 NULL 使用比较运算符”(算术运算符和比较运算符)时,我们就知道结果并不是这样的,也就是说结果不为真。

那结果会为假吗?实际上结果也不是假。如果结果为假,那么对其进行否定的条件 NOT purchase_price = 2800(进货单价不是 2800 日元)的结果应该为真,也就能选取出这两条记录了(因为假的对立面为真),但实际结果却并不是这样。

既不是真也不是假,那结果到底是什么呢?其实这是 SQL 中特有的情况。这时真值是除真假之外的第三种值——不确定(UNKNOWN。一般的逻辑运算并不存在这第三种值。SQL 之外的语言也基本上只使用真和假这两种真值。与通常的逻辑运算被称为二值逻辑相对,只有 SQL 中的逻辑运算被称为三值逻辑

KEYWORD

  • 不确定

  • 二值逻辑

  • 三值逻辑

因此,表 4 中的真值表并不完整,完整的真值表应该像表 6 这样包含“不确定”这个值。

表 6 三值逻辑中的 ANDOR 真值表

AND

P Q P AND Q
不确定 不确定
不确定
不确定 不确定
不确定
不确定 不确定 不确定

OR

P Q P OR Q
不确定
不确定 不确定
不确定
不确定 不确定
不确定 不确定 不确定

专栏

Product 表中设置 NOT NULL 约束的原因

原本只有 4 行的真值表,如果要考虑 NULL 的话就会像表 6 那样增加为 3×3=9 行,看起来也变得更加繁琐,考虑 NULL 时的条件判断也会变得异常复杂,这与我们希望的结果大相径庭。因此,数据库领域的有识>之士们达成了“尽量不使用 NULL”的共识。

这就是为什么在创建 Product 表时要给某些列设置 NOT NULL 约束(禁止录入 NULL)的缘故。

请参阅#

(完)


  1. 判定的结果相等。 ↩︎

  2. 需要注意的是,并不是只有一个条件成立时整个查询条件才成立,两个条件都成立时整个查询条件也同样成立。这与“到场的客人可以选择钥匙链或者迷你包作为礼品 ( 任选其一 )”中的“或者”有所不同。 ↩︎

  3. 但是在 SQL 中还存在“不确定”(UNKNOWN)这样的值。接下来会进行详细说明。 ↩︎

  4. 算术运算符返回的结果是数字。除了返回结果的类型不同之外,和比较运算符一样都会返回运算结果。 ↩︎

  5. 严格来说,此处的 1+1=1 与通常的整数运算并不相同。只是因为真值中只存在 0 和 1 两种情况,所以才有了这样的结果。 ↩︎

comments powered by Disqus