
4.2 使用SELECT语句
用户对表或视图的操作是通过SQL语句来实现的,SQL语句是一种标准的结构化查询语言。在众多的SQL语句中,使用频率最高的是SELECT语句,该语句主要用于检索数据。虽然在前面已经使用了一些SELECT语句,但是这些使用是零散的、不完整的。因此,在这里将对SELECT语句进行系统的、完整的介绍。
4.2.1 检索单表数据
检索单表数据是指从单个表中检索数据,检索的结果都来源于同一个表中,检索单表数据是检索数据最基础的操作。
在检索数据的过程中,既可以检索所有的列,也可以检索部分列。在检索数据时,数据将按照SELECT子句后面指定列的顺序显示。如果使用星号“*”,则表示检索所有的列,这时数据将按照定义表时指定列的顺序显示数据。
在下面的示例,将检索EMP表中的所有列和指定列。
① 以SCOTT/TIGER身份连接数据库。
SQL> sconnect scott/tiger 已连接。
提示:
Oracle 12c中,如果在安装过程中安装了PDB选项,在创建或使用已创建的用户名或角色时,需要在用户名和角色前面添加C##符号。
② 使用SELECT语句检索EMP表中的所有数据。在该检索中,使用星号代表所有的列名称。

③ 使用SELECT语句检索ENAME列名。注意,在该检索中,检索列的顺序与列的定义顺序是不同的。

在Oracle系统中,有一个标识行中唯一数据的行标识符,行标识符的名称为ROWID。行标识符ROWID是Oracle数据库内部使用的数据,其长度为18个数字,包含了该行数据在Oracle数据库中的物理地址。虽然使用DESCRIBE命令无法查看到ROWID的存在,但是可以在SELECT语句中检索该列。由于该列并不是在表中定义的列,所以该列也称为“伪列”。
例如,下面的语句检索EMP表中的ROWID、EMPNO和ENAME列等数据。

在使用SELECT语句检索表中的数据时,还可以执行加、减、乘和除运算。另外,在SELECT语句中不仅可以执行单独的数学运算,还可以执行单独的日期运算,以及执行与列名关联的运算。
在执行运算时,经常使用系统提供的DUAL表,DUAL表的结构和数据如下:

DUAL表只包含了一列一行数据,列名为DUMMY(中文含义为哑巴、样品等),数据类型为VARCHAR2(1)。一行数据为X。该表本身的结构和数据并不重要,但是基于该表可以执行一些基于表的运算。
实际上,为了使结果更好理解,用户经常会为这些列指定别名。另外,在定义表时,为了简单起见,列名经常是一些缩写形式,阅读这样的列名感觉上有些困难。因此,为列名指定一个描述性的别名,可以使阅读检索结果更加方便。
例如,下面的示例分别为EMP表的各个列指定了中文别名。

在为列指定别名时,关键字AS是可选的。例如,下面的语句就省略了关键字AS。
SQL> select ename "姓名",job "职位",hiredate "工作日期",sal "薪金" 2 from emp;
在检索数据时,有时会需要将检索出来的数据合并起来,以满足实际需求。例如,在HR用户模式中,职工的姓名是分别存储在FIRST_NAME和LAST_NAME列中的。如果希望以“姓名”的完整方式显示这些数据,就可以使用连接运算符“||”将两个列连接在一起。通常为提高这种连接运算的可读性,在连接两个列后为其指定一个别名。
例如,下面以HR身份连接数据库,并查询其中的职工信息表。

在执行字符连接运算时,应该根据需要在两个字符表达式之间插入一个分隔符,以防止将这两个字符表达式完全连接在一起,影响检索结果的可读性。
在检索数据时,还有一个点需要注意,表中的空值既不表示空字符串,也不表示数字0,而是表示没有值,是一个未知值。只有在允许为空的列中才会出现空值。在Oracle中,可以使用NVL( )函数为空值提供一个指定的显示值,NVL( )函数的使用方法如下:
nvl(column_name,displayed_message)
其中,如果COLUMN_NAME列为空值,那么就在空值相应的位置上显示DISPLAYED_ MESSAGE指定的字符。例如:

在SELECT语句中,还可以使用关键字DISTINCT,限制在检索结果中显示那些不重复的数据。该关键字用在SELECT子句中列的列表前面。例如,下面的语句使用DISTINCT关键字检索EMP表中所有职工的职务种类。
SQL> select distinct job 2 from emp;
4.2.2 过滤数据
在SELECT语句中可以使用WHERE子句过滤数据,只检索那些满足过滤条件的数据。当表中的数据非常多时,这种过滤操作是非常有意义的。通过过滤数据,可以从大量的数据中获取自己所需要的数据。
1. 比较运算符
在WHERE子句中可以使用比较运算符实现过滤数据,这样只有满足比较条件的数据行才会被检索出来,不满足比较条件的数据行则不会被检索出来。可以在WHERE子句中使用的比较运算符如下表所示。

当使用字符串和日期数据进行比较时,应注意要符合下面的规则。
※ 字符串和日期必须使用单引号标识。
※ 字符串数据是区分大小写的。
※ 日期数据的格式是敏感的,默认的日期格式是DD-MON-YY。
在下面的示例,将练习如何使用比较运算符过滤数据。
① 以SCOTT身份连接数据库。
② 使用SELECT语句检索EMP表,要求工作编号为7521。

③ 使用SELECT语句检索EMP表,要求职位为CLERK、ANALYST中的任何一个,这时可以使用ANY比较运算符。

2. SQL运算符
使用SQL运算符可以基于字符串的模式匹配、值的列表、值的范围和是否空值等情况来过滤数据。在Oracle中,可以使用的SQL运算符如下表所示。

在上表中的SQL运算符也可以与NOT运算符取反处理。例如,NO LIKE、NOT LIKE、NOT BETWEEN和IS NOT NULL等。如果LIKE为真,则NOT LIKE为假。
可以在WHERE子句中使用LIKE运算符指定将要匹配的字符串模式。在这种模式中,下画线“_”代表任意一个字符,百分号“%”代表任意数量字符。例如,‘A%’表示以字母A开头的任意长度的字符串,‘DB_’表示3个字符长且两个字符是DB的字符串。
例如,下面的示例使用LIKE运算符过滤数据,要求显示职工姓名中第一个字符是S的职工信息。

如果在模式中包含了实际的下画线或斜杠,则可以使用ESCAPE关键字来指定该字符是实际数据,而不是匹配标记。
例如,下面的示例添加了一行包含下画线“_”的数据,为了显示新添加的数据,使用EXCAPE关键字指定了一个转义字符“\”,这样转义字符后的下画线就不再表示匹配标记了。

使用IN运算符可以检索在指定列表中的数据,例如,过滤条件EMPNO IN(7369,7521,7789)表示检索工作编号是7369、7521或者7789的职工信息。NOT IN正好与IN相反,例如,EMPNO NOT IN(7369,7521,7789)表示检索工作编号除7369、7521或者7789之外的职工信息。但是,如果在NOT IN条件中包括了NULL值,那么NOT IN总是返回一个空值。

使用BETWEEN运算符可以在指定范围内搜索数据。例如,如果希望检索薪金为1000到2000之间的职工信息,那么可以使用BETWEEN运算符。需要注意的是,BETWEEN运算符仅适用于那些可以指定范围的数字数据。
例如,使用BETWEEN运算符检索指定薪金范围内的职工信息。
SQL> select empno,ename,job,sal 2 from emp 3 where sal between 1500 and 2000;
3. 逻辑运算符
前面介绍的过滤条件都是单一的条件,如果希望写出复杂的过滤条件,那么,必须使用逻辑运算符,以便把简单的条件组合起来。在Oracle系统中,可以使用的逻辑运算符如下表所示。

实际上,BETWEEN运算符的条件可以写成使用AND逻辑运算符连接起来的两个简单条件。例如,条件SAL BETWEEN 1500 AND 2000等价于SAL>=1500 AND SAL<=2000。

在使用逻辑运算符连接多个简单条件时,还需要注意运算符的优先级问题。对于逻辑运算符,优先级由高到低的顺序为NOT、AND和OR。即先进行NOT取反运算,然后再进行AND与运算,最后再进行OR或运算符。
在复合条件中,优先级越高的运算符将优先执行。如果优先级相同,则按照条件的先后顺序执行。如果复合条件中包括了括号,那么括号内的优先级高于括号外的。例如,在下面的示例,将检索职工所属部门编号为20,并且职位为MANAGER或SALESMAN的职工信息。

4.2.3 排序数据
在前面介绍的数据检索技术中,只是把数据库中的数据直接取出来。这时,结果集中数据的排列顺序是由数据的物理存储顺序所决定的。这种存储顺序比较混乱,并且可以不符合用户的各种业务需求,因此需要对检索到的结果集进行排序。在SELECT语句中,可以使用ORDER BY子句对检索的结果集进行排序。
添加ORDER BY子句后SELECT语句的语法规则如下。
select column _list from table_name where condition order by order_expression [ASC︱DESC],order_expression [ASC︱DESC],…
其中,ORDER_EXPRESSION表示将要排序的列名或由列组成的表达式;关键字ASC指定按照升序排列,这也是默认的排列顺序,关键字DESC指定按照降序排列。
在排序过程中,可以同时对多个列进行排序。如果是按照多个列进行排序,那么列之间的顺序非常重要。在这种情况下,系统首先按照第一个列进行排序,如果第一个列相同,则按照第二个列进行排序,以此类推。
在下面的示例,将使用ORDER BY子句对检索到的数据进行排序。
① 以SCOTT身份连接系统。
② 使用SELECT语句检索EMP表中的信息,并且按照职工的薪金SAL列和姓名ENAME列进行升序排序。

③ 在ORDER BY子句通过指定列的位置,指定进行排序的列。

提示:
这里使用的位置是根据SELECT子句后面出现的列表达式的顺序确定的。
4.2.4 多表检索
在实际应用中,经常会碰到需要检索的数据存在于两个或两个以上的表中。这时就需要使用SELECT语句执行多表检索。多表检索操作比单表检索复杂得多。为了更好地理解多表检索操作,需要理解表的别名、笛卡尔积、内连接、外连接、自然连接和交叉连接等概念。
1. 表的别名
在多表查询时,如果多个表之间存在同名的列,则必须使用表名来限定列引用。例如,在SCOTT模式中,EMP和DEPT表中都存在DEPTNO列,在进行多表检索时就是根据该列连接两个表。
然而,随着查询变得越来越复杂,语句会由于每次限定列时输入表名而变得冗长乏味。因此,SQL语言提供了另一种机制—表别名。表的别名是在SELECT语句中为表定义的临时性名称,以简化对表的引用。
下面的示例将使用表别名来实现多表的检索。
① 以SCOTT身份连接系统。
② 使用SELECT语句检索EMP和DEPT表,查询属于某一个部门的职工信息。在该检索中,没有使用表的别名,因此在WHERE子句中需要使用表的全名对列进行限定。

③ 使用SELECT语句查询EMP和DEPT表,同样查询属于某一个部门的职工信息。只是在该SELECT语句中为每个表指定了别名,并通过不同的列别名引用表。EMP表的别名为E,而DEPT表的别名为D。为表定义别名后,在SELECT语句的任何地方都可以使用E和D引用相应的表。

为更好地理解表别名的工作过程,在这里有必要介绍一下SELECT语句中各子句执行的顺序。在SELECT语句的执行顺序中,FROM子句是最先执行的,而SELECT子语是最后执行的。这样一旦在FROM子句中指定了表别名,当限定引用列时,其他所有子句都可以使用表的别名。需要注意的是,一旦为表指定了别名,则必须在整个剩余语句中使用表的别名,并且不再允许使用表原来的名称,否则,将出现ORA-00904错误。
提示:
在多表检索中,由于需要频繁地使用表名称限定指定的列名称,因此表的别名应该尽可能简单。大多数表的别名是由一个或两个字母组成的,但是,表的别名也应该具有描述性,为求与其他表的别名明显区分。
2. 内连接
内连接是指满足连接条件的连接操作,也是通常所说的连接操作。也就是说,在内连接的检索结果中,都是满足连接条件的数据。因此,内连接的检索结果就是笛卡尔积中满足连接条件的子集。
内连接的语法形式如下:
select column_list from table_name1 [inner] join table_name2 on join_condition;
其中,COLUMN_LIST表示将要检索的列名列表,通常情况下,这些列名来自两个不同的表中。TABLE_NAME1和TABLE_NAME2表示将要连接的表名称。INNER JOIN关键字表示内连接,其中INNER关键字是可选的。ON JOIN_CONDITION用于指定连接的条件。
例如,下面的示例将通过内连接检索EMP和DEPT表。
① 以SCOTT身份连接系统。
② 使用SELECT语句检索EMP和DEPT表。这两个表之间的连接是内连接,其连接条件是两个表中的DEPTNO列。

③ 使用SELECT语句检索EMP和DEPT表。但是,不使用INNER JOIN表示内连接,也不通过ON指定连接条件,而是通过WHERE子句指定连接条件。
SQL> select e.ename,e.job,e.sal,d.deptno,d.dname 2 from emp e,dept d 3 where e.deptno=d.deptno;
3. 外连接
在外连接中,如果某个表中的数据不满足条件,而又希望出现在检索结果中,那么,可以使用外连接。外连接的特点是某些不满足连接条件的数据也可以出现在连接结果中。
根据外连接检索结果中包含的数据,外连接可以分为左外连接、右外连接和全外连接。左外连接表示在结果中不仅包含了满足条件的数据,而且还包含了连接左边的左表;在右边接中,结果包含了不满足条件的右表中的数据;如果左表和右表中不满足连接条件的数据都出现在结果中,那么这种连接是全外连接。
提示:
在连接语句中,JOIN关键字左边的表称为“左表”,而右边的表称为“右表”。
外连接的语法和内连接的语法规则相似,区别在于外连接中用LEFT OUTER JOIN、RIGHT OUTER JOIN或FULL OUTER JOIN关键字,而不使用INNER JOIN关键字。其中OUTER是可选的。例如,左边接可以使用LEFT JOIN代替LEFT OUTER JOIN。
理解不同类型外连接之间的区别的最好方法是看各自的查询结果。在下面的示例,将分别演示内边接以及各种外连接之间的区别。
① 以SCOTT身份连接系统。
② 使用SELECT语句检索EMP和DEPT表,在这两个表之间执行左外连接。由于EMP表位于LEFT JOIN关键字的左边,所以EMP表中的所有数据都将显示出来。

从查询结果中可以看出,左边接的查询结果中不仅包括内连接的检索结果,还包括了新添加数据,尽管该行在DEPT表中没有匹配的行。由于新添加行在DEPT表中没有匹配的行,所以EMP表中的DEPTNO和DNAME列以NULL值表示。
③ 使用SELECT语句检索EMP和DEPT表,在这两个表之间执行右外连接。由于DEPT表位于RIGHT JOIN关键字的右边,所以DEPT表中所有的数据都将显示出来。

④ 使用SELECT语句检索EMP和DEPT表,在这两个表之间执行全外连接。

提示:
在外连接中,用户需要特别注意两个表的位置。
4. 自然连接
与内连接的功能相似,在使用自然连接检索多个表时,Oracle会将第一个表中的那些列与第二个表中具有相同名称的列进行连接。在自然连接中,用户不需要明确指定进行连接的列,系统会自动完成这一任务。
下面的SELECT语句将使用自然连接连接EMP和DEPT表。

自然连接的实际应用性较差,因为它需要连接的各个表之间必须具有相同名称的列。这将会强制设计者将要连接的表设计为具有相同名称的列,并且不能够让表中的其他列具有相同的名称。假如EMP和DEPT表中都有一个表示地址的ADDRESS列,则在进行自然连接时,Oracle会尝试使用DEPTNO和ADDRESS列进行连接。
5. 交叉连接
交叉连接实际上就是指没有连接条件的连接。实际上,这种连接的结果就是笛卡尔积的结果。
交叉连接的语法形式如下。
select column_list from table_name1 cross join table_name2;
在上面的语法中,CROSS JOIN关键字表示执行交叉连接。使用交叉连接检索表时,虽然会得到一个无实际用处的笛卡尔积。但是,可以通过在其中使用WHERE子句,从笛卡尔积中过滤需要的数据。
在下面的示例,将使用交叉连接检索EMP和DEPT表,并使用WHERE子句从中过滤所需要的数据。
① 以SCOTT身份连接系统。
② 使用SELECT语句检索EMP和DEPT表。由于这两个表之间的检索是交叉连接的,并且没有限定条件,所以查询结果是一个笛卡尔积。

③ 使用WHERE子句在笛卡尔积中过滤不必要的数据。

从检索结果中可以看出,WHERE子句的作用是从笛卡尔积中筛选适合条件的数据。实际上,各种连接方式也是从笛卡尔积中筛选数据。