在实际中,经常是从多张表中查询数据。本节学习多表连接查询、子查询等高级SELECT语句的应用。
多表查询是指SELECT命令中显示的列来源于多个数据表;
连接查询将多个表以某个或某些列为条件进行连接操作而检索出关联数据的过程。多个表之间以共同列而关联在一起。
用WHERE子句对数据记录限定检索条件,在WHERE子句中使用”=”,可以实现表的简单连接。表示第一个表中的列与第二个表中相应列匹配后才会在结果集中显示。
SELECT empno, ename, sal, scott.emp.deptno, scott.dept.deptno, dname
FROM scott.emp, scott.dept
WHERE scott.emp.deptno=scott.dept.deptno;
表scott.emp和scott.dept都包含列deptno,根据这个共同的列,在WHERE子句中使用等号(=)进行连接。
SELECT empno, ename, sal, scott.emp.deptno, scott.dept.deptno, dname
FROM scott.emp, scott.dept;
两表仅通过SELECT子句和FROM子句建立连接,而不加连接条件,查询结果为两张表的笛卡尔积,即用第一个表中的每一行与第二个表中的每一行进行连接,结果集中的行数是两表行数的乘积、列数是两表列数的和。
在多表查询中,如果多个表之间存在同名的列,则必须使用表名进行限定。为增加可读性,可以使用表的别名,还能提高SELECT语句的执行效率。
在FROM语句中引用该表时,将表别名跟在表的实际名称之后。
SELECT empno, ename, sal, e.deptno, d.deptno, dname
FROM scott.emp e, scott.dept d
WHERE scott.emp.deptno=scott.dept.deptno;
注意:如果为表设置了别名,表的实际名称也就会被覆盖,所有引用表名称的地方都必须使用表别名,而不能再用实际表名称;
另外,为表设置别名时,不能使用AS关键字。
多表之间可以使用英文逗号进行分割,还可以使用关键字JOIN。
包括:内连接和外连接。
SELECT column_list
FROM table_name1 join_type table_name2 [ON (join_condition)]
一般使用INNER JOIN关键字指定内连接,INNER可以省略,默认表示内连接。查询结果中只包含两表的公共字段值相等的行,列可以是两表中的任意列
使用INNER JOIN 连接scott.emp和scott.dept两个表,查询员工部门为accounting的信息。
SELECT empno, ename , sal ,d.deptno, dname
FROM scott.emp e INNER JOIN scott.dept d ON e.deptno=d.deptno
WHERE dname =‘ACCOUNTING’;
ON后面的条件中,除了可以使用(=)运算符比较,还可以使用比较运算符、LIKE 、IN和BETWEEN等关键字。
自然连接(NATURAL JOIN)是一种特殊的等值连接。它是由系统根据两表的同名字段作等值比较的内连接,因此不需要使用关键字ON指定连接条件。
SELECT empno, ename , sal ,d.deptno, dname
FROM scott.emp e NATURAL JOIN scott.dept d
WHERE dname =‘ACCOUNTING’;
包括左外连接、右外连接、全外连接
(1)左外连接 LEFT JOIN
结果集中包括两表连接后满足ON后面指定的连接条件的行,还显示JOIN关键字左侧表中所有满足检索条件的行,如何左表的某行在右表中没有匹配行,则在结果中,右表的所有选择列均为NULL。
(2)右外连接 RIGHT JOIN
是左外连接的反向连接。
(3)完全外连接 FULL JOIN
完全外连接查询的结果集包括两表内连接的结果集和左表与右表中不满足条件的行。
交叉连接(CROSS JOIN)是用左表中的每一行与右表中的每一行进行连接,不能使用ON关键字。所得到的结果将是这两个表中各行数据的所有组合,即这两个表所有数据的笛卡尔积。
在查询条件中,可以嵌套另一个查询,即在一个SELECT、UPDATE或DELETE语句内部使用一个SELECT语句的查询。
外层的SELECT语句叫做外部查询,内层的SELECT语句叫做子查询;
使用子查询主要是将查询的结果作为外部主查询的查询条件;
子查询可以嵌套多层,但每层嵌套需要使用圆括号()括起来;
大部分子查询是放在SELECT语句的WHERE子句中使用。
子查询的类型:
(2)根据子查询返回一行或多行查询结果,可将子查询分为2类:
单行子查询;多行子查询
(2)子查询的两种比较操作符
单行子查询的操作符:+、>、<、>=、<=、<>和!<>;
多行子查询的操作符:ALL、ANY、IN或函数min,max。
通常在一个SELECT、UPDATE或DELETE语句的WHERE子句中充当查询、修改或删除的条件
SELECT column_list FROM table_name WHERE expression operator (
select column_list FROM table_name WHERE condition
GROUP BY exp HAVING having
);
注:在WHERE子句中使用子查询时,子查询语句中不要用ORDER BY 子句。如果确实要用ORDER BY 子句对结果进行排序输出,可以在外部查询中使用该子句。
例如:在子查询中使用ORDER BY 子句,将查询结果按照empno列降序输出。
SELECT empno, ename, sal, d.deptno FROM scott.emp
WHERE deptno IN (
SELECT deptno FROM scott.emp
WHERE empno>7782 ORDER BY empno DESC);
这时,执行结果会出现错误信息。因为第四行的ORDER BY 子句无效(软件不会理睬)。更改如下:
SELECT empno, ename, sal, d.deptno FROM scott.emp
WHERE deptno IN (
SELECT deptno FROM scott.emp
WHERE empno>7782)
ORDER BY empno DESC;
单行子查询指子查询只返回单行单列数据;多行子查询是指子查询返回多行单列数据,即一组数据。
当子查询是单列多行子查询时,必须使用多行比较运算符,包括IN、NOT IN、ANY、ALL和SOME。
IN和NOT IN可以独立使用,表示用来比较表达式的值是否在子查询的结果集中。
ANY和ALL必须与单行比较运算符组合起来使用。
关联子查询是指需要引用外查询表的一列或多列的子查询语句,这种子查询与外部语句相关,是主要通过EXISTS运算符实现的查询。EXISTS用于测试子查询的结果是否为空,如子查询的结果集不为空,则EXISTS返回TRUE,否则返回FALSE。
例:检索scott.emp和scott.dept表中在NEW YORK工作的所有员工信息。
SELECT ename, job, sal,d.deptno FROM scott.emp
WHERE EXISTS (
SELECT * FROM scott.dept WHERE deptno=emp.deptno
AND loc = 'NEW YOK');
外层SELECT语句返回的每一行数据都要根据子查询来评估,如果EXISTS关键字中指定的条件为真,查询结果就包括这一行。
这里EXISTS 关键字只检索子查询返回的数据是否存在,因此子查询可以返回一个常量,这样可以提高查询的性能。如果使用1代替上述子查询语句中的*列,查询结果不变。
EXISTS与IN的比较:
IN操作符实现指定匹配查询,检索特定的值是否包含在值列表中;
EXISTS操作符只是检查行是否存在,针对行的存在性。
单行子查询和多行子查询获得的结果都是单列数据,但是多列子查询获得的是多列任意行数据。多列子查询是指返回多列数据的子查询。
使用子查询比较多个列的数据时,可以使用一下两种方式:
a.成对比较:要求多个列的数据必须同时匹配。
b.非成对比较:通过指定连接关键字,例如AND或OR等,指定多个列的数据是否必须同时匹配。
SELECT ename, sal,comm.,d.deptno FROM scott.emp
WHERE (sal, NVL(COMM. , -1)) IN (
SELECT sal,NVL(comm. , -1) FROM scott.emp
WHERE deptno = 30);
NVL()函数用于从两个表达式返回一个非NULL值。此处的查询为成对比较。
SELECT ename, sal,comm.,d.deptno FROM scott.emp
WHERE sal IN (
SELECT sal FROM scott.emp
WHERE deptno = 30) AND NVL(COMM. , -1) IN(
SELECT NVL(comm. , -1) FROM scott.emp
WHERE deptno=30);
此处为非成对比较。