4.3 检索数据
视频讲解:光盘\TM\lx\4\检索数据.mp4
用户对表或视图最常进行的操作就是检索数据,检索数据可以通过SELECT语句来实现,该语句由多个子句组成,通过这些子句可以完成筛选、投影和连接等各种数据操作,最终得到用户想要的查询结果。该语句的基本语法格式如下:
select {[ distinct | all ] columns | *} [into table_name] from {tables | views | other select} [where conditions] [group by columns] [having conditions] [order by columns]
在上面的语法中,共有7个子句,它们的功能分别如下。
select子句:用于选择数据表、视图中的列。
into子句:用于将原表的结构和数据插入新表中。
from子句:用于指定数据来源,包括表、视图和其他select语句。
where子句:用于对检索的数据进行筛选。
group by子句:用于对检索结果进行分组显示。
having子句:用于从使用group by子句分组后的查询结果中筛选数据行。
order by子句:用来对结果集进行排序(包括升序和降序)。
接下来将对上面的各种子句和查询方式进行详细介绍。
4.3.1 简单查询
只包含SELECT子句和FROM子句的查询就是简单查询,SELECT子句和FROM子句是SELECT语句的必选项,即每个SELECT语句都必须包含这两个子句。其中,SELECT子句用于选择想要在查询结果中显示的列,对于这些要显示的列,即可以使用列名来表示,也可以使用星号(*)来表示。在检索数据时,数据将按照SELECT子句后面指定的列名的顺序来显示;如果使用星号(*),则表示检索所有的列,这时数据将按照表结构的自然顺序来显示。
1.检索所有的列
如果要检索指定数据表的所有列,可以在SELECT子句后面使用星号(*)来实现。
在检索一个数据表时,要注意该表所属的模式。如果在指定表所属的模式内部检索数据,则可以直接使用表名;如果不在指定表所属的模式内部检索数据,则不但要查看当前模式是否具有查询的权限,而且还要在表名前面加上其所属的模式名称。
【例4.7】 在SCOTT模式下,在SELECT语句中使用星号(*)来检索dept表中所有的数据,代码如下。
SQL> connect scott/1qaz2wsx 已连接。 SQL> select * from dept;
本例运行结果如图4.3所示。
图4.3 检索dept表中所有的数据
说明
上面的SELECT语句若要在SYSTEM模式下执行,则需要在表dept前面加上scott,即scott.dept。
在上面的例子中,from子句的后面只有一个数据表,实际上可以在from子句的后面指定多个数据表,每个数据表名之间使用逗号(,)分隔开,其语法格式如下:
FROM table_name1, table_name2, table_name3…table_namen
【例4.8】 在SCOTT模式下,在from子句中指定两个数据表dept和salgrade,代码如下。
SQL> select * from dept, salgrade;
2.检索指定的列
用户可以指定查询表中的某些列而不是全部列,并且被指定列的顺序不受限制,指定部分列也称作投影操作。这些列名紧跟在SELECT关键字的后面,每个列名之间用逗号隔开。其语法格式如下:
SELECT column_name1, column_name2, column_name3, column_namen
说明
利用SELECT指定列的好处就是可以改变列在查询结果中的默认显示顺序。
【例4.9】在SCOTT模式下,检索emp表中指定的列job、ename、empno,代码如下。
SQL> select job, ename, empno from emp;
本例运行结果如图4.4所示。
图4.4 检索emp表中指定的列
注意
上面查询结果中列的显示顺序与emp表结构的自然顺序不同。
在Oracle数据库中,有一个标识行中唯一特性的行标识符,该行标识符的名称为ROWID。行标识符ROWID是Oracle数据库内部使用的隐藏列,由于该列实际上并不是定义在表中,所以也称为伪列。伪列ROWID长度为18位字符,包含该行数据在Oracle数据库中的物理地址。用户使用DESCRIBE命令是无法查到ROWID列的,但是可以在SELECT语句中检索到该列。
【例4.10】 在SCOTT模式下,检索emp表中指定的列job和ename。另外,还包括ROWID伪列,代码如下。
SQL> select rowid, job, ename from emp;
本例运行结果如图4.5所示。
图4.5 显示emp表的ROWID伪列
3.查询日期列
日期列是指数据类型为DATE的列。查询日期列与查询其他列没有任何区别,但日期列的默认显示格式为DD-MON-RR。
下面通过实例来说明查询日期列和以特定语言或者格式显示日期列数据的方法。
(1)以简体中文显示日期结果
【例4.11】 在SCOTT模式下,检索emp表中员工的入职时间(hiredate),并且以简体中文显示日期结果。如果以简体中文显示日期结果,可以将会话的NLS_DATE_LANGUAGE参数设置为SIMPLIFIED CHINESE。代码如下。
SQL> alter session set nls_date_language = 'SIMPLIFIED CHINESE'; SQL> select ename, hiredate from emp;
本例运行结果如图4.6所示。
图4.6 以简体中文显示日期结果
(2)以美国英语显示日期结果
【例4.12】 在SCOTT模式下,检索emp表中员工的入职时间(hiredate),并且以美国英语显示日期结果。如果以美国英语显示日期结果,可以将会话的NLS_DATE_LANGUAGE参数设置为AMERICAN。代码如下。
SQL> alter session set nls_date_language = 'AMERICAN'; SQL> select ename, hiredate from emp;
本例运行结果如图4.7所示。
图4.7 以美国英语显示日期结果
(3)以特定格式显示日期结果
不同国家地区、不同民族、不同人员都具有不同的日期使用习惯,如果希望定制日期显示格式,并按照特定方式显示日期格式,那么可以设置会话的NLS_DATE_FORMAT参数。
【例4.13】 在SCOTT模式下,检索emp表中员工的入职时间(hiredate),并且以“××××年××月××日”格式显示日期结果。代码如下。
SQL> alter session set nls_date_format = 'YYYY"年"MM"月"DD"日"'; SQL> select ename, hiredate from emp;
本例运行结果如图4.8所示。
图4.8 以特定格式显示日期结果
(4)使用TO_CHAR函数定制日期显示函数
除了可以使用参数NLS_DATE_FORMAT设置日期显示格式外,也可以使用TO_CHAR函数将日期值转变为特定格式的字符串。
TO_CHAR函数将在本章4.4.4节中进行详细讲解。
4.带有表达式的SELECT子句
在使用SELECT语句时,对于数字数据和日期数据都可以使用算术表达式。在SELECT语句中可以使用算术运算符,包括(+)、减(-)、乘(*)、除(/)和括号。另外,在SELECT语句中不仅可以执行单独的数学运算,还可以执行单独的日期运算以及与列名关联的运算。
【例4.14】 检索emp表的sal列,把其值调整为原来的1.1倍,代码如下。
SQL> select sal*(1+0.1), sal from emp;
本例运行结果如图4.9所示。
图4.9 显示工资调整后的值
注意
在上面的查询结果中,左侧显示的是sal列调整为原来1.1倍后的值,右侧显示的是sal列的原值。
5.为列指定别名
由于许多数据表的列名都是一些英文的缩写,用户为了方便查看检索结果,常常需要为这些列指定别名。在Oracle系统中,为列指定别名既可以使用AS关键字,也可以不使用任何关键字而直接指定。
【例4.15】 在SCOTT模式下,检索emp表的指定列empno、ename、job,并使用AS关键字为这些列指定中文的别名,代码如下(实例位置:光盘\TM\sl\4\5)。
SQL> connect scott/1qaz2wsx 已连接。 SQL>select empno as"员工编号", ename as"员工名称", job as"职务" from emp;
本例运行结果如图4.10所示。
图4.10 指定中文的别名1
在为列指定别名时,关键字AS是可选项,用户也可以在列名后面直接指定列的别名。
【例4.16】 在SCOTT模式下,检索emp表的指定列empno、ename、job,不使用任何关键字而直接为这些列指定中文的别名,代码如下。
SQL>select empno"员工编号", ename"员工名称", job"职务" from emp;
本例运行结果如图4.11所示。
图4.11 指定中文的别名2
6.显示不重复记录
默认情况下,结果集中包含所有符合查询条件的数据行,这样结果集中就有可能出现重复数据。而在实际的应用中,这些重复的数据除了占据较大的显示空间外,可能不会给用户带来太多有价值的东西,这样就需要去除重复记录,保留唯一的记录即可。在SELECT语句中,可以使用DISTINCT关键字来限制在查询结果显示不重复的数据,该关键字用在SELECT子句的列表前面。
【例4.17】 在SCOTT模式下:
(1)显示emp表中的job(职务)列,代码如下。
SQL> select job from emp;
本例运行结果如图4.12所示。
图4.12 显示不重复记录1
(2)显示emp表中的job(职务)列,要求显示的“职务”记录不重复,代码如下。
SQL> select distinct job from emp;
本例运行结果如图4.13所示。
图4.13 显示不重复记录2
7.处理NULL值
NULL表示未知值,它既不是空格,也不是0。当插入数据时,如果没有为特定列提供数据,并且该列没有默认值,那么其结果为NULL。
但是在实际应用程序中,NULL显示结果往往不能符合应用需求,在这种情况下需要使用函数NVL处理NULL,并将其转换为合理的显示结果。下面通过实例来说明不处理NULL的显示结果,以及处理NULL的具体方法。
(1)不处理NULL。当算术表达式包含NULL时,如果不处理NULL,那么显示结果为空。
【例4.18】 显示emp表中的雇员名、工资、奖金以及实发工资(SAL+COMM),代码如下。
SQL> select ename, sal, comm, sal+comm from emp;
本例运行结果如图4.14所示。
图4.14 不处理NULL值的显示结果
通过显示结果可以看出,COMM值为NULL的列,计算SAL+COMM的值也为NULL。
(2)使用NVL函数处理NULL。如果雇员的实发工资显示为空,那么显然是不符合实际情况的。为了避免出现这种情况,就应该处理NULL。
【例4.19】 显示emp表中的雇员名、工资、奖金以及实发工资(SAL+COMM)并处理NULL值,代码如下。
SQL> select ename, sal, comm, sal+nvl(comm,0) from emp;
本例运行结果如图4.15所示。
图4.15 使用NVL函数处理NULL值的显示结果
当使用函数NVL(COMM,0)时,如果COMM存在数值,则函数返回其原有数值;如果COMM列为NULL,则函数返回0。
8.连接字符串
当执行查询操作时,为了显示更有意义的结果值,有时需要将多个字符串连接起来。连接字符串可以使用“||”操作符或者CONCAT函数。
注意
当连接字符串时,如果在字符串中加入数字值,那么可以直接指定数字值;而如果在字符串中加入字符值或者日期值,那么必须用单引号引住。
(1)使用“||”操作符连接字符串
【例4.20】 显示emp表中所有雇员的雇员名及其岗位信息,使用“||”操作符连接雇员名和岗位信息,代码如下。
SQL> select ename, || ''''||'s job is '||job from emp;
本例运行结果如图4.16所示。
图4.16 使用“||”操作符连接字符串
(2)使用函数CONCAT连接字符串
【例4.21】 显示emp表中所有雇员的雇员名及其工资信息,使用CONCAT函数连接雇员名和工资信息,代码如下。
SQL> select concat(concat(ename, '''s salary is '), sal) from emp;
本例运行结果如图4.17所示。
图4.17 使用函数CONCAT连接字符串
具体CONCAT函数用法将在本章中4.4.1节做详细介绍。
4.3.2 筛选查询
在SELECT语句中使用WHERE子句可以实现对数据行的筛选操作,只有满足WHERE子句中判断条件的行才会显示在结果集中,而那些不满足WHERE子句判断条件的行则不包括在结果集中。这种筛选操作是非常有意义的,通过筛选数据,可以从大量的数据中得到用户所需要的数据。在SELECT语句中,WHERE子句位于FROM子句之后,其语法格式如下:
SELECT columns_list FROM table_name WHERE conditional_expression
columns_list:字段列表。
table_name:表名。
conditional_expression:筛选条件表达式。
接下来对几种常用的筛选情况进行详细讲解。
1.比较筛选
可以在WHERE子句中使用比较运算符来筛选数据,这样只有满足筛选条件的数据行才会被检索出来,不满足比较条件的数据行则不会被检索出来。基本的“比较筛选”操作主要有以下6种情况。
A=B:比较A与B是否相等。
A! B或A< >B:比较A与B是否不相等。
A>B:比较A是否大于B。
A<B:比较A是否小于B。
A>=B:比较A是否大于或等于B。
A<=B:比较A是否小于或等于B。
【例4.22】 在SCOTT模式下,查询emp表中工资(sal)大于1500的数据记录,代码如下。
SQL> select empno, ename, sal from emp where sal > 1500;
本例运行结果如图4.18所示。
图4.18 查询工资大于1500的记录
另外,除了基本的“比较筛选”操作外,还有以下两个特殊的“比较筛选”操作。
A{operator}ANY(B):表示A与B中的任何一个元素进行operator运算符的比较,只要有一个比较值为true,就返回数据行。
A={operator}ALL(B):表示A与B中的所有元素进行operator运算符的比较,只有与所有元素比较值都为true,才返回数据行。
【例4.23】 在SCOTT模式下,使用all关键字过滤工资(sal)同时不等于3000、950和800的员工记录,代码如下。
SQL> select empno, ename, sal from emp where sal <> all(3000,950,800);
本例运行结果如图4.19所示。
图4.19 过滤指定记录
说明
在进行比较筛选的过程中,字符串和日期的值必须使用单引号标识,否则Oracle会提示“标识符无效”。
2.使用特殊关键字筛选
SQL语言提供了LIKE、IN、BETWEEN和IS NULL等关键字来筛选数据,这些关键字的功能分别是匹配字符串、查询目标值、限定值的范围和判断值是否为空等,下面将对这些关键字进行详细讲解。
(1)LIKE关键字
在WHERE子句中使用LIKE关键字查询数据的方式也称为字符串模式匹配或字符串模糊查询,LIKE关键字需要使用通配符在字符串内查找指定的模式,所以需要了解常用的通配符。
通配符的英文原文为wildcard,该词的原意为扑克牌中的2或王,因为它们可以代替任何其他的牌,所以称为wildcard。
LIKE关键字可以使用以下两个通配符。
%:代表0个或多个字符。
_:代表一个且只能是一个字符。
例如,“K%”表示以字母K开头的任意长度的字符串,“%M%”表示包含字母M的任意长度的字符串,“_MRKJ”表示5个字符长度且后面4个字符是MRKJ的字符串。
【例4.24】 在emp表中,使用LIKE关键字匹配以字母S开头的任意长度的员工名称,代码如下。
SQL> select empno, ename, job from emp where ename like 'S%';
本例运行结果如图4.20所示。
图4.20 使用LIKE关键字
说明
可以在LIKE关键字前面加上NOT,表示否定的判断,如果LIKE为真,则NOT LIKE为假。另外,也可以在IN、BETWEEN、IS NULL和IS NAN等关键字前面加上NOT来表示否定的判断。
【例4.25】 在emp表中,查询工作是SALESMAN的员工姓名,但是不记得SALESMAN的准确拼写,但还记得它的第1个字符是S,第3个字符是L,第5个字符为S,代码如下。
SQL> select empno, ename, job from emp where job like 'S_L_S%';
本例运行结果如图4.21所示。
图4.21 查找工作是SALESMAN的员工姓名
从例4.24和例4.25的查询语句中可以看出,通过在LIKE表达式中使用不同的通配符“%”和“_”的组合,可以构造出相当复杂的限制条件。
另外,LIKE关键字还可以帮助简化某些WHERE子句。
【例4.26】 在emp表中,要显示在1981年雇用的所有员工的信息,代码如下。
SQL> select empno, ename, sal, hiredate 2 from emp 3 where hiredate like'%81';
本例运行结果如图4.22所示。
图4.22 显示在1981年雇用的所有员工的信息
但是如果要查询的字符串中含有“%”或“_”,又该如何处理呢?
要查询的字符串中含有“%”或“_”时,可以使用转义(escape)关键字实现查询。
为了进行练习,必须先创建一个临时表,之后再往该表中插入1行记录,该记录中包含通配符。
【例4.27】 ① 创建一个和dept表相同结构和数据的表dept_temp,代码如下。
SQL> create table dept_temp 2 as 3 select*from dept;
本例运行结果如图4.23所示。
图4.23 创建一个临时表dept_temp
② 插入一条记录,代码如下。
SQL> insert into dept_temp 2 values(60, 'IT_RESEARCH', 'BEIJING');
本例运行结果如图4.24所示。
图4.24 向临时表dept_temp中插入一条含有通配符的数据
说明
例4.27①中的语句将在以后的章节学习,②中的语句将在本章4.6.1节中学习。现在只需按照本例的语句来输入就可以。
③ 显示临时表dept_temp中部门名称以IT_开头的所有数据行,代码如下。
SQL> select * 2 from dept_temp 3 where dname like'IT\_%'escape'\';
本例运行结果如图4.25所示。
图4.25 显示临时表dept_temp中部门名称以IT_开头的所有数据行
在上述查询语句中使用了“\”,“\”为转义字符,即在“\”之后的“_”字符已不是通配符,而是它本来的含义,即下划线。因此该查询的结果为:前两个字符为“IT”,第3个字符为“_”,后跟任意字符的字符串。
没有必要一定使用“\”字符作为转义符,完全可以使用任何字符来作为转义符。当然,许多Oracle的专业人员之所以经常使用“\”字符作为转义符,是因为该字符在UNIX操作系统和C语言中就是转义符。
为了验证以上的论述,输入如下的语句。
SQL> select * 2 from dept_temp 3 where dname like'ITa_%'escape'a';
本例运行结果如图4.26所示。
图4.26 验证转义符可以是任何字符
在上面的查询中,将’a’定义为转义符,但是显示结果和图4.25中显示的结果完全相同。
建议
最好不要将在SQL和SQL *Plus中有特殊含义的字符定义为转义符,否则该SQL语句将变得很难理解。
(2)IN关键字
当测试一个数据值是否匹配一组目标值中的一个时,通常使用IN关键字来指定列表搜索条件。IN关键字的格式是IN(目标值1,目标值2,目标值3, …),目标值的项目之间必须使用逗号分隔,并且括在括号中。
【例4.28】 在emp表中,使用IN关键字查询职务为PRESIDENT、MANAGER和ANALYST中任意一种的员工信息,代码如下。
SQL> select empno, ename, job from emp where job in('PRESIDENT', 'MANAGER', 'ANALYST');
本例运行结果如图4.27所示。
图4.27 使用IN关键字
另外,NOT IN表示查询指定的值不在某一组目标值中,这种方式在实际应用中也很常见。
【例4.29】 在emp表中,使用NOT IN关键字查询职务不在指定目标列表(PRESIDENT、MANAGER、ANALYST)范围内的员工信息,代码如下。
SQL> select empno, ename, job from emp where job not in('PRESIDENT', 'MANAGER', 'ANALYST');
本例运行结果如图4.28所示。
图4.28 使用NOT IN关键字
(3)BETWEEN关键字
需要返回某一个数据值是否位于两个给定的值之间,可以使用范围条件进行检索。通常使用BETWEEN…AND和NOT…BETWEEN…AND来指定范围条件。
使用BETWEEN…AND查询条件时,指定的第一个值必须小于第二个值。因为BETWEEN…AND实质是查询条件“大于等于第一个值,并且小于等于第二个值”的简写形式,即BETWEEN…AND要包括两端的值,等价于比较运算符(>=…<=)。
【例4.30】 在emp表中,使用BETWEEN…AND关键字查询工资(sal)在2000到3000之间的员工信息,代码如下。
SQL> select empno, ename, sal from emp where sal between 2000 and 3000;
本例运行结果如图4.29所示。
图4.29 使用BETWEEN…AND关键字
而NOT…BETWEEN…AND语句返回在两个指定值的范围以外的某个数据值,但并不包括两个指定的值。
【例4.31】 在emp表中,使用NOT…BETWEEN…AND关键字查询工资(sal)不在1000到3000之间的员工信息,代码如下。
SQL> select empno, ename, sal from emp where sal not between 1000 and 3000;
本例运行结果如图4.30所示。
图4.30 使用NOT…BETWEEN…AND关键字
(4)IS NULL关键字
空值(NULL)从技术上来说就是未知的、不确定的值,但空值与空字符串不同,因为空值是不存在的值,而空字符串是长度为0的字符串。
因为空值代表的是未知的值,所以并不是所有的空值都相等。例如,student表中有两个学生的年龄未知,但无法证明这两个学生的年龄相等。这样就不能用“=”运算符来检测空值。所以SQL引入了一个IS NULL关键字来检测特殊值之间的等价性,并且IS NULL关键字通常在WHERE子句中使用。
【例4.32】 在HR模式下,使用IS NULL关键字过滤出locations表中省份或州(state_province)的名称为空值的街道地址信息,代码及运行结果如下(实例位置:光盘\TM\sl\4\6)。
SQL> connect hr/hr; 已连接。 SQL> select street_address from locations where state_province is null; STREET_ADDRESS --------------------------------------- 1297 Via Cola di Rie 93091 Calle della Testa 9450 Kamiya-cho 40-5-12 Laogianggen 198 Clementi North 8204 Arthur St 已选择6行。
说明
HR模式属于Oracle 11g数据库中的一个示例模式,用来管理一个人力资源管理系统的数据。
注意
当与NULL进行比较时,不要使用等于(=)、不等于(< >)操作符。尽管使用它们不会有任何语法错误,但条件总是FALSE。下面以显示公司总裁为例,说明NULL语句前面使用IS和等于或不等于操作符的区别。
使用IS NULL关键字时,代码如下。
SQL>select ename, mgr from emp where mgr is null;
本例运行结果如图4.31所示。
图4.31 使用IS NULL关键字
在NULL前面使用等号(=),代码如下。
SQL>select ename, mgr from emp where mgr=null;
本例运行结果如图4.32所示。
图4.32 在NULL前面使用等号(=)
3.逻辑筛选
逻辑筛选是指在WHERE子句中使用逻辑运算符AND、OR和NOT进行数据筛选操作,那么这些逻辑运算符可以把多个筛选条件组合起来,这样便于用户获取更加准确的数据记录。
AND逻辑运算符表示两个逻辑表达式之间“逻辑与”的关系,用户完全可以使用AND运算符加比较运算符来代替BETWEEN…AND关键字。
【例4.33】 在emp表中,使用AND运算符查询工资(sal)在2000到3000之间的员工信息,代码如下。
SQL> select empno, ename, sal from emp where sal >= 2000 and sal <= 3000;
本例运行结果如图4.33所示。
图4.33 使用AND运算符
OR逻辑运算符表示两个逻辑表达式之间“逻辑或”的关系,两个表达式的结果中有一个为true,则这个逻辑或表达式的值就为true。
【例4.34】 在emp表中,使用OR逻辑运算符查询工资小于2000或工资大于3000的员工信息,代码如下(实例位置:光盘\TM\sl\4\7)。
SQL> connect scott/1qaz2wsx 已连接。 SQL> select empno, ename, sal from emp where sal < 2000 or sal > 3000;
本例运行结果如图4.34所示。
图4.34 使用OR运算符
NOT逻辑运算符对表达式执行逻辑非的运算,在前面的示例中已经多次用到过,这里不再给出示例。
4.3.3 分组查询
数据分组的目的是用来汇总数据或为整个分组显示单行的汇总信息,通常在查询结果集中使用GROUP BY子句对记录进行分组。在SELECT语句中,GROUP BY子句位于FROM子句之后,其语法格式如下:
SELECT columns_list FROM table_name [WHERE conditional_expression] GROUP BY columns_list
columns_list:字段列表,在GROUP BY子句中也可以指定多个列分组。
table_name:表名。
conditional_expression:筛选条件表达式。
GROUP BY子句可以基于指定某一列的值将数据集合划分为多个分组,同一组内所有记录在分组属性上具有相同值,也可以基于指定多列的值将数据集合划分为多个分组。
1.使用GROUP BY子句进行单列分组
单列分组是指基于列生成分组统计结果。当进行单列分组时,会基于分组列的每个不同值生成一个统计结果。
【例4.35】 在emp表中,按照部门编号(deptno)和职务(job)列进行分组,具体代码如下。
SQL> select deptno, job from emp group by deptno, job order by deptno;
本例运行结果如图4.35所示。
图4.35 分组显示
GROUP BY子句经常与聚集函数一起使用。使用GROUP BY子句和聚集函数,可以实现对查询结果中每一组数据进行分类统计。所以,在结果中每组数据都有一个与之对应的统计值。在Oracle系统中,经常使用的统计函数如表4.2所示。
表4.2 常用的统计函数
【例4.36】 在emp表中,使用GROUP BY子句对工资记录进行分组,并计算平均工资(AVG)、所有工资的总和(SUM)以及最高工资(MAX)和各组的行数(COUNT),具体代码如下。
SQL> select job, avg(sal), sum(sal), max(sal), count(job) 2 from emp 3 group by job;
本例运行结果如图4.36所示。
图4.36 分组统计
在使用GROUP BY子句时,要注意:
在SELECT子句的后面只可以有两类表达式:统计函数和进行分组的列名。
在SELECT子句中的列名必须是进行分组的列,除此之外添加其他的列名都是错误的,但是GROUP BY子句后面的列名可以不出现在SELECT子句中。
在默认情况下,将按照GROUP BY子句指定的分组列升序排列,如果需要重新排序,可以使用ORDER BY子句指定新的排列顺序。
GROUP BY子句中的列可以不在SELECT列表中。这里以下面的示例进行说明。
【例4.37】 查询emp表,显示按职位(job)分类(job并没有包含在SELECT子句中)的每类员工的平均工资,并且显示的结果是按职位由小到大排列的,具体代码如下。
SQL> select avg(sal) 2 from emp 3 group by job;
本例运行结果如图4.37所示。
图4.37 演示GROUP BY子句中的列可以不在SELECT列表中
从上面的运行结果中很难看出这一结果是按什么排序的。为了提高程序的可读性,应尽可能不使用这样的查询方法。
下面的实例是错误的,Oracle会返回错误信息,代码如下。
SQL> select job, avg(sal) 2 from emp;
运行结果如图4.38所示。
图4.38 分组函数与GROUP BY子句的非法操作
注意
如果在一个查询中使用了分组函数,则任何不在分组函数中的列或表达式必须在GROUP BY子句中。
为什么在Oracle中会有这样的规定呢?在SELECT子句中的列名称job告诉Oracle系统显示每行数据的职位(job),在emp表中有多条数据。而在SELECT子句中的AVG(sal)告诉Oracle系统显示emp表中所有数据行的平均工资,在这个查询语句中只能产生一个平均工资。查询语句的这两个要求显然是矛盾的,因此,Oracle系统会报错。
为了改正这一错误,可以在查询语句中增加GROUP BY子句,并把列job放在该子句中,修改后的查询语句代码如下。
SQL> select job, avg(sal) 2 from emp 3 group by job;
运行结果如图4.39所示。
图4.39 增加GROUP BY子句使语句正确
显示的结果给出了emp表中每种职位(job)的平均工资(AVG(sal))。
2.使用GROUP BY子句进行多列分组
多列分组是指基于两个或两个以上的列生成分组统计结果。当进行多列分组时,会基于多个列的不同值生成统计结果。
【例4.38】 使用GROUP BY进行多列分组。查询emp表,显示每个部门每种岗位的平均工资和最高工资,具体代码如下。
SQL> select deptno, job, avg(sal), max(sal) 2 from emp 3 group by deptno, job;
本例运行结果如图4.40所示。
图4.40 使用GROUP BY子句进行多列分组
3.使用ORDER BY子句改变分组排序结果
当使用GROUP BY子句执行分组统计时,会自动基于分组列进行升序排列。为了改变分组数据的排序结果,需要使用ORDER BY子句。
【例4.39】 查询emp表,显示每个部门的部门号及工资总额,而且工资总额降序排列,具体代码如下。
SQL> select deptno, sum(sal) 2 from emp 3 group by deptno 4 order by sum(sal)desc;
本例运行结果如图4.41所示。
图4.41 使用ORDER BY子句改变分组排序结果
4.使用HAVING子句限制分组结果
HAVING子句通常与GROUP BY子句一起使用,在完成对分组结果统计后,可以使用HAVING子句对分组的结果做进一步的筛选。如果不使用GROUP BY子句,HAVING子句的功能与WHERE子句一样。HAVING子句和WHERE子句的相似之处都是定义搜索条件。唯一不同的是HAVING子句中可以包含聚合函数,如常用的(count)、(avg)、(sum)等;在WHERE子句中则不可以使用聚合函数。
如果在SELECT语句中使用了GROUP BY子句,那么HAVING子句将应用于GROUP BY子句创建的那些组。如果指定了WHERE子句,而没有指定GROUP BY子句,那么HAVING子句将应用于WHERE子句的输出,并且整个输出被看作一个组,如果在SELECT语句中既没有指定WHERE子句,也没有指定GROUP BY子句,那么HAVING子句将应用于FROM子句的输出,并且将其看作一个组。
提示
对HAVING子句作用的理解有一个办法,就是记住SELECT语句中的子句处理顺序。在SELECT语句中,首先由FROM子句找到数据表,WHERE子句则接收FROM子句输出的数据,而HAVING子句则接收来自GROUP BY、WHERE或FROM子句的输出。
【例4.40】 在emp表中,首先通过分组的方式计算出每个部门的平均工资,然后再通过HAVING子句过滤出平均工资大于2000的记录信息,具体代码如下。
SQL>select deptno as部门编号,avg(sal)as平均工资 2 from emp 3 group by deptno 4 having avg(sal)>2000;
本例运行结果如图4.42所示。
图4.42 平均工资大于2000的记录信息
从上面的查询结果中可以看出,SELECT语句使用GROUP BY子句对emp表进行分组统计,然后再由HAVING子句根据统计值作进一步筛选。
上面的示例无法使用WHERE子句直接过滤出平均工资大于2000的部门信息,因为在WHERE子句中不可以使用聚合函数(这里是AVG)。
通常情况下,HAVING子句与GROUP BY子句一起使用,这样可以汇总相关数据后再进一步筛选汇总的数据。
5.在GROUP BY子句中使用ROLLUP和CUBE操作符
默认情况下,当使用GROUP BY子句生成数据统计结果时,只会生成相关列的数据统计信息,而不会生成小计和总计统计。例如,当使用GROUP BY子句统计不同部门、不同岗位的平均工资时,会生成如表4.3所示的统计结果。
表4.3 使用GROUP BY子句统计不同部门、不同岗位的平均工资
在实际应用程序中,不仅需要获得以上统计结果,而且可能还需要取得横向、纵向小计统计以及总计统计,例如部门的平均工资、岗位的平均工资、所有雇员的平均工资等。为了取得更全面的数据统计,可以使用ROLLUP和CUBE操作符。当使用ROLLUP操作符时,在保留原有统计结果的基础上,还会生成横向小计(部门平均工资)和总计(所有雇员平均工资),如表4.4所示。
表4.4 使用ROLLUP操作符生成横向小计和总计
当使用CUBE操作符时,在保留原有统计结果的基础上,还会生成横向小计(部门平均工资)、纵向小计岗位平均工资和总计(所有雇员平均工资),如表4.5所示。
表4.5 使用CUBE操作符生成横向小计、纵向小计和总计
(1)使用ROLLUP操作符执行数据统计
当直接使用GROUP BY子句进行多列分组时,只能生成简单的数据统计结果。为了生成数据统计、横向小计和总计统计,可以在GROUP BY子句中使用ROLLUP操作符。
【例4.41】 在emp表中,使用ROLLUP操作符,显示各部门每岗位的平均工资、每部门的平均工资、所有雇员的平均工资,具体代码如下。
SQL>select deptno as部门编号,job as岗位,avg(sal)as平均工资 2 from emp 3 group by rollup(deptno, job);
本例运行结果如图4.43所示。
图4.43 使用ROLLUP操作符
(2)使用CUBE操作符执行数据统计
为了生成数据统计、横向小计、纵向小计以及总计统计,可以使用CUBE操作符。
【例4.42】 在emp表中,使用CUBE操作符,显示各部门各岗位的平均工资、部门平均工资、岗位平均工资、所有雇员平均工资,具体代码如下。
SQL>select deptno as部门编号,job as岗位,avg(sal)as平均工资 2 from emp 3 group by cube(deptno, job);
本例运行结果如图4.44所示。
图4.44 使用CUBE操作符
(3)使用GROUPING函数
当使用ROLLUP或者CUBE操作符生成统计结果时,某个统计结果行可能用到一列或者多列,也可能没有使用任何列。为了确定统计结果是否使用了特定列,可以使用GROUPING函数。如果该函数返回0,则表示统计结果使用了该列;如果函数返回1,则表示统计结果没有使用该列。
【例4.43】 在emp表中,使用GROUPING函数确定统计结果所使用的列,具体代码如下。
SQL> select deptno, job, sum (sal), grouping(deptno), grouping(job) 2 from emp 3 group by rollup(deptno, job);
本例运行结果如图4.45所示。
图4.45 使用GROUPING函数
(4)在ROLLUP操作符中使用复合列
复合列被看作一个逻辑单元的列组合,当引用复合列时,需要用括号括住相关列。通过在ROLLUP操作符中使用复合列,可以略过ROLLUP操作符的某些统计结果。例如,子句GROUP BY ROLLUP(a, b, c)的统计结果等同于GROUP BY(a, b, c)、GROUP BY(a, b)、GROUP BY a以及GROUP BY()的并集;而如果将(b, c)作为复合列,那么子句GROUP BY ROLLUP(a,(b, c))的结果等同于GROUP BY(a, b, c)、GROUP BY a以及GROUP BY()的并集。
【例4.44】 在ROLLUP操作符中使用复合列,在emp表中显示特定部门特定岗位的工资总额以及所有雇员的工资总额,具体代码如下。
SQL> select deptno, job, sum (sal) 2 from emp 3 group by rollup((deptno, job));
本例运行结果如图4.46所示。
图4.46 在ROLLUP操作符中使用复合列
(5)在CUBE操作符中使用复合列
通过在CUBE操作符中使用复合列,可以略过CUBE操作符的某些统计结果。例如,子句GROUP BY CUBE(a, b, c)的统计结果等同于GROUP BY(a, b, c)、GROUP BY(a, b)、GROUP BY(a, c)、GROUP BY(b, c)、GROUP BY a、GROUP BY b、GROUP BY c以及GROUP BY()的并集;而如果将(a, b)作为复合列,那么子句GROUP BY CUBE((a, b), c)的结果等同于GROUP BY(a, b, c)、GROUP BY(a, b)、GROUP BY c以及GROUP BY()的并集。
【例4.45】 在CUBE操作符中使用复合列,在emp表中显示特定部门特定岗位的工资总额以及所有雇员的工资总额,具体代码如下。
SQL> select deptno, job, sum (sal) 2 from emp 3 group by cube((deptno, job));
本例运行结果如图4.47所示。
图4.47 在CUBE操作符中使用复合列
互动练习:使用带ROLLUP操作符的GROUP BY子句。
6.使用GROUPING SETS操作符
GROUPING SETS操作符是GROUP BY子句的进一步扩展。在Oracle Database 9i之前,使用GROUP BY子句一次只能显示单种分组结果,如果要生成多种分组统计结果,那么需要编写多条SELECT分组语句。从Oracle Database 9i开始,通过使用GROUPING SETS操作符,可以合并多个分组的统计结果,从而简化了多个分组操作。下面用实例来说明GROUPING SETS操作符的作用及使用方法。
【例4.46】 在emp表中,执行以下操作。
(1)显示每个部门的平均工资,需要使用部门号(deptno)执行分组统计操作,具体代码如下。
SQL> select deptno, avg (sal) 2 from emp 3 group by deptno;
运行结果如图4.48所示。
图4.48 显示每个部门的平均工资
(2)显示每个岗位的平均工资,需要使用岗位(job)执行分组统计,具体代码如下。
SQL> select job, avg (sal) 2 from emp 3 group by job;
运行结果如图4.49所示。
图4.49 显示每个岗位的平均工资
(3)显示部门平均工资和岗位平均工资,具体代码如下。
SQL> select deptno, job, avg (sal) 2 from emp 3 group by grouping sets(deptno, job);
为了显示多个分组的统计结果,可以使用GROUPING SETS操作符合并分组统计结果。例如,如果既要显示部门平均工资,也要显示岗位的平均工资,那么可以使用GROUPING SETS操作符合并分组结果。运行结果如图4.50所示。
图4.50 显示部门平均工资和岗位平均工资
4.3.4 排序查询
在检索数据时,如果把数据从数据库中直接读取出来,这时查询结果将按照默认顺序排列,但往往这种默认排列顺序并不是用户所需要的。尤其返回数据量较大时,用户查看自己想要的信息非常不方便,因此需要对检索的结果集进行排序。在SELECT语句中,可以使用ORDER BY子句对检索的结果集进行排序,该子句位于FROM子句之后,其语法格式如下:
SELECT columns_list FROM table_name [WHERE conditional_expression] [GROUP BY columns_list] ORDER BY{order_by_expression[ASC|DESC]} [, ...n]
columns_list:字段列表,在GROUP BY子句中也可以指定多个列分组。
table_name:表名。
conditional_expression:筛选条件表达式。
order_by_expression:表示要排序的列名或表达式。关键字ASC表示按升序排列,这也是默认的排序方式;关键字DESC表示按降序排列。
ORDER BY子句可以根据查询结果中的一个列或多个列对查询结果进行排序,并且第一个排序项是主要的排序依据,其他的是次要的排序依据。
1.单列排序
【例4.47】 在SCOTT模式下,检索emp表中的所有数据,并按照部门编号(deptno)、员工编号(empno)排序,具体代码如下。
SQL> select deptno, empno, ename from emp order by deptno, empno;
本例运行结果如图4.51所示。
图4.51 使用ORDER BY子句进行排序
注意
如果使用了ORDER BY子句,则该子句一定是SQL语句的最后一个子句。例如,当在SELECT语句中同时包含多个子句(WHERE、GROUP BY、HAVING、ORDER BY)时,ORDER BY必须是最后一个子句。
还可以在ORDER BY子句中使用列号。当执行排序操作时,不仅可以按照列名、列别名进行排序,也可以按照列或表达式在选择列表中的位置进行排序。如果列名或表达式名称很长,那么使用列位号排序可以缩减排序语句长度。另外,当使用UNION、UNION ALL、INTERSECT、MINUS等集合操作符合并查询结果时,如果选择列表的列名不同,并且希望进行排序,则必须使用列位置进行排序。
【例4.48】 查询emp表中员工的年工资,并按照年工资降序排列,代码如下。
SQL> select empno, ename, sal*12 Annual Salary 2 from emp 3 order by 3 desc;
这里的3表示第3列,所以ORDER BY 3就是按第3列排序。
本例运行结果如图4.52所示。
图4.52 使用列号排序
但是在SQL语句中应该尽可能地不使用ORDER BY子句的上述用法,因为这种用法的易读性实在太差了。在不少有关Oracle SQL的书中根本就没有介绍ORDER BY子句的这种用法。尽管如此,当使用UNION、UNION ALL、INTERSECT、MINUS等集合操作符合并查询结果时,或者为了减少输入,特别是当放在ORDER BY子句之后的列名或表达式很长时,还是可以使用此种用法。本书介绍这一用法的目的是,当看到SQL语句中包含了这一用法时,读者可以理解它,但并不鼓励用。
可以使用非选择列表列排序。当执行排序操作时,多数情况都会选择列表中的列执行排序操作,以便于更直观地显示数据。但是在执行排序操作时,排序列也可以不是选择列表中的列。
【例4.49】 使用非选择列表列进行排序的方法,按工资降序显示员工名。代码如下。
SQL> select ename from emp order by sal desc;
本例运行结果如图4.53所示。
图4.53 使用非选择列表列排序
互动练习:使用列的别名排序。
2.多列排序
当执行排序操作时,不仅可以基于单列进行排序,也可以基于多列进行排序。当以多列进行排序时,首先按照第一列进行排序,当第一列存在相同数据时,再以第二列进行排序,以此类推。
【例4.50】 查询emp表,按照部门号升序工资降序显示雇员名、部门号和工资,代码如下。
SQL> select ename, deptno, sal 2 from emp 3 order by deptno, sal desc;
在此查询语句,首先按照部门号排序,在同一个部门中,按照工资的从高到低进行排序。本例运行结果如图4.54所示。
图4.54 多列排序
4.3.5 多表关联查询
在实际的应用系统开发中会设计多个数据表,每个表的信息不是独立存在的,而是若干个表之间的信息存在一定的关系,当用户查询某一个表的信息时,很可能需要查询关联数据表的信息,这就是多表关联查询。SELECT语句自身是支持多表关联查询的,多表关联查询要比单表查询复杂得多。在进行多表关联查询时,可能会涉及表别名、内连接、外连接、自然连接和交叉连接等概念,下面将对这些内容进行讲解。
1.表的别名
在多表关联查询时,如果多个表之间存在同名的列,则必须使用表名来限定列的引用。例如,在SCOTT模式中,DEPT表和EMP表都有DEPTNO列,那么当用户使用该列关联查询两个表时,就需要通过指定表名来区分这两个列的归属。但是,随着查询变得越来越复杂,语句就会因为每次限定列必须输入表名而变得冗长。对于这种情况,SQL语言提供了设定表别名的机制,使用简短的表别名就可以替代原有较长的表名称,这样就可以大大缩减语句的长度。
【例4.51】 在SCOTT模式下,通过DEPTNO(部门号)列来关联emp表和dept表,并检索这两个表中相关字段的信息,代码及运行结果如下(实例位置:光盘\TM\sl\4\8)。
SQL>select e.empno as员工编号,e.ename as员工名称,d.dname as部门 2 from emp e, dept d 3 where e.deptno=d.deptno 4 and e.job='MANAGER'; 员工编号 员工名称 部门 --------------- ---------- ------------- 7782 CLARK ACCOUNTING 7566 JONES RESEARCH 7698 BLAKE SALES
在上面的SELECT语句中,FROM子句最先执行,然后才是WHERE子句和SELECT子句,这样在FROM子句中指定表的别名后,当需要限定引用列时,其他所有子句都可以使用表的别名。
另外,还需要注意一点,一旦在FROM子句中为表指定了别名,则必须在剩余的子句中都使用表的别名,而不允许再使用原来的表名称,否则,将出现如图4.55所示的提示。
图4.55 关于标识符无效的错误提示
总结一下,使用表的别名的注意事项如下。
表的别名在FROM子句中定义,别名放在表名之后,它们之间用空格隔开。
别名一经定义,在整个查询语句中就只能使用表的别名而不能再使用表名。
表的别名只在所定义的查询语句中有效。
应该选择有意义的别名,表的别名最长为30个字符,但越短越好。
互动练习:理解笛卡尔积的概念。
2.内连接
内连接是一种常用的多表关联查询方式,一般使用关键字INNER JOIN来实现。其中,INNER关键字可以省略,当只使用JOIN关键字时,语句只表示内连接操作。在使用内连接查询多个表时,必须在FROM子句之后定义一个ON子句,ON子句指定内连接操作列出与连接条件匹配的数据行,使用比较运算符比较被连接列的值。简单地说,内连接就是使用JOIN指定用于连接的两个表,使用ON指定连接表的连接条件。若进一步限制查询范围,则可以直接在后面添加WHERE子句。内连接的语法格式如下:
SELECT columns_list FROM table_name1[INNER] JOIN table_name2 ON join_condition;
columns_list:字段列表。
table_name1和table_name2:两个要实现内连接的表。
join_condition:实现内连接的条件表达式。
【例4.52】 在SCOTT模式下,通过deptno字段来内连接emp表和dept表,并检索这两个表中相关字段的信息,代码及运行结果如下。
SQL>select e.empno as员工编号,e.ename as员工名称,d.dname as部门 2 from emp e inner join dept d 3 on e.deptno=d.deptno;
本例运行结果如图4.56所示。
图4.56 内连接操作
由于上面代码表示内连接操作,所以在FROM子句中完全可以省略INNER关键字。
3.外连接
使用内连接进行多表查询时,返回的查询结果中只包含符合查询条件和连接条件的行。内连接消除了与另一个表中的任何行不匹配的行,而外连接扩展了内连接的结果集,除了返回所有匹配的行外,还会返回一部分或全部不匹配的行,这主要取决于外连接的种类。外连接通常有以下3种。
左外连接:关键字为LEFT OUTER JOIN或LEFT JOIN。
右外连接:关键字为RIGHT OUTER JOIN或RIGHT JOIN。
完全外连接:关键字为FULL OUTER JOIN或FULL JOIN。
与内连接不同的是,外连接不只列出与连接条件匹配的行,还能够列出左表(左外连接时)、右表(右外连接时)或两个表(全部外连接时)中所有符合搜索条件的数据行。
(1)左外连接
左外连接的查询结果中不仅包含了满足连接条件的数据行,而且还包含左表中不满足连接条件的数据行。
【例4.53】 首先使用INSERT语句在emp表中插入新记录(注意没有为deptno和dname列插入值,即它们的值为null),然后实现emp表和dept表之间通过deptno列进行左外连接,具体代码如下(实例位置:光盘\TM\sl\4\9)。
SQL> insert into emp(empno, ename, job) values(9527, 'EAST', 'SALESMAN'); 已创建1行。 SQL> select e.empno, e.ename, e.job, d.deptno, d.dname 2 from emp e left join dept d 3 on e.deptno=d.deptno;
本例运行结果如图4.57所示。
图4.57 左外连接操作
从上面的查询结果中可以看到,虽然新插入数据行的deptno列值为null,但该行记录仍然出现在查询结果中,这说明左外连接的查询结果会包含左表中不满足“连接条件”的数据行。
(2)右外连接
同样道理,右外连接的查询结果中不仅包含了满足连接条件的数据行,而且还包含右表中不满足连接条件的数据行。
【例4.54】 在SCOTT模式下,实现emp表和dept表之间通过deptno列进行右外连接,具体代码如下。
SQL> select e.empno, e.ename, e.job, d.deptno, d.dname 2 from emp e right join dept d 3 on e.deptno=d.deptno;
本例运行结果如图4.58所示。
图4.58 右外连接
从上面的查询结果中可以看到,虽然部门编号为40的部门现在在emp表中还没有员工记录,但它却出现在查询结果中,这说明右外连接的查询结果会包含右表中不满足“连接条件”的数据行。
在外连接中也可以使用外连接的连接运算符,外连接的连接运算符为“(+)”,该连接运算符可以放在等号的左边,也可以放在等号的右边,但一定要放在缺少相应信息的那一边,如放在e.deptno所在的一方。
注意
当使用“(+)”操作符执行外连接时,应该将该操作符放在显示较少行(完全满足连接条件行)的一端。
上面的查询语句还可以这么写,代码如下:
SQL> select e.empno, e.ename, e.job, d.deptno, d.dname 2 from emp e, dept d 3 where e.deptno(+)=d.deptno;
本例运行结果如图4.59所示。
图4.59 使用外连接的连接运算符“(+)”
使用“(+)”操作符时应注意:
当使用“(+)”操作符执行外连接时,如果在WHERE子句中包含多个条件,则必须在所有条件中都包含“(+)”操作符。
“(+)”操作符只适用于列,而不能用在表达式上。
“(+)”操作符不能与ON和IN操作符一起使用。
(3)完全外连接
在执行完全外连接时,Oracle会执行一个完整的左外连接和右外连接查询,然后将查询结果合并,并消除重复的记录行。
【例4.55】 在SCOTT模式下,实现emp表和dept表之间通过deptno列进行完全外连接,具体代码如下(实例位置:光盘\TM\sl\4\10)。
SQL> select e.empno, e.ename, e.job, d.deptno, d.dname 2 from emp e full join dept d 3 on e.deptno=d.deptno;
本例运行结果如图4.60所示。
图4.60 完全外连接
4.自然连接
自然连接和内连接的功能相似,自然连接是指在检索多个表时,Oracle会将第一个表中的列与第二个表中具有相同名称的列进行自动连接。在自然连接中,用户不需要明确指定进行连接的列,这个任务由Oracle系统自动完成,自然连接使用NATURAL JOIN关键字。
【例4.56】 在emp表中检索工资(sal字段)大于2000的记录,并实现emp表与dept表的自然连接,具体代码如下:
SQL> select empno, ename, job, dname 2 from emp natural join dept 3 where sal>2000;
本例运行结果如图4.61所示。
图4.61 自然连接
由于自然连接强制要求表之间必须具有相同的列名称,这样容易在设计表时出现不可预知的错误,所以在实际应用系统开发中很少用到自然连接。但这毕竟是一种多表关联查询数据的方式,在某些特定情况下还是有一定的使用价值。另外,需要注意的是,在使用自然连接时,不能为列指定限定词(即表名或表的别名),否则Oracle系统会弹出“ORA-25155: NATURAL连接中使用的列不能有限定词”的错误提示。
5.自连接
在应用系统开发中,用户可能会拥有“自引用式”的外键。“自引用式”外键是指表中的一个列可以是该表主键的一个外键。
自连接主要用在自参考表上显示上下级关系或者层次关系。自参照表是指在同一张表的不同列之间具有参照关系或主从关系的表。例如,emp表包含empno(雇员号)和mgr(管理员号)列,两者之间就具有参照关系。这样用户就可以通过mgr列与empno列的关系,实现查询某个管理者所管理的下属员工信息,如图4.62所示。
图4.62 emp表中empno列和mgr列之间的关系
根据empno列和mgr列的对应关系,可以确定雇员JONES、BLAKE和CLARK的管理者为KING。为了显示雇员及其管理者之间的对应关系,可以使用自连接。因为自连接是在同一张表之间的连接查询,所以必须定义表别名。通过下面的实例,说明使用自连接的方法。
【例4.57】 在SCOTT模式下,查询所有管理者所管理的下属员工信息,具体代码如下:
SQL>select em2.ename上层管理者,em1.ename as下属员工 2 from emp em1 left join emp em2 3 on em1.mgr=em2.empno 4 order by em1.mgr;
本例运行结果如图4.63所示。
图4.63 自连接
6.交叉连接
交叉连接实际上就是不需要任何连接条件的连接,它使用CROSS JOIN关键字来实现,其语法格式如下:
Select colums_list From table_name1 cross join table_name2
colums_list:字段列表。
table_name1和table_name2:两个实现交叉连接的表名。
交叉连接的执行结果是一个笛卡尔积,这种查询结果是非常冗余的,但可以通过WHERE子句来过滤出有用的记录信息,如下面实例所示。
【例4.58】 在SCOTT模式下,通过交叉连接dept表和emp表,计算出查询结果的行数,具体代码如下:
SQL> select count(*) 2 from dept cross join emp;
本例运行结果如图4.64所示。
图4.64 交叉连接