- Oracle PL/SQL宝典
- 赵越编著
- 1034字
- 2020-08-28 06:11:40
第5章 数据的基本查询
有关查询操作在前面大家已经接触过了,从本章开始将详细地对查询进行讲解。所谓查询就是让数据库服务器根据客户端的要求搜寻出用户所需要的信息资料,并按用户规定的格式进行整理后返回给客户端。
查询语句SELECT在任何一种SQL语言中,都是使用频率最高的语句。可以说,SELECT语句是SQL语言的灵魂。SELECT语句具有强大的查询功能,有的用户甚至只需要熟练掌握SELECT语句的一部分,就可以轻松地利用数据库来完成自己的工作。本章将主要介绍SELECT语句的基本结构,以及一些简单的查询操作。
5.1 查询语句的基础
SELECT语句由一系列灵活的子句组成,这些子句共同确定检索哪些数据。使用SELECT语句,用户除了可以查看普通数据库中的表和视图的信息外,还可以查看Oracle的系统信息。在介绍SELECT语句的使用之前,有必要对SELECT语句的基本语法结构及执行过程做一下介绍。
5.1.1 查询语句语法
在数据库开发过程中查询语句是使用最多的语句,它以SELECT开头,而最明显的特征就是包含SELECT关键词,这个关键词是查询当中必需的部分,而其他子句可以根据实际需求进行变动。查询语句的完整语法相对比较复杂,这里不做完整的介绍,而是讲解SELECT语句的基本语法结构。其语法结构大致如下:
SELECT [DISTINCT|ALL] select_list FROM table_list [ where_clause ] [ group_by_clause ] [ HAVING condition ] [ order_by_clause ]
【语法说明】
◆ SELECT:查询动作关键词,查询语句的第一个单词。
◆ [DISTINCT|ALL]:描述列中的数据是否去除重复记录。
◆ select_list:需要查询的列,也可以说是占位符。可以是一个字段,也可以是多个字段。
◆ FROM:关键词,表示数据的来源。
◆ table_list:数据来源列表,允许有一个或多个;可以是表名,也可以是视图,还可以是别名。
◆ [ where_clause ]:查询条件部分。
◆ [ group_by_clause ]:GROUP BY子句部分。
◆ [ HAVING condition ]:HAVING子句部分。
◆ [ order_by_clause ]:排序部分。
5.1.2 查询各关键词的顺序及功能
SELECT语句中的子句顺序非常重要。可以省略可选子句,但这些子句在使用时必须按适当的顺序出现。各子句在SELECT查询语句中的排列顺序及主要作用如表5.1所示。
表5.1 SELECT查询语句各子句的排列顺序及作用
如果在同一个SELECT查询语句中,用到了表5.1中所示的一些查询子句,则各查询子句就依照它们的顺序序号由低到高的顺序排列。
5.1.3 查询语句执行顺序
为了让数据库管理系统显示表中的值,最简单的就是执行带有FROM子句的SELECT语句。而在实践中,几乎所有的SELECT语句都包括强制输出数据满足某种标准的WHERE子句;另外,许多SELECT语句涉及从多个表中选择列的问题。
当执行SELECT语句时,数据库管理系统的执行步骤可表示如下。
step 1 首先执行FROM子句,组装来自不同数据源的数据,即根据FROM子句中的一个或多个表创建工作表。如果在FROM子句中有两个或多个表,数据库管理系统将执行CROSS JOIN运算对表进行交叉连接,作为工作表。
step 2 如果有WHERE子句,实现基于指定的条件对记录行进行筛选,即数据库管理系统将WHERE子句列出的搜索条件作用于第1步中生成的工作表。数据库管理系统将保留那些满足搜索条件的行,删除那些不满足搜索条件的行。
step 3 如果有GROUP BY子句,它将把数据划分为多个分组。数据库管理系统将第2步生成的结果表中的行分成多个组,每个组中所有行的group_by_expression字段具有相同的值。接着,数据库管理系统将每组减少到单行,而后将其添加到新的结果表中,用以代替第1步的工作表。
step 4 如果有HAVING子句,它将筛选分组。数据库管理系统将HAVING子句列出的搜索条件作用于第3步生成的“组合”表中的每一行。数据库管理系统将保留那些满足搜索条件的行,删除那些不满足搜索条件的行。
step 5 将SELECT子句作用于结果表,删除结果表中不包含在select_list中的列。如果SELECT子句包含DISTINCT关键词,数据库管理系统将从结果中删除重复的行。
step 6 如果有ORDER BY子句,则按指定的排序规则对结果进行排序。
step 7 对于交互式的SELECT语句,在屏幕上显示结果;对于嵌入式的SQL,使用游标将结果传递给宿主程序。
以上就是SELECT语句的基本执行过程。对于初学者来讲,可能很难理解。之所以在这里提出,是为了让读者对SELECT语句有一个整体的了解。下面就从简单的查询开始,详细讲解SELECT查询语句的具体使用。
5.2 最容易理解的查询
DBMS将NULL值看做是相等的,而且把所有的NULL值都放入自己的组中。
在SELECT语句中,只使用FROM子句即可实现最简单的列查询。有时甚至可以不使用FROM子句,只用SELECT子句实现无数据源检索。
5.2.1 增加实验数据
为了便于通过实例讲解SELECT查询语句的使用,本章的所有查询实例均针对前面创建的BOOKINFO表,且向该表中添加一些数据。
在Oracle数据库中,向表中添加数据可以使用Oracle企业管理器,或SQL Developer工具。不过,对于初学者,可以使用最常用的SQL*Plus工具,也可以使用PL/SQL提供的INSERT INTO语句。这里向BOOKINFO表添加的数据如表5.2所示。
表5.2 BOOKlNFO表中的数据
使用PL/SQL的INSERT INTO语句向BOOKINFO表中添加数据的代码如下:
insert into BOOKINFO (BOOKID, BOOKNAME, PUBLISH, PUBDATE, PRICE, AUTHOR, STORE, READER, REMARKS) values(4001, ' HTML教程', '青年出版社', to_date('01-09-2004', 'dd-mm-yyyy'), 35, '王宾,李宏', '1', 9702, null); insert into BOOKINFO (BOOKID, BOOKNAME, PUBLISH, PUBDATE, PRICE, AUTHOR, STORE, READER, REMARKS) values(4002, 'Photoshop 设 计 基础 ', ' 大众 出 版 社 ', to_date('01-02-2005', 'dd-mm-yyyy'), 47, '刘小静', '1', 9707, null); insert into BOOKINFO (BOOKID, BOOKNAME, PUBLISH, PUBDATE, PRICE, AUTHOR, STORE, READER, REMARKS) values(4003, 'Photoshop 实例设计基础', '大众出版社', to_date('01-02-2006', 'dd-mm-yyyy'), 40, '刘小静', '1', 9705, null); insert into BOOKINFO (BOOKID, BOOKNAME, PUBLISH, PUBDATE, PRICE, AUTHOR, STORE, READER, REMARKS)values(4004, 'Photoshop 设 计 基 础 ', ' 大 众 出 版 社 ', to_date('01-02-2005', 'dd-mm-yyyy'), 47, '刘小静', '0', null, null); insert into BOOKINFO (BOOKID, BOOKNAME, PUBLISH, PUBDATE, PRICE, AUTHOR, STORE, READER, REMARKS) values(4006, '数字图形处理', '科学出版社', to_date('01-08-2002', 'dd-mm-yyyy'), 56, '王刚,赵明', '0', null, null); insert into BOOKINFO (BOOKID, BOOKNAME, PUBLISH, PUBDATE, PRICE, AUTHOR, STORE, READER, REMARKS) values(4007, 'Photoshop 设 计 基础 ', ' 大众 出 版 社 ', to_date('01-03-2005', 'dd-mm-yyyy'), 45, '刘小静', '1', 9727, null); commit;
以上的脚本并没有把所有列出的数据都放进数据库中,而是选择了一部分存入数据库当中,如果读者有需求,可以根据自己的实际情况自行插入数据。
5.2.2 查询表中列的数据
一般情况下,数据库中的每个表都包含若干列信息。用户在查询表中的记录时,大多数情况下只是关心表的一列或者几列的信息。这时,只需要使用SELECT语句的常规使用方式即可。相关语法结构如下:
01 SELECT 列名1[,列名2,…列名n] 02 FROM 表名
【语法说明】
◆ 列名允许有多个。
◆ 表名允许有多个。
SELECT关键词指明了要查询的表的列名称,FROM关键词指明了要查询的列所在表的名字。
【实例5-1】查询单列数据。
要求查询BOOKINFO表中的BOOKNAME列的数据,相关脚本如下:
01 SELECT BOOKNAME 02 FROM BOOKINFO;
【代码解析】
◆ SELECT:查询关键词。
◆ BOOKNAME:实例中要求查询数据的列。
◆ FROM:后面接数据的来源。
◆ BOOKINFO:数据的来源表。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.1。
图5.1 查询BOOKNAME列的数据
对比上面的数据插入脚本,查询结果包含了BOOKNAME列的所有数据,即包括了重复的数据,且查询结果集中数据的顺序与其在表中的存储位置一致。
在SQL语言中,SQL关键词对大小写不敏感,所以对SELECT关键词来说,SELECT、select或者Select都是一样的。在Oracle数据库中,其字段名称对大小写也不敏感。
除了实例中介绍的查询单列外,也可以一次查询多列数据。多列查询需要在SELECT关键词后指定要查询的列,但是各列之间必须用逗号分开。注意:在列出的最后一列的名字后面不能加逗号,否则会造成语法错误。
【实例5-2】查询多列数据。
要求查询BOOKINFO表中的BOOKNAME、BOOKID、PRICE和PUBDATE列的数据,相关脚本如下:
01 SELECT BOOKNAME,BOOKID,PRICE,PUBDATE 02 FROM BOOKINFO
【代码解析】
◆ BOOKNAME,BOOKID,PRICE,PUBDATE是准备查询数据的列名。注意:最后一个列名后面没有逗号。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.2。
图5.2 查询多列数据
在查询时,除了可以直接使用表中的列名外,还可以为列指定一个别名,别名通常都是有实际意义的单词或词组。通常为列名指定别名有两种方式,写法如下:
01 column c_alias 02 column AS c_alias
第一种方式是在原列后面加空格,然后加该列的别名;第二种方式则是在原列的后面使用关键词“AS”,然后加别名。
【实例5-2】中的脚本如果使用别名的话,可以参考下面的脚本:
SELECT BOOKNAME 书名,BOOKID ID,PRICE AS 价格,PUBDATE FROM BOOKINFO
这段脚本的执行效果这里不再给出,读者可以自行实验。别名虽然简单,但在复杂的查询中往往有简化SQL语句的效果。
5.2.3 查询所有列
除了能够进行单列查询和多列查询以外,使用SELECT语句还可以查询表中的所有列,这是通过“*”(星号)通配符实现的。其相关语法如下:
01 SELECT * 02 FROM table_name
【代码说明】
◆ *:表示查询所有的列。
◆ table_name:数据源部分。
使用“*”通配符,查询结果将列出表中所有列的元素,而不必指明各列的列名,这在用户不清楚表中各列的列名时非常有用。服务器会按用户创建表时声明列的顺序来显示所有的列。
【实例5-3】查询表中所有列的记录。
要求查询BOOKINFO表中所有列的记录,相关脚本如下:
01 SELECT * --使用通配符 02 FROM BOOKINFO
【执行效果】
在SQL*Plus中执行该脚本,效果见图5.3。
由于查询时没有对查询结果进行格式化,所以出现了列名自动换行的情况,读者可以自行进行相关设置,或在SQL Developer工具中进行该操作。
使用“*”通配符时要慎重,在不需要查询所有列时,尽量采用前面介绍的单列查询或多列查询,以免占用过多的资源。
图5.3 查询所有数据
5.2.4 去除查询结果中的重复记录
使用前面介绍的最基本的查询方式会返回从表中搜索到的所有行的数据,而不管这些数据是否重复,这常常不是用户所希望看到的。使用DISTINCT关键词,就能够从返回的结果集中删除重复的行,使返回的结果更简洁。在SELECT子句中,通过指明DISTINCT关键词去除列中的重复信息。相关语法如下:
SELECT DISTINCT select_list FROM table_name
【语法说明】
◆ DISTINCT:去除重复记录的关键词。
◆ select_list:列名列表。DISTINCT作用于后面的所有列名上。
◆ table_name:数据源。
DISTINCT关键词去除的是SELECT子句查询的列的重复信息。如果SELECT子句查询的列为多列,那么只有这些列的信息同时重复的记录才被去除。
【实例5-4】查询BOOKINFO表中所有的图书名称,不允许有重复的记录。
要求查询BOOKINFO表中存在的所有图书的名称,即查询BOOKNAME列的数据,且去除重复的记录。相关脚本如下:
01 SELECT DISTINCT BOOKNAME 02 FROM BOOKINFO
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.4。
图5.4 DlSTlNCT的使用
对比前面实例的查询结果,这里将重复的记录删除了,同时对结果集中的数据按照升序方式进行了排序。实际上,数据库的操作过程是先对查询结果排序,然后查找并从结果集中去除重复的值。
正因为如此,使用DISTINCT是要付出代价的。因为要去掉重复值,必须对结果集中的记录进行排序,使得相同的记录聚集在一起,只有按这种方法对记录进行分组,才能去掉重复值,而这一工作甚至比查询本身还费时间。
在使用DISTINCT关键词后,如果表中有多个为NULL的数据,服务器会把这些数据视为相等。
【实例5-5】DISTINCT对NULL值的处理。
要求查询BOOKINFO表中所有读者的编号,即查询READER列的数据,且去除重复的记录。相关脚本如下:
01 SELECT DISTINCT READER 02 FROM BOOKINFO
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.5。可见,所有的NULL值被视为相等的,且排序时在最前面。
图5.5 NULL的处理
5.2.5 返回查询的部分数据
Oracle数据库中提供了ROWNUM关键词,让用户指定返回前面一定数量的数据。当查询到的数据量非常庞大,比如有100万行,但没有必要对所有数据进行浏览时,使用ROWNUM关键词查询可以大大减少查询花费的时间。
ROWNUM其实是Oracle数据库中为查询结果顺序分配的行的编号,它是一个伪字段。查询结果中返回的第一行分配的是1,第二行是2。
【实例5-6】查询BOOKINFO表的前2行记录。
要求使用ROWNUM关键词查询BOOKINFO表的前2行记录,相关脚本如下:
01 SELECT BOOKNAME, PUBLISH, READER 02 FROM BOOKINFO 03 WHERE ROWNUM < 3 04 /
【代码解析】
◆ 第1行给出了要查询的列名。
◆ 第3行表示查询前2行记录。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.6。
图5.6 ROWNUM的使用
ROWNUM关键词在查询前N条数据时虽然很好用,但有些地方初学者应该注意:它从1开始,所以查询结果的第1行记录的ROWNUM为1。可以使用如下的脚本来查询第1行数据:
SELECT BOOKNAME, PUBLISH, READER FROM BOOKINFO WHERE ROWNUM = 1
不可以出现类似于“ROWNUM=2”这样的情况。
5.3 带条件的查询
一个数据表中存放着大量相关的记录数据,在实际使用时,往往只需要其中满足要求的部分记录,这时就需要用到WHERE条件子句。
5.3.1 单一的条件查询
WHERE子句允许指定查询条件,使得SELECT语句的结果集中只包含那些满足查询条件的记录。语法格式如下:
SELECT 列名1[,列名2,…列名n] FROM 表名 WHERE 条件
使用WHERE子句可以限制查询的范围,提高查询效率。在使用时,WHERE子句必须紧跟在FROM子句后面。WHERE子句中的条件表达式包括算术表达式和逻辑表达式两种,WHERE子句中查询条件的数目没有限制。
【实例5-7】查询BOOKINFO表中PUBLISH列为科学出版社的记录。
要求使用WHERE子句,查询BOOKINFO表中PUBLISH列为科学出版社的所有记录。相关脚本如下:
01 SELECT BOOKNAME, PUBLISH 02 FROM BOOKINFO 03 WHERE PUBLISH='科学出版社' 04 /
【语法说明】
◆ 第3行表示查询数据的条件。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.7。
图5.7 条件查询效果
当处理带WHERE子句的SELECT语句时,数据库对输入表的每一行应用搜索条件,从而进行筛选。具体来讲,就是用行中列的值代替WHERE子句中的列名。在本例中,数据库管理系统依次取出PUBLISH列中的记录,将其与“科学出版社”比较,满足条件则保留,否则就排除在结果表外。
在该例中,WHERE子句中使用了“=”运算符,它要求两边的数值类型必须相同。PUBLISH列为varchar2类型,所以后面的“科学出版社”必须写在单引号内,表明它是一个字符串;如果是数值,比如是integer类型,直接写值即可。
5.3.2 查询中使用比较表达式
在WHERE子句中,可以使用比较表达式作为搜索条件。其一般表达形式如下所示:
达式 算术运算符 表达式
其中的表达式可以为常量、变量和列表达式的任意有效组合。
在WHERE子句中允许使用的比较运算符包括:
◆ =(等于):表示查询出列的数据要和提供的数据相等。
◆ <(小于):表示查询出列的数据要小于提供的数据。
◆ >(大于):表示查询出列的数据要大于提供的数据。
◆ <>(不等于):表示查询出列的数据和提供的数据不能相同。
◆ <=(小于等于):表示查询出列的数据要小于或等于提供的数据。
◆ >=(大于等于):表示查询出列的数据要大于或等于提供的数据。
◆ !=(不等于):它和“<>”起相同的作用。
算术表达式的使用比较简单。下面就通过具体的实例讲一下比较运算符的用法。
【实例5-8】查询BOOKINFO表中价格不超过50元的图书记录。
要求查询BOOKINFO表中价格低于50元的图书记录。相关脚本如下:
01 SELECT BOOKNAME,PRICE,PUBLISH 02 FROM BOOKINFO 03 WHERE PRICE < 50 04 /
【代码解析】
◆ 第1行表示要查询数据的列名。
◆ 第3行表示价格低于50元。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.8。
图5.8 价格不超过50元的图书
实际上,除了可以对数值类型的字段使用比较运算符外,对于字符串类型的字段也可以使用比较运算符。对于如“>”、“>=”、“<”等比较运算符,是根据字符的排列顺序比较的。
字符串的排序是根据其首字母的顺序进行的,如果首字符相同,则比较其下一个字符,依此类推。而对于汉字的排序,是根据其汉语拼音的第一个字母的顺序进行的,如果第1个字母相同,则比较第2个字母,依此类推,且汉字的顺序要高于字母的顺序。
【实例5-9】使用比较运算符比较字符。
要求查询BOOKINFO表的BOOKNAME字段中图书名称字符串小于等于“多媒体基础”的所有记录。相关脚本如下:
01 SELECT BOOKNAME,PRICE,PUBLISH 02 FROM BOOKINFO 03 WHERE BOOKNAME<='多媒体基础' 04 /
【代码解析】
◆ 第1行表示查询的列。
◆ 第3行使用了比较运算符比较字符串,字符串需要用单引号括起来。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.9。
图5.9 比较字符串
从图5.9所示的结果不难理解比较运算符对字符串的操作结果。另外,在比较运算符中,不等于运算符“<>”和“!=”也经常使用,二者实现的功能完全相同。
【实例5-10】不等于运算符“<>”的使用。
要求查询BOOKINFO表中除了“大众出版社”外,其他出版社的图书记录。相关脚本如下:
01 SELECT BOOKNAME,PRICE,PUBLISH 02 FROM BOOKINFO 03 WHERE PUBLISH<>'大众出版社'
【代码解析】
◆ 实例中使用了“<>”运算符,它表示PUBLISH列的数据不为“大众出版社”。这里也可以使用“!=”。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.10。
图5.10 不等于运算符的使用
5.3.3 使用简单逻辑表达式
在WHERE子句中,可以使用多个搜索条件选择记录(行),即通过逻辑运算符(NOT、AND或OR)将多个单独的搜索条件结合在一个WHERE子句中,形成一个复合的搜索条件。当对复合的搜索条件求值时,数据库管理系统会对每个单独的搜索条件求值,然后执行布尔运算来决定整个WHERE子句的值是TRUE还是FALSE。只有那些满足整个WHERE子句的值是TRUE的记录才出现在结果表中。
1.AND运算符
AND运算符表示逻辑“与”的关系。当使用AND运算符组合两个逻辑表达式时,只有当两个表达式均为TRUE时才返回TRUE。其基本语法如下:
boolean_expression AND boolean_expression
AND运算符的真值表如表5.3所示。
表5.3 AND运算符的真值表
在查询中可以使用AND运算符进行多个条件查询,表示查询的数据必须同时符合多个条件。
【实例5-11】使用AND运算符进行多条件查询。
要求查询BOOKINFO表中已经被借出(STORE值为1)的大众出版社的图书记录。相关脚本如下:
01 SELECT BOOKNAME,PRICE,PUBLISH,STORE 02 FROM BOOKINFO 03 WHERE STORE=1 04 AND PUBLISH='大众出版社' 05 /
【代码解析】
◆ 第1行是准备查询的列。
◆ 第3行表示要求STORE列中的数据值为“1”。
◆ 第4行利用“AND”表示除了第3行的条件外,还要满足PUBLISH列的数据为“大众出版社”才可以。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.11。
图5.11 AND多条件查询
在一个WHERE子句中,也可以同时使用多个AND运算符连接多个查询条件。这时,只有满足所有的查询条件的记录,才被包括在结果表中。
2.OR运算符
OR运算符实现逻辑“或”的运算关系。当使用OR运算符组合两个逻辑表达式时,只要其中一个表达式的条件为TRUE,结果便返回TRUE。其基本语法如下:
boolean_expression OR boolean_expression
OR运算符的真值表如表5.4所示。
表5.4 OR运算符的真值表
OR运算符的优先级低于AND运算符,即在AND运算符之后才对OR运算符求值。不过,使用括号可以更改求值的顺序。
【实例5-12】使用OR运算符进行多条件查询。
要求查询BOOKINFO表中大众出版社或者价格高于50元的图书记录。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,STORE,READER 02 FROM BOOKINFO 03 WHERE PUBLISH='大众出版社' 04 OR PRICE>50 05 /
【代码解析】
◆ 第3行表示查询PUBLISH列中为“大众出版社”的数据。
◆ 第4行表示除了第3行的数据外,也可以是PRICE列中大于50的数据。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.12。
图5.12 OR多条件查询
3.NOT运算符
NOT运算符实现逻辑“非”的运算关系,用于对搜索条件的逻辑值求反。其语法如下:
NOT boolean_expression
NOT运算符的真值表如表5.5所示。
表5.5 NOT运算符的真值表
NOT运算符表示对某个条件的否定,下面的实例演示了如何使用该关键词。
【实例5-13】使用NOT运算符进行多条件查询。
要求查询BOOKINFO表中非大众出版社的图书记录。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,STORE,READER 02 FROM BOOKINFO 03 WHERE NOT PUBLISH='大众出版社' 04 /
【代码解析】
◆ 第3行利用了NOT关键词,它指定搜索PUBLISH列中不为“大众出版社”的所有数据。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.13。
图5.13 NOT运算符的使用
NOT运算符对紧跟其后的条件取反,比如在本实例中,“NOT PUBLISH='大众出版社'”语句实际上就等价于“PUBLISH!='大众出版社'”或者“PUBLISH <>'大众出版社'”语句。
在实际应用中,使用NOT运算符时,经常忽视其对NULL值的处理问题。要时刻牢记一点, NOT NULL的结果仍为NULL。比如下面的实例。
【实例5-14】NOT运算符对NULL值的处理。
要求在BOOKINFO表中查询读者编号不大于9704(包括没有读者)的所有图书记录。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,STORE,READER 02 FROM BOOKINFO 03 WHERE NOT READER>9704
【代码解析】
◆ 第3行使用了NOT关键词,它所表示的含义是对后面的READER>9704条件取反,也就是查询不大于9704的数据。
【执行效果】
在SQL*Plus中执行以上脚本,效果见图5.14。
图5.14 查询结果
从图5.14中可以看出,读者编号(READER)为NULL的值并没有出现在结果表中。因为当READER为NULL时,“READER>9704”这个条件是无法查出READER为NULL的数据的,而NOT NULL表示不为空,所以此时NULL数据没有被查询出来。
在WHERE子句中,有关空值(NULL)的判断与操作在5.3.4节会进行详细的介绍。
4.AND、OR、NOT的组合使用
在WHERE子句中,各种逻辑运算符可以组合使用,即AND、OR、NOT运算符可以同时使用。与使用算术运算符进行运算一样,使用逻辑运算符也存在运算的优先级问题。NOT运算符的优先级最高,而后是AND,最后是OR。要特别注意:当AND和OR运算符同时使用时,即当出现下面的运算关系时:
condition1 OR condition2 AND condition3
其运算实际上等价于:
condition1 OR (condition2 AND condition3)
下面通过一个实例来说明运算符的优先级顺序。
【实例5-15】逻辑运算符的优先级。
要求查询BOOKINFO表中所有的大众出版社和科学出版社并且价格不高于50元的图书记录。如果采用下面的脚本:
01 SELECT BOOKNAME,PRICE,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE PUBLISH='大众出版社' 04 OR PUBLISH='科学出版社' 05 AND NOT PRICE>50 06 /
则出现如图5.15所示的查询结果。
图5.15 错误的查询结果
由图5.15中标出部分可以看出并没有得到预期的结果。下面就根据逻辑运算符的优先级顺序分析WHERE子句中代码的执行过程。
◆ 首先,数据库管理系统执行NOT运算,即,结果集中的记录需要满足价格不高于50元。
◆ 然后,数据库管理系统执行AND运算,即,结果集中的记录需要满足科学出版社且价格不高于50元。
◆ 最后,数据库管理系统执行OR运算,即,结果集中的记录需要满足大众出版社或者满足科学出版社且价格不高于50元。
显然这与预期的结果不相符。如果要得到正确的结果,就需要使用括号改变运算的顺序。比如下面的脚本:
01 SELECT BOOKNAME,PRICE,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE (PUBLISH='大众出版社' OR PUBLISH='科学出版社') 04 AND NOT PRICE > 50 05 /
【代码解析】
◆ 第3行使用了小括号,表示查询数据中PUBLISH列的查询结果只能是“大众出版社”或“科学出版社”。
◆ 第4行表示除了第3行的条件外,也必须满足价格不大于50元这个条件。
【执行效果】
以上脚本执行后,查询结果见图5.16。
图5.16 正确的查询结果
当OR运算符和AND运算符同时使用时,建议用户不要考虑其默认的优先级顺序,而是采用括号( )来实现所需要的执行顺序,这样可以增强程序的可读性。
5.3.4 有关NULL值的判断
通过前面的学习可知,除非在创建时指定了NOT NULL约束,否则数据库表中某些列的值可以为NULL。什么是NULL?顾名思义,NULL就是空,在数据库中,含有空值的表的列长度为零。
具体来讲,NULL与0、空字符串、空格都不同,它等价于没有任何值,是未知数。然而,NULL却是一个数据值,而且它属于一个域。例如一个字符串字段,其中的NULL只能是一个字符串。尽管它的内容没有定义,或者未知,但它是字符串,这一点毋庸置疑。
对于NULL,一般的运算,比如加、减、乘、除都会返回NULL。永远不会有什么数据等于NULL。注意:NULL也不等于NULL,说一个NULL等于NULL是错误的。
在SQL中如果出现表达式“X IS NULL”,那么当X是NULL时,就返回TRUE;如果X不为NULL时,则返回FALSE。
因此,在WHERE子句中,判断一个值是否为NULL,只能采用IS [NOT] NULL判别式。下面通过具体实例说明IS [NOT] NULL判别式的用法。
【实例5-16】判别式处理NULL值。
要求在BOOKINFO表中查询读者编号不大于9704(包括没有读者,为NULL)的所有图书记录。相关脚本如下:
01 SELECT BOOKNAME,PRICE,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE NOT READER>9704 04 OR READER IS NULL 05 /
【代码解析】
◆ 第3行实际上是查询READER列小于等于9704的数据。
◆ 第4行表示或者的意思,也就是说,查询结果也允许READER列中的数据为空。这种用法很普遍。
【执行效果】
以上脚本执行后,查询结果见图5.17。
图5.17 查询结果
X IS NULL不可以写成X=NULL;除了IS [NOT] NULL之外,空值不满足任何查找条件;如果NULL参与算术运算,则该算术表达式的值为NULL;如果NULL参与聚集运算,则除count(*)之外其他聚集函数都忽略NULL。
5.3.5 有关模糊查询
在实际的应用中,用户不会总是能够给出精确的查询条件。因此,经常需要根据一些并不确切的线索来搜索信息。PL/SQL提供了LIKE子句来进行这类模糊查询,语法格式如下:
表达式 [NOT] LIKE 条件
在大多数情况下,LIKE子句会与通配符配合使用。PL/SQL中提供了2个通配符供用户灵活地实现复杂的模糊查询条件,这两个通配符分别如下。
◆ _:可以替代一个字符。
◆ %:可以替代个数不确定的字符。
下面就具体介绍各通配符的使用方法。
1.“_”通配符
“_”通配符只能匹配任何单个字符。比如,“_ean”表示将查询以“ean”结尾的所有4个字母的字符串(如“Dean”、“2ean”、“Sean”等)。当然,要表示两个字符的匹配,就需要使用两个“_”通配符,即写成“__”即可。
只有在用户确定所要查询的字符串的个数,只是不确定其中的一个或几个字符的确切值时,才能使用“_”通配符。
下面用两个实例来帮助读者理解该通配符的使用方法。
【实例5-17】使用“_”通配符实现模糊查询。
要求在BOOKINFO表中查询书名以“数字”开头,以“处理”结尾的6个字的图书记录。相关脚本如下:
01 SELECT BOOKNAME,PRICE,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE BOOKNAME LIKE '数字__处理' 04 /
【代码解析】
◆ 第3行进行了模糊查询,即利用了两个“_”通配符,单引号内表示为6个字,其中通配符部分为任意字符。
【执行效果】
以上脚本执行后,查询结果见图5.18。
图5.18 查询以“数字”开头,以“处理”结尾的6个字的图书记录
利用该通配符,可以实现查询固定长度的字符串。下面的实例演示了这种使用方法。
【实例5-18】使用“_”通配符实现查询固定长度的数据。
要求查询BOOKINFO表中BOOKNAME列的长度为13的所有数据。相关脚本如下:
01 SELECT BOOKNAME,PRICE,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE BOOKNAME LIKE '_____________' 04 /
【代码解析】
◆ 第3行使用了13个“_”通配符,表示长度为13的字符串。
【执行效果】
以上脚本执行后,查询结果见图5.19。
图5.19 查询BOOKNAME列长度为13的数据
从图5.19中可以看出,列出的数据是符合要求的。在某种情况下,也需要查询指定长度之外的数据,那么可以参考下面的实例。
【实例5-19】演示NOT和“_”通配符一起使用。
要求查询BOOKINFO表中BOOKNAME列的长度为13之外的所有数据。相关脚本如下:
01 SELECT BOOKNAME,PRICE,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE BOOKNAME NOT LIKE '_____________' 04 /
【代码解析】
◆ 第3行中使用了NOT关键词,使查询结果与【实例5-18】的查询结果相反。
【执行效果】
以上脚本执行后,查询结果见图5.20。
图5.20 使用NOT和“_”通配符
2.“%”通配符
“%”通配符和“_”通配符不同,它表示任意字符的匹配,且不计字符的多少。如“计算机%”表示匹配以字符串“计算机”开头的任意字符串;“%计算机”表示匹配以字符串“计算机”结尾的任意字符串;“%计算机%”表示匹配含有字符串“计算机”的任意字符串。
【实例5-20】使用“%”通配符进行模糊查询。
要求在BOOKINFO表中查询图书名称中含有“设计基础”字符串的所有图书记录。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE BOOKNAME LIKE '%设计基础%' 04 /
【代码解析】
◆ 第3行使用了“%”通配符,它可以表示多个字符,这里表示BOOKNAME中的数据只要包含“设计基础”就符合要求。
【执行效果】
以上脚本执行后,查询结果见图5.21。
图5.21 利用“%”查询数据
可见,图书名称中只要含有“数据库”字符串的记录均出现在结果表中,而不论其在书名中的位置。
此外,使用“%”通配符还可以指定开头和结尾同时匹配的方式,这在实际应用中使用也较多。
【实例5-21】使用“%”通配符实现头、尾匹配。
要求在BOOKINFO表中查询图书名称中以“Ph”字符串开头,以“基础”字符串结尾的所有图书记录。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE BOOKNAME LIKE 'Ph%基础' 04 /
【代码解析】
◆ 第3行使用了通配符,表示查询BOOKNAME中以“Ph”字符串开头,以“基础”字符串结尾的所有图书记录。
【执行效果】
以上脚本执行后,查询结果见图5.22。
图5.22 查询以“Ph”开头,以“基础”结尾的所有图书记录
虽然数据库中对字段或关键词的大小写不加区分,但表中的数据记录是区分大小写的。
如果把以上脚本写成下面的形式,是无法检索出对应数据的。
SELECT BOOKNAME,PUBLISH,READER FROM BOOKINFO WHERE BOOKNAME LIKE 'PH%基础' /
除此之外,“%”通配符还经常用在NOT LIKE语句中实现排除查询。下面的实例给读者演示了该通配符如何应用在NOT LIKE语句中。
【实例5-22】使用“%”通配符实现排除查询。
要求在BOOKINFO表中查询所有的不是基础类的图书记录,即书名中不能包含“基础”字符串。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE BOOKNAME NOT LIKE '%基础%' 04 /
【代码解析】
◆ 第3行使用了NOT关键词,表示查询的数据不包含“基础”两个字。
【执行效果】
以上脚本执行后,查询结果见图5.23。
图5.23 模糊查询中的排除查询
在实际工作当中,开发者可以根据情况同时使用这两个通配符来达到预期的目的。
5.3.6 指定数据范围
在WHERE子句中,使用BETWEEN关键词可以更方便地限制查询数据的范围。当然,还可以使用NOT BETWEEN关键词查询限定数据范围之外的记录。
其语法格式可表示如下:
表达式[NOT] BETWEEN 表达式1 AND 表达式2
【语法说明】
◆ 表达式:待指定范围的表达式。
◆ 表达式1:限定范围的起始。
◆ 表达式2:限定范围的结束。
比如,选择范围在10~100之间的数,采用BETWEEN运算符可以表示为BETWEEN 10 AND 100。
使用BETWEEN限制查询数据范围时同时包括了边界值,而使用NOT BETWEEN进行查询时没有包括边界值。
【实例5-23】使用BETWEEN限定数据范围查询。
要求在BOOKINFO表中查询图书价格在35~60元之间的所有图书记录。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,PRICE 02 FROM BOOKINFO 03 WHERE PRICE BETWEEN 35 AND 60 04 /
【代码解析】
◆ 第3行表示利用BETWEEN…AND语句限定PRICE列中数据的范围,利用该查询条件可满足实例的要求。
【执行效果】
以上脚本执行后,查询结果见图5.24。
图5.24 查询BOOKlNFO表中图书价格在35~60元之间的记录
从结果可见,使用BETWEEN关键词查询包含了边界值。
实际上,使用BETWEEN表达式进行查询的效果完全可以用含有“>=”和“<=”的逻辑表达式来代替,使用NOT BETWEEN进行查询的效果完全可以用含有“>”和“<”的逻辑表达式来代替。
对于该实例,若采用含有“>=”和“<=”的逻辑表达式来代替BETWEEN表达式,可以参照如下的脚本:
SELECT BOOKNAME,PUBLISH,PRICE FROM BOOKINFO WHERE PRICE >=35 AND PRICE <=60 /
执行该代码,会得到同样的查询结果。
实际上,使用BETWEEN运算符也可以实现时间的比较,下面就给出一个具体的应用实例。
【实例5-24】使用BETWEEN运算符限定时间范围查询。
要求在BOOKINFO表中查询出版日期在2004-9-1和2006-1-1之间的所有图书记录。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,PUBDATE 02 FROM BOOKINFO 03 WHERE PUBDATE 04 BETWEEN TO_DATE('2004-9-1','YYYY-MM-DD') 05 AND TO_DATE('2006-1-1','YYYY-MM-DD') 06 /
【代码解析】
◆ 第4行和第5行表示PUBDATE列中的数据要在2004-9-1和2006-1-1之间,并包含这两个边界。这里使用了TO_DATE函数,把提供字符串转换成了日期类型进行相关查询。
【执行效果】
以上脚本执行后,查询结果见图5.25。
图5.25 指定BOOKlNFO表中出版日期的范围查询
5.3.7 如何限制检索数据范围
同BETWEEN关键词一样,IN的引入也是为了更方便地限制检索数据的范围,灵活地使用IN关键词,可以用简洁的语句实现结构复杂的查询。其语法格式如下:
表达式 [NOT] IN (表达式1 , 表达式2 [,…表达式n])
【语法说明】
◆ 表达式:待指定范围的表达式。
◆ 表达式1:代表某个具体含义的表达式。
◆ 表达式2:代表某个具体含义的表达式。
◆ 括号中允许有N个表达式,也就是说,允许有N个值,然后待指定范围的表达式会根据括号中的参数一一进行查询。
所有的条件在IN运算符后面罗列,并以括号( )包括起来,条件中间用逗号分开。当要判断的表达式处于括号中列出的一系列值之中时,IN运算符求值为TRUE。
【实例5-25】使用IN限定检索数据的范围。
要求在BOOKINFO表中查询9702、9705和9707读者所借阅的所有图书记录。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,READER 02 FROM BOOKINFO 03 WHERE READER IN (9702,9705,9707) 04 /
【代码解析】
◆ 第3行使用了IN关键词,它根据后面括号中提供的参数进行一一查询,当有符合要求的数据时会返回结果,当没有符合要求的数据时会检索下一个参数,然后继续查询,最终的返回结果是根据括号中的所有参数进行查询的结果。
【执行效果】
以上脚本执行后,查询结果见图5.26。
图5.26 查询BOOKlNFO表中9702、9705和9707读者所借阅的图书记录
在大多数情况下,OR运算符与IN运算符可以实现相同的功能。比如该实例采用OR运算符实现,脚本如下:
SELECT BOOKNAME,PUBLISH,READER FROM BOOKINFO WHERE READER =9702 OR READER =9705 OR READER =9707 /
这里就不给出查询结果了,感兴趣的读者可以自己进行试验。
IN运算符与OR运算符可以实现相同的功能,然而使用IN运算符更为简洁,特别是当选择的条件很多时,只需在括号内用逗号分隔各条件即可,其运行效率也比OR运算符要高。另外,使用IN运算符,其后面所有的条件可以是另一条SELECT语句,即子查询。这在后面的章节中会有详细介绍。
5.3.8 定义转义符
在使用PL/SQL进行数据查询时,在某种情况下会遇到特殊字符的问题,例如使用LIKE进行模糊查询时,当“%”和“_”符号单独出现时,就会被认为是通配符。但有时可能需要搜索包含一个或多个特殊通配符的字符串,例如,数据库表中可能存储含百分号(%) 的折扣值。若要搜索作为字符而不是通配符的百分号,必须提供ESCAPE关键词和转义符。相关语法如下:
ESCAPE 'escape_character'
【语法说明】
◆ 这个表达式表示在字符串中搜索通配符,但不将其作为通配符使用。
◆ 关于escape_character,则是放在通配符前表示该用途的字符,也就是说,利用该语法结构,escape_character后面的通配符失去通配符的意义,而是表示普通的字符。
下面用一个实例来演示该关键词的使用方法。
【实例5-26】演示ESCAPE的使用方法。
相关操作步骤如下:
step 1 创建NEW_TABLE表。下面是该表的创建脚本,测试记录同时包含在该脚本中。NEW_TABLE的创建脚本:
CREATE TABLE NEW_TABLE ( TESET VARCHAR2(12) )
NEW_TABLE的数据记录脚本:
insert into NEW_TABLE (TESET)values ('test%test'); insert into NEW_TABLE (TESET)values ('ab%test'); insert into NEW_TABLE (TESET)values ('cd_test'); insert into NEW_TABLE (TESET)values ('catest'); commit;
在SQL*Plus中或其他工具中执行以上脚本,可以获得该实例的测试表和数据。
step 2 要求查询NEW_TABLE表中所有以“%test”结束的记录,相关脚本如下:
01 SELECT * FROM NEW_TABLE; 02 SELECT * FROM NEW_TABLE WHERE TESET LIKE '%%test'; 03 SELECT * FROM NEW_TABLE WHERE TESET LIKE '%/%test' ESCAPE '/';
【代码说明】
◆ 第1行表示查询NEW_TABLE表中的所有数据。
◆ 第2行表示没有使用ESCAPE时进行查询,看是否能够得到预期的数据。
◆ 第3行利用了ESCAPE进行查询,看是否能够得到预期的数据。
【执行效果】
在SQL*Plus中分别执行以上3条语句,执行效果见图5.27。从图中的标记部分可以看出,利用ESCAPE关键词实现了实例中的要求;而上面未使用该关键词的查询,则把所有的数据都查询出来了。
图5.27 查询以“%test”结束的记录
5.4 排序查询结果
使用SELECT语句获得的数据一般是没有排序的(使用DISTINCT关键词除外)。为了方便阅读和使用,最好对查询结果进行一次排序。在SQL语言中,用于排序的是ORDER BY子句。
5.4.1 基本排序
使用ORDER BY子句可以指定在SELECT语句返回的列中所使用的排序顺序。其语法格式可表示如下:
ORDER BY { expr | position | c_alias } [ ASC | DESC ] [ NULLS FIRST | NULLS LAST ] [, { expr | position | c_alias } [ ASC | DESC ] [ NULLS FIRST | NULLS LAST ] ]...
【语法说明】
◆ ORDER BY项:排序关键词。
◆ expr项:表达式。
◆ position项:表中列的位置。
◆ c_alias项:别名。
◆ [ ASC | DESC ]项:正序或倒序。
◆ [ NULLS FIRST | NULLS LAST ]项:对空字段的处理方式。
◆ 可以根据多个字段排序。
其中,表达式是用于排序的列。可以对多列进行排序,各列在ORDER BY子句中的顺序决定了排序过程中的优先级。
需要注意的是:
◆ 在默认情况下,ORDER BY按升序进行排列,即默认使用的是ASC关键词。如果用户特别要求按降序进行排列,则必须使用DESC关键词。
◆ ORDER BY子句一定要放在所有子句的最后(无论包含多少子句)。
【实例5-27】基本排序操作。
要求在BOOKINFO表中查询所有的BOOKNAME、PUBLISH、PUBDATE信息,并将结果按照PUBDATE列的升序排序。相关脚本如下:
01 SELECT BOOKNAME,PUBLISH,PUBDATE 02 FROM BOOKINFO 03 ORDER BY PUBDATE 04 /
【代码解析】
◆ 第3行使用了ORDER BY进行排序,排序的字段是PUBDATE。默认是升序排序。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.28。
图5.28 按照PUBDATE列的升序排序的查询结果
可见,查询结果按照PUBDATE字段的升序顺序排列。
对于时间、数值类型的字段排序,其排序规则就是按照时间的早晚、数值的大小进行的;对于字符型的字段排序,则是依照其ASCII码的先后顺序进行的。
如果要使用降序方式排列数据,则需要指定DESC关键词。在排序时,空值(NULL)默认被看成最大的值,比如下面的实例。
【实例5-28】降序排序操作。
要求在BOOKINFO表中查询所有的BOOKNAME、PUBDATE、PRICE、READER信息,但结果按照READER列的降序排序。相关脚本如下:
01 SELECT BOOKNAME,PUBDATE,PRICE,READER 02 FROM BOOKINFO 03 ORDER BY READER DESC 04 /
【代码解析】
◆ 第3行表示对READER字段数据进行排序,由于使用了DESC关键词,所以这里表示降序排列。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.29。
图5.29 按照READER列的降序排序的查询结果
可见,按照降序方式排列,NULL值排在了最前面。有关NULL值,在PL/SQL中可以设置它所在的位置,在图5.29中是默认的情况。在下面的实例中演示了如何设置NULL的位置。
【实例5-29】在降序排序中设置NULL的位置。
要求在BOOKINFO表中查询所有的BOOKNAME、PUBDATE、PRICE、READER信息,但结果按照READER列的降序排序,而且NULL值只能排在最后。相关脚本如下:
01 SELECT BOOKNAME,PUBDATE,PRICE,READER 02 FROM BOOKINFO 03 ORDER BY READER DESC 04 NULLS LAST 05 /
【代码解析】
◆ 第3行以READER字段进行降序排序。
◆ 第4行利用“NULLS LAST”关键词把排序列中的NULL值放到查询结果的最后。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.30。
图5.30 NULL值放到查询结果最后
该实例同【实例5-28】进行比较可以看出,利用“NULLS LAST”关键词可以成功地对NULL值进行放置。
另外,在查询当中,也可以把查询列之外的其他列作为查询的排序列,比如下面的实例。
【实例5-30】使用查询列之外的列进行排序操作。
本实例在BOOKINFO表中查询所有的BOOKNAME、PUBDATE、PRICE信息,结果按照READER列的降序排序。相关脚本如下:
SELECT BOOKNAME,PUBDATE,PRICE FROM BOOKINFO ORDER BY READER DESC NULLS LAST /
对该实例不做解释,读者可直接查看查询结果,并同【实例5-28】进行对比,检查是否按照READER列进行了排序。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.31。
图5.31 按照READER列的降序排序的查询结果
可见,虽然排序列不在查询列中,但依然可以使用该列对查询结果进行排序。除此之外,也可以对别名列进行排序。
5.4.2 多列排序
在排序语法结构中可以看出,使用ORDER BY子句也可以根据两列或多列的结果进行排序,只要用逗号分隔不同的排序关键词就可以了。简单的语法结构如下所示:
ORDER BY col1 DESC,col2,……
其实际的排序结果是根据ORDER BY子句后面列名的顺序确定优先级的,即查询结果首先以列col1的降序顺序进行排序,只有当列col1出现相同的信息时,这些相同的信息再按col2列的升序进行排序,依此类推。
【实例5-31】对多列进行排序操作。
要求在BOOKINFO表中查询图书的BOOKNAME、PUBDATE、PRICE信息,结果按照BOOKNAME列的降序和PUBDATE列的升序顺序排序。相关脚本如下:
01 SELECT BOOKNAME,PUBDATE,PRICE 02 FROM BOOKINFO 03 ORDER BY BOOKNAME DESC, 04 PUBDATE ASC 05 /
【代码解析】
◆ 第3行对BOOKNAME列进行降序排序。
◆ 第4行对PUBDATE列进行升序排序。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.32。
图5.32 对多列进行排序操作
可见,系统根据排序的优先级,首先依据BOOKNAME字段的记录进行降序排序(汉语拼音的首字母),当BOOKNAME字段的记录相同时,再依据PUBDATE字段的记录进行升序排序。
另外,ORDER BY子句除了可以根据列名进行排序外,还支持根据列的相对位置(即序号)进行排序。比如下面的语句:
SELECT col1, col2, ……, coln FROM table_name ORDER BY 2, 1
ORDER BY子句后面的序号实际上就是SELECT关键词后面的列名的序号。此时ORDER BY子句就等同于ORDER BY col2,col1。
【实例5-32】使用序号对多列进行排序操作。
要求在BOOKINFO表中查询所有的BOOKNAME、PUBDATE、PRICE信息,并通过使用序号的方式对结果按照BOOKNAME列的降序和PUBDATE列的升序顺序排序。实现脚本如下:
01 SELECT BOOKNAME,PUBDATE,PRICE 02 FROM BOOKINFO 03 ORDER BY 1 DESC,2 04 /
【代码解析】
◆ 第3行利用列的序号进行了排序,列的序号从1开始,即该行的“1”对应着第1行中的“BOOKNAME”,而“2”对应着第1行中的“PUBDATE”。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.33。
图5.33 利用列序号进行排序
当ORDER BY所需要的列不在SELECT语句所选的范围内时,采用列的序号显然是行不通的。当然,可以将列的别名与序号混合起来解决这个问题。
从5.4.1节中的排序语法结构得知,还可以对列的别名进行排序,这样实现的效果和直接使用列名进行排序是一样的。下面的实例演示了如何对列的别名进行排序。
【实例5-33】使用序号、列名、别名对多列进行排序操作。
要求在BOOKINFO表中查询所有的BOOKNAME、PUBDATE、PRICE信息,并通过使用序号、列名和别名的方式对查询结果进行综合排序。实现脚本如下:
01 SELECT BOOKNAME AS 书名,PUBDATE 日期,PRICE 价格 02 FROM BOOKINFO 03 ORDER BY 1 DESC, 04 日期 ASC, 05 PRICE DESC 06 /
【代码解析】
◆ 第1行查询了3个列,其中BOOKNAME列的别名是“书名“,PUBDATE列的别名是“日期”,PRICE列的别名是“价格”。它们使用了不同的命名别名的方式,这在前面已经介绍过了。
◆ 第3行表示查询结果根据第1列进行降序排序。
◆ 第4行表示查询结果根据日期进行升序排序。
◆ 第5行表示查询结果根据PRICE列进行降序排序。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.34。
图5.34 使用序号、列名、别名对多列进行排序
从图5.34中可以看出,排序结果是成功的,也就是说,查询中支持这种混合的排序方式,但作者建议读者不要混合使用排序方式,因为这样的脚本降低了可读性。
5.5 PL/SQL中的运算符
运算符是一种符号,用来指定要在一个或多个表达式中执行的操作,执行列、常量或变量之间的数学运算和比较操作。而表达式则是标识符、值和运算符的组合。简单的表达式可以是一个常量、函数、列名、变量与子查询。可以用运算符将两个或更多的简单表达式连接起来组成复杂的表达式。PL/SQL中的运算符包括:算术运算符、位运算符、比较运算符、逻辑运算符、赋值运算符、字符串连接符和一元运算符。
5.5.1 算术运算符
算术运算符用于执行数字型表达式的算术运算。在PL/SQL中支持的算术运算及其说明如下。
◆ +(加):加法或正号。
◆ -(减):减法或负号。
◆ *(乘):乘法操作。
◆ /(除):除法操作。
5.5.2 比较运算符
比较运算符用来比较两个表达式的大小。在PL/SQL中可以支持的比较运算符主要包括:
◆ >:大于。
◆ =:等于。
◆ <:小于。
◆ >=:大于或等于。
◆ <=:小于或等于。
◆ <>(!=):不等于。
5.5.3 逻辑运算符
逻辑运算符用来对逻辑条件进行测试,以获得其真实情况。它与比较运算符一样,根据测试结果返回布尔值TRUE、FALSE或NULL。逻辑运算符有AND、OR、NOT、BETWEEN和LIKE等,运算过程与结果如下。
◆ AND:当两个逻辑表达式都为TRUE时,则为TRUE。
◆ OR:当两个逻辑表达式中的一个为TRUE时,则为TRUE。
◆ NOT:对逻辑表达式的值取反。
◆ BETWEEN:若操作数在某个范围之内,则为TRUE。
◆ IN:若操作数等于表达式列表中的一个,则为TRUE。
◆ LIKE:若操作数与一种模式相匹配,则为TRUE。
◆ ALL:若与一系列的比较都为TRUE,则为TRUE。
◆ ANY:若与一系列比较中的任何一个为TRUE,则为TRUE。
◆ SOME:若在一系列的比较中有些为TRUE,则为TRUE。
◆ EXISTS:若子查询中包含一些行,则为TRUE。
AND、OR、NOT、BETWEEN、IN这些逻辑运算符的使用方法在本书前面的小节中已经介绍过了;而ALL、SOME、ANY和EXISTS逻辑运算符,则用于判断一个表达式的值与一个子查询结果集中的所有、部分或任一个值间的关系是否满足指定的比较条件。这在后面有关子查询的章节中会有详细介绍。
5.5.4 字符串连接符
字符串连接符“||”用于实现字符串之间的连接操作。比如下面的操作:
'abc'||'def'
其运算结果为'abcdef',也就是把两边的表达式连接在一起。
在DB2、Oracle和PostgreSQL数据库系统中,采用的连接符为“||”;而在SQL Server和Access数据库系统中,采用的连接符为“+”。
5.5.5 赋值运算符
在PL/SQL中的赋值运算符为“:=”,通常在PL/SQL块中使用,用来进行赋值操作,即将表达式的值赋给一个变量。例如下面一段脚本,演示了如何为变量赋值。
01 DECLARE 02 v_name VARHAR2(12); 03 BEGIN 04 v_name := 'TEST'; 05 … 06 END;
【代码解析】
◆ 第1行为声明。
◆ 第2行中变量名为v_name。
◆ 第4行表示为该变量赋值。
5.5.6 运算符的优先级
Oralce中的运算符具有不同的优先级,同一个表达式中包含有不同的运算符时,运算符的优先级决定了表达式的计算和比较操作顺序。Oralce中各种运算符的优先级顺序如下。
◆ 括号:()。
◆ 正、负运算:+、-。
◆ 乘、除运算:*、/。
◆ 加、减、字符连接运算:+、-、||。
◆ 比较运算:=、>、<、>=、<=、<>、!=。
◆ 逻辑非运算:NOT。
◆ 逻辑与运算:AND。
◆ ALL、ANY、BETWEEN、IN、LIKE、OR、SOME等运算。
◆ 赋值运算::=。
5.6 查询中表达式的使用
在进行数据查询时,经常需要对查询到的数据进行再次计算处理。PL/SQL允许直接在SELECT子句中使用计算列,计算列并不存在于表所存储的数据中,它是通过对某些列的数据进行演算得来的结果。
5.6.1 使用连接符连接列
在SQL Server中可以使用连接符“||”将两列或者多列的记录合成一列在结果表中显示,也可以在每行记录中添加固定的字符显示。
关于连接符的使用,这里通过一个实例来说明。
【实例5-34】使用连接符连接列。
本实例将查询BOOKINFO表中的图书记录,要求将BOOKNAME和PUBLISH列合成一列,并添加相应的标识。相关脚本如下:
01 SELECT BOOKID,('书名:'||BOOKNAME||'出版社:'||PUBLISH)AS 连接后,AUTHOR 02 FROM BOOKINFO 03 ORDER BY BOOKNAME 04 /
【代码解析】
◆ 第1行中使用了连接符“||”,它对“书名:”、“BOOKNAME”、“出版社:”、“PUBLISH”进行了连接,连接后设置别名为“连接后”。
◆ 第3行表示按BOOKNAME列进行排序。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.35。
图5.35 连接符的使用
可见,使用连接符不仅可以连接表中的两列,而且还可以加入一些固定字符。然而,合并后的列没有列名。
使用连接符进行多列连接时,要求被连接的列必须有相同的或能够相互隐式转换的数据类型。不能对具有完全不同的数据类型的列进行连接。
另外,需要说明的是NULL值的处理问题。如果连接的两个字段中有一个字段的值为NULL,那么NULL会被正常连接,只不过NULL值不会有任何数据显示,比如下面的实例。
【实例5-35】连接中NULL值的处理。
本实例将查询BOOKINFO表中的图书记录,要求将BOOKNAME和READER列合成一列,并添加相应的标识。相关脚本如下:
01 SELECT BOOKID,('书名:'||BOOKNAME||'借阅者:'||READER)AS 连接后,AUTHOR 02 FROM BOOKINFO 03 ORDER BY BOOKNAME 04 /
【代码解析】
◆ 第1行使用了连接符,并指定了别名为“连接后”。
◆ 第3行表示按BOOKNAME列进行升序排列。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.36。
图5.36 连接符对NULL值的处理
从图5.36中的标记部分可以看出,NULL值部分正常执行了操作,不需要对它有特殊的处理,只不过它表示的是空,所以在BOOKNAME列后没有显示其他数据。
5.6.2 算术表达式的使用
在SELECT子句中,可以使用算术运算符直接对列的数据进行算术运算,而在查询的结果集中显示的就是运算后的记录。
【实例5-36】在查询中进行算术运算。
要求将BOOKINFO表中的图书打折售出,即按照75折再便宜1元的价格对外出售,现需要查询一下所有图书折后的相关信息。相关脚本如下:
01 SELECT BOOKNAME, PRICE*0.75-1 AS 折后价格,PUBLISH,AUTHOR 02 FROM BOOKINFO 03 ORDER BY 折后价格 04 /
【代码解析】
◆ 第1行使用了列的别名操作,并使用了表达式,PRICE*0.75-1表示对PRICE列中的数据乘以0.75后减去1,该操作最终得到的结果将是查询结果。这就是表达式的作用。
【执行效果】
在SQL*Plus中执行以上脚本,执行效果见图5.37。
图5.37 查询中表达式的使用
如果对列进行计算后,未为其重命名,则系统会把查询列或表达式作为列名。
同样,如果需要查询折后价格超过30元的所有图书记录,在WHERE子句中,切不可使用“WHERE折后价格>30”这样的语句,而必须采用如下的语句:
01 SELECT BOOKNAME, PRICE*0.75-1 AS 折后价格,PUBLISH,AUTHOR 02 FROM BOOKINFO 03 WHERE PRICE*0.75-1 > 30 04 ORDER BY 折后价格 05 /
【代码解析】
◆ 第3行表示在查询条件中使用了表达式,它会把最后结果大于30元的数据列出来。
5.7 小结
本章重点介绍了SELECT语句的基本语法结构,以及其中涉及的SELECT子句、WHERE子句和ORDER BY子句的使用,还有查询中表达式的使用。掌握了这些知识,基本的查询操作就不成问题了。另外,读者要特别注意5.1节介绍的SELECT语句各子句的顺序及执行步骤。