目录
一、SELECT 语句基础1.1 列的查询1.2 查询出表中所有的列表1.3 为列设定别名1.4 常数的查询1.5 从结果中删除重复行1.6 根据 WHERE 语句来选择记录1.7 注释的书写方法二、算术运算符和比较运算符2.1 算术运算符2.2 需要注意 NULL2.3 比较运算符2.4 字符串使用不等号时的注意事项2.5 不能对 NULL 使用比较运算符三、逻辑运算符3.1 NOT 运算符3.2 AND 运算符和 OR 运算符3.3 通过括号强化处理3.4 逻辑运算符和真值3.5 含有 NULL 时的真值本文介绍如何使用SELECT语句查询 SQL 如何对表进行创建、更新和删除操作 中创建的Product表中数据。这里使用的SELECT语句是 SQL 最基本也是最重要的语句。
请大家在实际运行本文中的SELECT语句时,亲身体验一下其书写方法和执行结果。
执行查询操作时可以指定想要查询数据的条件(查询条件)。查询时可以指定一个或多个查询条件,例如某一列等于这个值某一列计算之后的值大于这个值等。
一、SELECT 语句基础
本节重点
使用SELECT语句从表中选取数据。
为列设定显示用的别名。
SELECT语句中可以使用常数或者表达式。
通过指定DISTINCT可以删除重复的信息。
SQL 语句中可以使用注释。
可以通过WHERE语句从表中选取出符合查询条件的数据。
1.1 列的查询
从表中选取数据时需要使用SELECT语句,也就是只从表中选出(SELECT)必要数据的意思。通过SELECT语句查询并选取出必要数据的过程称为匹配查询或查询(query)。
SELECT语句是 SQL 语句中使用最多的最基本的 SQL 语句。掌握了SELECT语句,距离掌握 SQL 语句就不远了。
SELECT语句的基本语法如下所示。
语法 1 基本的 SELECT 语句
SELECT<列名>,…… FROM<表名>;该SELECT语句包含了SELECT和FROM两个子句(clause)。子句是 SQL 语句的组成要素,是以SELECT或者FROM等作为起始的短语。
SELECT子句中列举了希望从表中查询出的列的名称,而FROM子句则指定了选取出数据的表的名称。
接下来,我们尝试从 SQL 如何对表进行创建、更新和删除操作 中创建出的Product(商品)表中,查询出图 1 所示的product_id(商品编号)列、product_name(商品名称)列和purchase_price(进货单价)列。
图 1 查询出 Product 表中的列
对应的SELECT语句请参见代码清单 1,该语句正常执行的结果如执行结果所示 [1]。
代码清单 1 从 Product 表中输出 3 列
SELECTproduct_id, product_name, purchase_priceFROMProduct;执行结果:
product_id| product_name |purchase_price-----------+--------------+---------------0001| T恤衫 |5000002| 打孔器 |3200003| 运动T恤 |28000004| 菜刀 |28000005| 高压锅 |50000006| 叉子 |0007| 擦菜板 |7900008| 圆珠笔 |SELECT语句第一行的SELECT product_id, product_name, purchase_price就是SELECT子句。查询出的列的顺序可以任意指定。
查询多列时,需要使用逗号进行分隔。查询结果中列的顺序和SELECT子句中的顺序相同[2]。
1.2 查询出表中所有的列
想要查询出全部列时,可以使用代表所有列的星号(*)。
语法 2 查询全部的列
SELECT*FROM<表名>;例如,查询Product表中全部列的语句如代码清单 2 所示。
代码清单 2 输出 Product 表中全部的列
SELECT*FROMProduct;得到的结果和代码清单 3 中的SELECT语句的结果相同。
代码清单 3 与代码清单 2 具有相同含义的 SELECT 语句
SELECTproduct_id, product_name, product_type, sale_price, purchase_price, regist_dateFROMProduct;执行结果如下所示:
product_id|product_name|product_type|sale_price|purchase_price|regist_date------------+--------------+--------------+------------+----------------+------------0001|T恤衫|衣服|1000|500|2009-09-200002|打孔器|办公用品|500|320|2009-09-110003|运动T恤|衣服|4000|2800|0004|菜刀|厨房用具|3000|2800|2009-09-200005|高压锅|厨房用具|6800|5000|2009-01-150006|叉子|厨房用具|500||2009-09-200007|擦菜板|厨房用具|880|790|2008-04-280008|圆珠笔|办公用品|100||2009-11-11法则 1
星号(*)代表全部列的意思。
但是,如果使用星号的话,就无法设定列的显示顺序了。这时就会按照CREATE TABLE语句的定义对列进行排序。
专栏
随意使用换行符
SQL 语句使用换行符或者半角空格来分隔单词,在任何位置进行分隔都可以,即使像下面这样通篇都是换行符也不会影响SELECT语句的执行。
但是这样可能会由于看不清楚而出错。原则上希望大家能够以子句为单位进行换行(子句过长时,为方便起见可以换行)。
SELECT*FROMProduct;
另外,像下面这样插入空行(无任何字符的行)会造成执行错误,请特别注意。
SELECT * FROM Product;
1.3 为列设定别名
SQL 语句可以使用AS关键字为列设定别名。请参见代码清单 4。
代码清单 4 为列设定别名
SELECTproduct_idASid, product_nameASname, purchase_priceASpriceFROMProduct;执行结果:
id| name |price------+---------+-------0001| T恤衫 |5000002| 打孔器 |3200003| 运动T恤 |28000004| 菜刀 |28000005| 高压锅 |50000006| 叉子 |0007| 擦菜板 |7900008| 圆珠笔 |别名可以使用中文,使用中文时需要用双引号(")括起来 [3]。请注意不是单引号()。设定中文别名的SELECT语句请参见代码清单 5。
代码清单 5 设定中文别名
SELECTproduct_idAS"商品编号", product_nameAS"商品名称", purchase_priceAS"进货单价"FROMProduct;执行结果:
商品编号| 商品名称 |进货单价----------+----------+---------0001| T恤衫 |5000002| 打孔器 |3200003| 运动T恤 |28000004| 菜刀 |28000005| 高压锅 |50000006| 叉子 |0007| 擦菜板 |7900008| 圆珠笔 |通过执行结果来理解就更加容易了。像这样使用别名可以让SELECT语句的执行结果更加容易理解和操作。
法则 2
设定汉语别名时需要使用双引号(")括起来。
1.4 常数的查询
SELECT子句中不仅可以书写列名,还可以书写常数。
代码清单 6 中的SELECT子句中的第一列商品是字符串常数,第 2 列38是数字常数,第 3 列2009-02-24是日期常数,它们将与product_id列和product_name列一起被查询出来。[4]
代码清单 6 查询常数
SELECT商品ASstring,38ASnumber,2009-02-24ASdate, product_id, product_nameFROMProduct;执行结果:
string|number|date|product_id|product_name---------+----------+-------------+------------+--------------商品|38|2009-02-24|0001|T恤衫商品|38|2009-02-24|0002|打孔器商品|38|2009-02-24|0003|运动T恤商品|38|2009-02-24|0004|菜刀商品|38|2009-02-24|0005|高压锅商品|38|2009-02-24|0006|叉子商品|38|2009-02-24|0007|擦菜板商品|38|2009-02-24|0008|圆珠笔如上述执行结果所示,所有的行中都显示出了SELECT子句中的常数。
此外,SELECT子句中除了书写常数,还可以书写计算式。我们将在下一节中学习如何书写计算式。
1.5 从结果中删除重复行
想知道Product表中保存了哪些商品种类(product_type)时,如果能像图 2 那样删除重复的数据该有多好啊。
图 2 除去重复数据后的商品种类
如上所示,想要删除重复行时,可以通过在SELECT子句中使用DISTINCT来实现(代码清单 7)。
代码清单 7 使用DISTINCT删除product_type列中重复的数据
SELECTDISTINCTproduct_typeFROMProduct;执行结果:
product_type--------------- 厨房用具 衣服 办公用品法则 3
在SELECT语句中使用DISTINCT可以删除重复行。
在使用DISTINCT时,NULL也被视为一类数据。NULL存在于多行中时,也会被合并为一条NULL数据。
对含有NULL数据的purchase_price(进货单价)列使用DISTINCT的SELECT语句请参见代码清单 8。
除了两条2800的数据外,两条NULL的数据也被合并为一条。
代码清单 8 对含有NULL数据的列使用DISTINCT关键字
SELECTDISTINCTpurchase_priceFROMProduct;执行结果:
DISTINCT也可以像代码清单 9 那样在多列之前使用。此时,会将多个列的数据进行组合,将重复的数据合并为一条。
代码清单 9 中的SELECT语句,对product_type(商品种类)列和regist_date(登记日期)列的数据进行组合,将重复的数据合并为一条。
代码清单 9 在多列之前使用 DISTINCT
SELECTDISTINCTproduct_type, regist_dateFROMProduct;执行结果:
product_type|regist_date--------------+------------衣服|2009-09-20办公用品|2009-09-11办公用品|2009-11-11衣服|厨房用具|2009-09-20厨房用具|2009-01-15厨房用具|2008-04-28如上述执行结果所示,product_type列为厨房用具,同时regist_date列为2009-09-20的两条数据被合并成了一条。
DISTINCT关键字只能用在第一个列名之前。因此,请大家注意不能写成regist_date, DISTINCT product_type。
1.6 根据 WHERE 语句来选择记录
前面的例子都是将表中存储的数据全都选取出来,但实际上并不是每次都需要选取出全部数据,大部分情况都是要选取出满足商品种类为衣服销售单价在1000元以上等某些条件的数据。
SELECT语句通过WHERE子句来指定查询数据的条件。在WHERE子句中可以指定某一列的值和这个字符串相等或者某一列的值大于这个数字等条件。
执行含有这些条件的SELECT语句,就可以查询出只符合该条件的记录了。[5]
在SELECT语句中使用WHERE子句的语法如下所示。
语法 3 SELECT 语句中的 WHERE 子句
SELECT<列名>, ……FROM<表名>WHERE<条件表达式>;图 3 显示了从Product表中选取商品种类(product_type)为衣服的记录。
图 3 选取商品种类为’衣服’的记录
从被选取的记录中还可以查询出想要的列。为了更加容易理解,我们在查询product_type列的同时,把product_name列也读取出来。
SELECT语句请参见代码清单 10。
代码清单 10 用来选取 product_type 列为 衣服 的记录的 SELECT 语句
SELECTproduct_name, product_typeFROMProductWHEREproduct_type =衣服;执行结果:
product_name | product_type--------------+-------------- T恤衫 | 衣服 运动T恤 | 衣服WHERE子句中的product_type = 衣服就是用来表示查询条件的表达式(条件表达式)。
等号是比较两边的内容是否相等的符号,上述条件就是将product_type列的值和衣服进行比较,判断是否相等。Product表的所有记录都会被进行比较。
接下来会从查询出的记录中选取出SELECT语句指定的product_name列和product_type列,如执行结果所示,也就是首先通过WHERE子句查询出符合指定条件的记录,然后再选取出SELECT语句指定的列(图 4)。
图 4 选取行之后,再输出列
代码清单 10 中的语句为了确认选取出的数据是否正确,通过SELECT子句把作为查询条件的product_type列也选取出来了,其实这并不是必须的。
如果只想知道商品名称的话,可以像代码清单 11 那样只选取出product_name列。
代码清单 11 也可以不选取出作为查询条件的列
SELECTproduct_nameFROMProductWHEREproduct_type =衣服;执行结果:
product_name--------------- T恤衫 运动T恤SQL 中子句的书写顺序是固定的,不能随意更改。WHERE子句必须紧跟在FROM子句之后,书写顺序发生改变的话会造成执行错误(代码清单 12)。
代码清单 12 随意改变子句的书写顺序会造成错误
SELECTproduct_name, product_typeWHEREproduct_type =衣服FROMProduct;执行结果(PostgreSQL):
ERROR:"FROM"或者其前后有语法错误第3行: FROM Product;法则 4
WHERE子句要紧跟在FROM子句之后。
1.7 注释的书写方法
最后给大家介绍一下注释的书写方法。注释是 SQL 语句中用来标识说明或者注意事项的部分。
注释对 SQL 的执行没有任何影响。因此,无论是英文字母还是汉字都可以随意使用。
注释的书写方法有如下两种。
单行注释书写在--之后,只能写在同一行。[6]多行注释书写在/*和*/之间,可以跨多行。实际的示例请参见代码清单 13 和代码清单 14。
代码清单 13 单行注释的使用示例
-- 本SELECT语句会从结果中删除重复行。SELECT DISTINCT product_id, purchase_price FROM Product;代码清单 14 多行注释的使用示例
/* 本SELECT语句, 会从结果中删除重复行。*/SELECTDISTINCTproduct_id, purchase_priceFROMProduct;任何注释都可以插在 SQL 语句中(代码清单 15、代码清单 16)。
代码清单 15 在 SQL 语句中插入单行注释
SELECTDISTINCTproduct_id, purchase_price-- 本SELECT语句会从结果中删除重复行。 FROM Product;代码清单 16 在 SQL 语句中插入多行注释
SELECTDISTINCTproduct_id, purchase_price/* 本SELECT语句, 会从结果中删除重复行。*/FROMProduct;这些SELECT语句的执行结果与没有使用注释时完全一样。
注释能够帮助阅读者更好地理解 SQL 语句,特别是在书写复杂的 SQL 语句时,希望大家能够尽量多加简明易懂的注释。
注释不仅可以写在SELECT语句中,而且可以写在任何 SQL 语句当中,写多少都可以。
法则 5
注释是 SQL 语句中用来标识说明或者注意事项的部分。
分为单行注释和多行注释两种。
二、算术运算符和比较运算符
本节重点
运算符就是对其两边的列或者值进行运算(计算或者比较大小等)的符号。
使用算术运算符可以进行四则运算。
括号可以提升运算的优先顺序(优先进行运算)。
包含NULL的运算,其结果也是NULL。
比较运算符可以用来判断列或者值是否相等,还可以用来比较大小。
判断是否为NULL,需要使用IS NULL或者IS NOT NULL运算符。
2.1 算术运算符
SQL 语句中可以使用计算表达式。代码清单 17 中的SELECT语句,把各个商品单价的 2 倍(sale_price的 2 倍)以 "sale_price_x2" 列的形式读取出来。
代码清单 17 SQL 语句中也可以使用运算表达式
SELECTproduct_name, sale_price, sale_price *2AS"sale_price_x2"FROMProduct;执行结果:
product_name|sale_price|sale_price_x2---------------+-------------+----------------T恤衫|1000|2000打孔器|500|1000运动T恤|4000|8000菜刀|3000|6000高压锅|6800|13600叉子|500|1000擦菜板|880|1760圆珠笔|100|200sale_price_x2列中的sale_price * 2就是计算销售单价的 2 倍的表达式。
以product_name列的值为T 恤衫的记录行为例,sale_price列的值1000的 2 倍是2000,它以sale_price_x2列的形式被查询出来。
同样,打孔器记录行的值500的 2 倍1000,运动 T 恤记录行的值4000的 2 倍8000,都被查询出来了。运算就是这样以行为单位执行的。
SQL 语句中可以使用的四则运算的主要运算符如表 1 所示。
表 1 SQL 语句中可以使用的四则运算的主要运算符
含义
运算符
加法运算
+
减法运算
-
乘法运算
*
除法运算
/
四则运算所使用的运算符(+、-、*、/)称为算术运算符。运算符就是使用其两边的值进行四则运算或者字符串拼接、数值大小比较等运算,并返回结果的符号。
加法运算符(+)前后如果是数字或者数字类型的列名的话,就会返回加法运算后的结果。SQL 中除了算术运算符之外还有其他各种各样的运算符。
法则 6
SELECT 子句中可以使用常数或者表达式。
当然,SQL 中也可以像平常的运算表达式那样使用括号()。括号中运算表达式的优先级会得到提升,优先进行运算。
例如在运算表达式(1 + 2) * 3中,会先计算1 + 2的值,然后再对其结果进行* 3运算。
括号的使用并不仅仅局限于四则运算,还可以用在 SQL 语句的任何表达式当中。具体的使用方法今后会慢慢介绍给大家。
2.2 需要注意 NULL
像代码清单 2-17 那样,SQL 语句中进行运算时,需要特别注意含有 NULL 的运算。请大家考虑一下在 SQL 语句中进行如下运算时,结果会是什么呢?
A:5 + NULL
B:10 - NULL
C:1 * NULL
D:4 / NULL
E:NULL / 9
F:NULL / 0
正确答案全部都是NULL。大家可能会觉得奇怪,为什么会这样呢?实际上所有包含NULL的计算,结果肯定是NULL。即使像 F 那样用NULL除以0时这一原则也适用。
通常情况下,类似5/0这样除数为0的话会发生错误,只有NULL除以0时不会发生错误,并且结果还是NULL。
尽管如此,很多时候我们还是希望NULL能像0一样,得到5 + NULL = 5这样的结果。
不过也不要紧,SQL 中也为我们准备了可以解决这类情况的方法(将会在 SQL 常用的函数 中进行介绍)。
专栏
FROM 子句真的有必要吗?
在第 1 节中我们介绍过SELECT语句是由SELECT子句和FROM子句组成的。
可实际上FROM子句在SELECT语句中并不是必不可少的,只使用SELECT子句进行计算也是可以的。
代码清单 A 只包含 SELECT 子句的 SELECT 语句
SQL Server PostgreSQL MySQL
SELECT (100 + 200) * 3 AS calculation;
执行结果:
calculation------------- 900
实际上,通过执行SELECT语句来代替计算器的情况基本上是不存在的。不过在极少数情况下,还是可以通过使用没有FROM子句的SELECT语句来实现某种业务的。
例如,不管内容是什么,只希望得到一行临时数据的情况。
但是也存在像 Oracle 这样不允许省略SELECT语句中的FROM子句的 RDBMS,请大家注意。
在 Oracle 中,FROM子句是必需的,这种情况下可以使用DUAL这个临时表。另外,DB2 中可以使用SYSIBM.SYSDUMMY1这个临时表。
2.3 比较运算符
在第 1 节学习WHERE子句时,我们使用符号=从Product表中选取出了商品种类(product_type)为字符串衣服的记录。
下面让我们再使用符号=选取出销售单价(sale_price)为500元(数字500)的记录(代码清单 18)。
代码清单 18 选取出 sale_price 列为 500 的记录
SELECTproduct_name, product_typeFROMProductWHEREsale_price =500;执行结果:
product_name | product_type---------------+-------------- 打孔器 | 办公用品 叉子 | 厨房用具像符号=这样用来比较其两边的列或者值的符号称为比较运算符,符号=就是比较运算符。在WHERE子句中通过使用比较运算符可以组合出各种各样的条件表达式。
接下来,我们使用不等于这样代表否定含义的比较运算符<>[7],选取出sale_price列的值不为500的记录(代码清单 19)。
代码清单 19 选取出 sale_price 列的值不是 500 的记录
SELECTproduct_name, product_typeFROMProductWHEREsale_price <>500;执行结果:
product_name| product_type---------------+-------------- T恤衫 |衣服 运动T恤| 衣服 菜刀 |厨房用具 高压锅| 厨房用具 擦菜板 |厨房用具 圆珠笔| 办公用品SQL 中主要的比较运算符如表 2 所示,除了等于和不等于之外,还有进行大小比较的运算符。
表 2 比较运算符
运算符
含义
=
和 ~ 相等
<>
和 ~ 不相等
>=
大于等于 ~
>
大于 ~
<=
小于等于 ~
<
小于 ~
这些比较运算符可以对字符、数字和日期等几乎所有数据类型的列和值进行比较。
例如,从Product表中选取出销售单价(sale_price) 大于等于1000元的记录,或者登记日期(regist_date)在2009 年 9 月 27 日之前的记录,可以使用比较运算符>=和<,在WHERE子句中生成如下条件表达式(代码清单 20、代码清单 21)。
代码清单 20 选取出销售单价大于等于 1000 元的记录
SELECTproduct_name, product_type, sale_priceFROMProductWHEREsale_price >=1000;执行结果:
product_name| product_type |sale_price---------------+--------------+-------------- T恤衫| 衣服 |1000运动T恤| 衣服 |4000菜刀| 厨房用具 |3000高压锅| 厨房用具 |6800代码清单 21 选取出登记日期在 2009 年 9 月 27 日 之前的记录
SELECTproduct_name, product_type, regist_dateFROMProductWHEREregist_date <2009-09-27;执行结果:
product_name|product_type|regist_date---------------+--------------+-----------T恤衫|衣服|2009-09-20打孔器|办公用品|2009-09-11菜刀|厨房用具|2009-09-20高压锅|厨房用具|2009-01-15叉子|厨房用具|2009-09-20擦菜板|厨房用具|2008-04-28小于某个日期就是在该日期之前的意思。想要实现在某个特定日期(包含该日期)之后的查询条件时,可以使用代表大于等于的>=运算符。
另外,在使用大于等于(>=)或者小于等于(<=)作为查询条件时,一定要注意不等号(<、>)和等号(=)的位置不能颠倒。
一定要让不等号在左,等号在右。如果写成(=<)或者(=>)就会出错。当然,代表不等于的比较运算符也不能写成(><)。
法则 7
使用比较运算符时一定要注意不等号和等号的位置。
除此之外,还可以使用比较运算符对计算结果进行比较。代码清单 22 在WHERE子句中指定了销售单价(sale_price)比进货单价(purchase_price)高出500元以上的条件表达式。
为了判断是否高出500元,需要用sale_price列的值减去purchase_price列的值。
代码清单 22 WHERE 子句的条件表达式中也可以使用计算表达式
SELECTproduct_name, sale_price, purchase_priceFROMProductWHEREsale_price - purchase_price >=500;执行结果:
product_name|sale_price|purchase_price---------------+-------------+---------------T恤衫|1000|500运动T恤|4000|2800高压锅|6800|50002.4 对字符串使用不等号时的注意事项
对字符串使用大于等于或者小于等于不等号时会得到什么样的结果呢?接下来我们使用表 3 中的Chars表来进行确认。
虽然该表中存储的都是数字,但chr是字符串类型(CHAR类型)的列。
表 3 Chars 表
chr(字符串类型)
-
1
-
2
-
3
-
10
-
11
-
222
-
可以使用代码清单 23 中的 SQL 语句来创建Chars表。
代码清单 23 创建 Chars 表并插入数据
-- DDL :创建表CREATE TABLEChars(chr CHAR(3) NOTNULL,PRIMARY KEY (chr));SQL Server PostgreSQL
-- DML :插入数据BEGIN TRANSACTION; -------------①INSERT INTO CharsVALUES(1);INSERT INTO CharsVALUES(2);INSERT INTO CharsVALUES(3);INSERT INTO CharsVALUES(10);INSERT INTO CharsVALUES(11);INSERT INTO CharsVALUES(222); COMMIT;特定的 SQL
代码清单 23 中的 DML 语句根据 DBMS 的不同而略有差异。
在 MySQL 中执行该语句时,请大家把 ① 的部分改成START TRANSACTION;。
在 Oracle 和 DB2 中执行时不需用到 ① 的部分,请删除。
那么,对Chars表执行代码清单 24 中的SELECT语句(查询条件是chr列大于2)会得到什么样的结果呢?
代码清单 24 选取出大于 2 的数据的 SELECT 语句
SELECTchrFROMCharsWHEREchr>2;大家是不是觉得应该选取出比2大的3、10、11和222这 4 条记录呢?下面就让我们来看看该SELECT语句的执行结果吧。
执行结果:
chr-----3222没想到吧?是不是觉得10和11比2大,所以也应该选取出来呢?大家之所以这样想,是因为混淆了数字和字符串,也就是说2和2并不一样。
现在,chr列被定为字符串类型,并且在对字符串类型的数据进行大小比较时,使用的是和数字比较不同的规则。
典型的规则就是按照字典顺序进行比较,也就是像姓名那样,按照条目在字典中出现的顺序来进行排序。
该规则最重要的一点就是,以相同字符开头的单词比不同字符开头的单词更相近。
Chars表chr列中的数据按照字典顺序进行排序的结果如下所示。
110112222310和11同样都是以1开头的字符串,首先判定为比2小。这就像在字典中提问提议和问题按照如下顺序排列一样。
提问提议问题或者我们以书籍的章节为例也可以。1-1 节包含在第 1 章当中,所以肯定比第 2 章更靠前。
11-11-21-322-12-23进行大小比较时,得到的结果是1-3比2小(1-3 < 2),3大于2-2(3 > 2)。
比较字符串类型大小的规则今后还会经常使用,所以请大家牢记 [8]。
法则 8
字符串类型的数据原则上按照字典顺序进行排序,不能与数字的大小顺序混淆。
2.5 不能对 NULL 使用比较运算符
关于比较运算符还有一点十分重要,那就是作为查询条件的列中含有NULL的情况。
例如,我们把进货单价(purchase_price)作为查询条件。请注意,商品叉子和圆珠笔的进货单价是NULL。
我们先来选取进货单价为2800元(purchase_price = 2800)的记录(代码清单 25)。
代码清单 25 选取进货单价为 2800 元的记录
SELECTproduct_name, purchase_priceFROMProductWHEREpurchase_price =2800;执行结果:
product_name|purchase_price---------------+---------------运动T恤|2800菜刀|2800大家对这个结果应该都没有疑问吧?接下来我们再尝试选取出进货单价不是2800元(purchase_price <> 2800)的记录(代码清单 26)。
代码清单 26 选取出进货单价不是 2800 元的记录
SELECTproduct_name, purchase_priceFROMProductWHEREpurchase_price <>2800;执行结果:
product_name|purchase_price---------------+---------------T恤衫|500打孔器|320高压锅|5000擦菜板|790执行结果中并没有叉子和圆珠笔。这两条记录由于进货单价不明(NULL),因此无法判定是不是2800元。
那如果想选取进货单价为NULL的记录的话,条件表达式该怎么写呢?历经一番苦思冥想后,用purchase_price = NULL试了试,还是一条记录也取不出来。
代码清单 27 错误的 SELECT 语句(一条记录也取不出来)
SELECTproduct_name, purchase_priceFROMProductWHEREpurchase_price =NULL;执行结果:
即使使用<>运算符也还是无法选取出NULL的记录 [9]。因此,SQL 提供了专门用来判断是否为NULL的IS NULL运算符。
想要选取NULL的记录时,可以像代码清单 28 那样来书写条件表达式。
代码清单 28 选取 NULL 的记录
SELECTproduct_name, purchase_priceFROMProductWHEREpurchase_priceISNULL;执行结果:
product_name | purchase_price---------------+--------------- 叉子 | 圆珠笔 |反之,希望选取不是NULL的记录时,需要使用IS NOT NULL运算符(代码清单 29)。
代码清单 29 选取不为 NULL 的记录
SELECTproduct_name, purchase_priceFROMProductWHEREpurchase_priceISNOTNULL;执行结果:
product_name|purchase_price---------------+---------------T恤衫|500打孔器|320运动T恤|2800菜刀|2800高压锅|5000擦菜板|790法则 9
希望选取NULL记录时,需要在条件表达式中使用IS NULL运算符。
希望选取不是NULL的记录时,需要在条件表达式中使用IS NOT NULL运算符。
除此之外,对 NULL 使用比较运算符的方法还有很多,详细内容将会在接下来的 SQL 常用的函数 中进行介绍。
三、逻辑运算符
本节重点
通过使用逻辑运算符,可以将多个查询条件进行组合。
通过NOT运算符可以生成不是~这样的查询条件。
两边条件都成立时,使用AND运算符的查询条件才成立。
只要两边的条件中有一个成立,使用OR运算符的查询条件就可以成立。
值可以归结为真(TRUE)和假(FALSE)其中之一的值称为真值。比较运算符在比较成立时返回真,不成立时返回假。
但是,在 SQL 中还存在另外一个特定的真值——不确定(UNKNOWN)。
将根据逻辑运算符对真值进行的操作及其结果汇总成的表称为真值表。
SQL 中的逻辑运算是包含对真、假和不确定进行运算的三值逻辑。
3.1 NOT 运算符
在第 2 节中我们介绍过,想要指定不是~这样的否定条件时,需要使用<>运算符。除此之外还存在另外一个表示否定,并且使用范围更广的运算符NOT。
NOT不能单独使用,必须和其他查询条件组合起来使用。例如,选取出销售单价(sale_price)大于等于1000元的记录的SELECT语句如下所示(代码清单 30)。
代码清单 30 选取出销售单价大于等于 1000 元的记录
SELECTproduct_name, product_type, sale_priceFROMProductWHEREsale_price >=1000;执行结果:
product_name| product_type |sale_price---------------+--------------+------------ T恤衫| 衣服 |1000运动T恤| 衣服 |4000菜刀| 厨房用具 |3000高压锅| 厨房用具 |6800向上述SELECT语句的查询条件中添加NOT运算符之后的结果如下所示(代码清单 31)。
代码清单 31 向代码清单 30 的查询条件中添加 NOT 运算符
SELECTproduct_name, product_type, sale_priceFROMProductWHERENOTsale_price >=1000;执行结果:
product_name| product_type |sale_price---------------+--------------+------------- 打孔器| 办公用品 |500叉子| 厨房用具 |500擦菜板| 厨房用具 |880圆珠笔| 办公用品 |100明白了吗?通过否定销售单价大于等于1000元(sale_price >= 1000)这个查询条件,就可以选取出销售单价小于1000元的商品。
也就是说,代码清单 31 中WHERE子句指定的查询条件,与代码清单 32 中WHERE子句指定的查询条件(sale_price < 1000)是等价的 [10](图 5)。
代码清单 32 WHERE 子句的查询条件和代码清单 31 中的查询条件是等价的
SELECTproduct_name, product_typeFROMProductWHEREsale_price <1000;图 5 使用 NOT 运算符时查询条件的变化
通过以上的例子大家可以发现,不使用NOT运算符也可以编写出效果相同的查询条件。不仅如此,不使用NOT运算符的查询条件更容易让人理解。
使用NOT运算符时,我们不得不每次都在脑海中进行大于等于 1000 元以上这个条件的否定就是小于 1000 元这样的转换。
虽然如此,但是也不能完全否定NOT运算符的作用。在编写复杂的 SQL 语句时,经常会看到NOT的身影。
这里只是希望大家了解NOT运算符的书写方法和工作原理,同时提醒大家不要滥用该运算符。
法则 10
NOT 运算符用来否定某一条件,但是不能滥用。
3.2 AND 运算符和 OR 运算符
到目前为止,我们看到的每条 SQL 语句中都只有一个查询条件。但在实际使用当中,往往都是同时指定多个查询条件对数据进行查询的。
例如,想要查询商品种类为厨房用具、销售单价大于等于3000元或进货单价大于等于5000元或小于1000元的商品等情况。
在WHERE子句中使用AND运算符或者OR运算符,可以对多个查询条件进行组合。
AND运算符在其两侧的查询条件都成立时整个查询条件才成立,其意思相当于并且。
OR运算符在其两侧的查询条件有一个成立时整个查询条件都成立,其意思相当于或者 [11]。
例如,从Product表中选取出商品种类为厨房用具(product_type = 厨房用具),并且销售单价大于等于3000元(sale_price >= 3000)的商品的查询条件中就使用了AND运算符(代码清单 33)。
代码清单 33 在 WHERE 子句的查询条件中使用 AND 运算符
SELECTproduct_name, purchase_priceFROMProductWHEREproduct_type =厨房用具ANDsale_price >=3000;执行结果:
product_name|purchase_price---------------+---------------菜刀|2800高压锅|5000该查询条件的文氏图如图 6 所示。左侧的圆圈代表符合查询条件商品种类为厨房用具的商品,右侧的圆圈代表符合查询条件销售单价大于等于3000元的商品。
两个圆重合的部分(同时满足两个查询条件的商品)就是通过AND运算符能够选取出的记录。
文氏图
将集合(事物的聚集)的关系通过更加容易理解的图形进行可视化展示。
图 6 AND 运算符的工作效果图
选取出商品种类为厨房用具(product_type = 厨房用具),或者销售单价大于等于3000元(sale_price >= 3000)的商品的查询条件中使用了OR运算符(代码清单 34)。
代码清单 34 在 WHERE 子句的查询条件中使用 OR 运算符
SELECTproduct_name, purchase_priceFROMProductWHEREproduct_type =厨房用具ORsale_price >=3000;执行结果:
product_name|purchase_price---------------+---------------运动T恤|2800菜刀|2800高压锅|5000叉子|擦菜板|790还是让我们来看看查询条件的文氏图吧(图 7)。
包含在左侧的圆圈(商品种类为厨房用具的商品)或者右侧的圆圈(销售单价大于等于3000元的商品)中的部分(两个查询条件中满足任何一个的商品)就是通过OR运算符能够取出的记录。
图 7 OR 运算符的工作效果图
通过文氏图可以方便地确认由多个条件组合而成的复杂的 SQL 语句的查询条件,大家可以多多加以利用。
法则 11
多个查询条件进行组合时,需要使用AND运算符或者OR运算符。
3.3 通过括号强化处理
接下来我们尝试书写稍微复杂一些的查询条件。例如,使用下面的查询条件对Product表进行查询的SELECT语句,其WHERE子句的条件表达式该怎么写呢?
商品种类为办公用品并且登记日期是2009年9月11日或者2009年9月20日满足上述查询条件的商品(product_name)只有打孔器。
把上述查询条件原封不动地写入WHERE子句中,得到的SELECT语句似乎就可以满足需求了(代码清单 35)。
代码清单 35 将查询条件原封不动地写入条件表达式
SELECTproduct_name, product_type, regist_dateFROMProductWHEREproduct_type =办公用品ANDregist_date =2009-09-11ORregist_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运算符及其两侧的查询条件括起来。
代码清单 36 通过使用括号让 OR 运算符先于 AND 运算符执行
SELECTproduct_name, product_type, regist_dateFROMProductWHEREproduct_type =办公用品AND( regist_date =2009-09-11ORregist_date =2009-09-20);执行结果:
product_name|product_type|regist_date---------------+--------------+------------打孔器|办公用品|2009-09-11这样就选取出了想要得到的打孔器。
法则 13
AND运算符的优先级高于OR运算符。想要优先执行OR运算符时需要使用括号。
3.4 逻辑运算符和真值
本节介绍的三个运算符NOT、AND和OR称为逻辑运算符。这里所说的逻辑就是对真值进行操作的意思。真值就是值为真(TRUE)或假(FALSE)其中之一的值 [12]。
上一节介绍的比较运算符会把运算结果以真值的形式进行返回。比较结果成立时返回真(TRUE),比较结果不成立时返回假(FALSE)[13]。
例如,对于purchase_price >= 3000这个查询条件来说,由于product_name列为运动 T 恤的记录的purchase_price列的值是2800,因此会返回假(FALSE),而product_name列为高压锅的记录的purchase_price列的值是5000,所以返回真(TRUE)。
逻辑运算符对比较运算符等返回的真值进行操作。AND运算符两侧的真值都为真时返回真,除此之外都返回假。
OR运算符两侧的真值只要有一个不为假就返回真,只有当其两侧的真值都为假时才返回假。
NOT运算符只是单纯的将真转换为假,将假转换为真。真值表(truth table)就是对这类操作及其结果进行的总结(表 4)。
表 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 日
QORR:登记日期是 2009 年 9 月 11 日 或者 2009 年 9 月 20 日
PAND(QORR):商品种类为办公用品,并且,登记日期是 2009 年 9 月 11 日 或者 2009 年 9 月 20 日
代码清单 36 中的SELECT语句,查询出了唯一满足 PAND(QORR) 为真的记录打孔器。
法则 14
通过创建真值表,无论多复杂的条件,都会更容易理解。
专栏
逻辑积与逻辑和
将表 4 的真值表中的真变为1、假变为0,意外地得到了下述规则。
表 A 真为 1、假为 0 的真值表
AND(逻辑积)
PQ积P AND Q111×11101×00010×10000×00
OR(逻辑和)
PQ和P OR Q111+11101+01010+11000+00
NOT
P反转NOT P11 → 0000 → 11
NOT运算符并没有什么特别的改变,但是AND运算的结果与乘法运算(积),OR运算的结果与加法运算(和)的结果却是一样的。
严格来说,此处的1+1=1与通常的整数运算并不相同。只是因为真值中只存在0和1两种情况,所以才有了这样的结果。
因此,使用AND运算符进行的逻辑运算称为逻辑积,使用OR运算符进行的逻辑运算称为逻辑和。
3.5 含有 NULL 时的真值
上一节我们介绍了查询NULL时不能使用比较运算符(=或者<>),需要使用IS NULL运算符或者IS NOT NULL运算符。实际上,使用逻辑运算符时也需要特别对待NULL。
我们来看一下Product(商品)表,商品叉子和圆珠笔的进货单价(purchase_price)为NULL。
那么,对这两条记录使用查询条件purchase_price = 2800(进货单价为2800元)会得到什么样的真值呢?如果结果为真,则通过该条件表达式就可以选取出叉子和圆珠笔这两条记录。
但是在之前介绍不能对NULL使用比较运算符(第 2 节)时,我们就知道结果并不是这样的,也就是说结果不为真。
那结果会为假吗?实际上结果也不是假。
如果结果为假,那么对其进行否定的条件NOT purchase_price = 2800(进货单价不是2800元)的结果应该为真,也就能选取出这两条记录了(因为假的对立面为真),但实际结果却并不是这样。
既不是真也不是假,那结果到底是什么呢?其实这是 SQL 中特有的情况。这时真值是除真假之外的第三种值——不确定(UNKNOWN)。一般的逻辑运算并不存在这第三种值。
SQL 之外的语言也基本上只使用真和假这两种真值。与通常的逻辑运算被称为二值逻辑相对,只有 SQL 中的逻辑运算被称为三值逻辑。
因此,表 4 中的真值表并不完整,完整的真值表应该像表 6 这样包含不确定这个值。
表 6 三值逻辑中的 AND 和 OR 真值表
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)的缘故。
原文链接:https://www.developerastrid.com/sql/sql-select-where/
(完)
结果的显示方式根据 RDBMS 的客户端的不同略有不同(数据的内容都是相同的)。 ↩︎行的顺序也可能存在与上述执行结果不同的情况。如果用户不设定SELECT语句执行结果中行的顺序,就可能会发生上述情况。行的排序方法将在 SQL 如何对表进行聚合和分组查询并对查询结果进行排序 中进行学习。 ↩︎使用双引号可以设定包含空格(空白)的别名。但是如果忘记使用双引号就可能出错,因此并不推荐。大家可以像product_list这样使用下划线(_)来代替空白。 ↩︎在 SQL 语句中使用字符串或者日期常数时,必须使用单引号 () 将其括起来。 ↩︎这和 Excel 中根据过滤条件对行进行过滤的功能是相同的。 ↩︎MySQL 中需要在--之后加入半角空格(如果不加的话就不会被认为是注释)。 ↩︎有很多 RDBMS 可以使用比较运算符!=来实现不等于功能。但这是限于不被标准 SQL 所承认的特定 SQL,出于安全的考虑,最好不要使用。 ↩︎该规则对定长字符串和可变长字符串都适用。 ↩︎SQL 不识别= NULL和<> NULL的理由将会在下一节(包含NULL情况下的真值)中进行说明。 ↩︎判定的结果相等。 ↩︎需要注意的是,并不是只有一个条件成立时整个查询条件才成立,两个条件都成立时整个查询条件也同样成立。这与到场的客人可以选择钥匙链或者迷你包作为礼品 ( 任选其一 )中的或者有所不同。 ↩︎但是在 SQL 中还存在不确定(UNKNOWN)这样的值。接下来会进行详细说明。 ↩︎算术运算符返回的结果是数字。除了返回结果的类型不同之外,和比较运算符一样都会返回运算结果。 ↩︎免责声明:内容来自用户上传并发布,站点仅提供信息存储空间服务,不拥有所有权,本网站所提供的信息只供参考之用。