Oracle 查询(SELECT)语句(一)
Ø 简介
本文介绍 Oracle 中查询(SELECT)语句的使用,在 SQL 中 SELECT 语句相对增删改(CUD/DML)的语句知识点较多,也是比较重要 SQL 编程技术,所以这里拿出来单独学习。
首先,我们先来理一下思路,我们知道查询通常包含以下内容:
Ø 查询所有列(*)
Ø 查询指定的列
Ø 根据指定的条件查询,即 WHERE 条件
Ø 去除重复行,即 DISTINCT 子句
Ø 查询数据聚合,即 COUNT()、MAX()、MIN() 等
Ø 按条件输出,即 CASE WHEN THEN 子句
Ø 排序(ORDER BY)
Ø 分组(GROUP BY)与分组过滤(HAVING)
Ø 连接查询(INNER JOIN、LEFT JOIN 等)
Ø 子查询(SELECT 子查询、WHERE 子查询等)
Ø 其他
好了,既然知道了有这些查询功能,下面我们就一一突破,准备了以下内容:
1. 准备数据
2. SELECT 语法
3. 基本用法
4. WHERE 子句
5. 聚合查询
6. CASE WHEN THEN 子句
7. 排序
8. 分组与过滤
9. 多表查询
10. 子查询(单行与多行子查询)
11. 连接查询
1. 准备数据
1) 创建表结构
CREATE TABLE JNUser (
UserId NUMBER(10) NOT NULL,
Name VARCHAR2(8) NOT NULL,
Sex NUMBER(1) NOT NULL, --性别(0 女,1 男,2 未知)
Age NUMBER(2) NOT NULL,
Birthday DATE NOT NULL,
City VARCHAR2(6) NOT NULL,
IdNumber CHAR(18) NOT NULL,
Salary FLOAT,
Remarks VARCHAR2(4000) NOT NULL,
PRIMARY KEY (UserId),
CONSTRAINT UQ_JNUser_IdNumber UNIQUE (IdNumber),
CONSTRAINT CK_JNUser_Sex CHECK (Sex >= 0 AND Sex <= 2)
);
CREATE TABLE JNOrder (
OrderId NUMBER(10) NOT NULL,
UserId NUMBER(10) NOT NULL,
OrderNo VARCHAR2(16) NOT NULL,
TotalAmount FLOAT NOT NULL,
OrderDate DATE NOT NULL,
Remarks VARCHAR2(4000),
PRIMARY KEY (OrderId),
CONSTRAINT UQ_JNOrder_OrderNo UNIQUE (OrderNo)
);
2) 插入数据
INSERT ALL
--用户数据
INTO JNUser VALUES(1, '孙器璇', 2, 17, to_date('2002/11/18', 'yyyy/mm/dd'), '北京', '17730094858437637X', 10400, 'hello')
INTO JNUser VALUES(2, '周器璇', 0, 22, to_date('1966/05/12', 'yyyy/mm/dd'), '武汉', '534668342907162757', 15000, '用户2')
INTO JNUser VALUES(3, '张二娃', 1, 24, to_date('1967/01/24', 'yyyy/mm/dd'), '杭州', '36935324879542561X', 7600, '大家好,我是张二娃')
INTO JNUser VALUES(4, '陈悴', 2, 22, to_date('1984/10/11', 'yyyy/mm/dd'), '武汉', '585823614699788016', 7100, '你好,贵姓?')
INTO JNUser VALUES(5, '钱悴', 2, 58, to_date('1961/10/21', 'yyyy/mm/dd'), '深圳', '449489822531507067', 15320, 'hello 张二娃')
INTO JNUser VALUES(6, '张无忌', 1, 61, to_date('1988/02/24', 'yyyy/mm/dd'), '杭州', '385929993868497572', 17800, '哈啰,我是张无忌')
INTO JNUser VALUES(7, '张大彪', 1, 24, to_date('1995/05/16', 'yyyy/mm/dd'), '上海', '466484458398445233', 1200, '我说了,我是彪爷')
INTO JNUser VALUES(8, '冯小二', 1, 29, to_date('1976/09/28', 'yyyy/mm/dd'), '广州', '40016865591929392X', 5700, '客观,吃点莫子?')
INTO JNUser VALUES(9, 'DBA', 2, 32, to_date('1997/04/28', 'yyyy/mm/dd'), '上海', '488500420672587475', 16000, '小崽子们,你们好,我是 DBA')
INTO JNUser VALUES(10, '陈双', 0, 18, to_date('1995/02/07', 'yyyy/mm/dd'), '武汉', '942310204386191671', 10343, '我是双儿')
--订单数据
INTO JNOrder VALUES(1, 2, '58977924501badf7', 2620, to_date('2019/06/10', 'yyyy/mm/dd'), '又是双十一,我卖完再剁手啦')
INTO JNOrder VALUES(2, 3, '316626433f743978', 1115, to_date('2019/08/19', 'yyyy/mm/dd'), '其他没什么,就是想买')
INTO JNOrder VALUES(3, 4, '8698789361c78946', 1734, to_date('2019/03/16', 'yyyy/mm/dd'), '')
INTO JNOrder VALUES(4, 5, '58716471589e3df2', 897, to_date('2019/11/13', 'yyyy/mm/dd'), ' ')
INTO JNOrder VALUES(5, 8, '5583165337e0ee25', 2097, to_date('2019/01/21', 'yyyy/mm/dd'), '我已下单,快点发货,老板')
INTO JNOrder VALUES(6, 8, '395799340826d6f2', 304, to_date('2019/02/27', 'yyyy/mm/dd'), ' ')
INTO JNOrder VALUES(7, 3, '887799782996b3f1', 1246, to_date('2019/09/20', 'yyyy/mm/dd'), '老板,给我来个好看的包装盒,我要送老丈人')
INTO JNOrder VALUES(8, 8, '39482046468266d6', 306, to_date('2019/02/28', 'yyyy/mm/dd'), NULL)
SELECT * FROM DUAL;
COMMIT;
2. SELECT 语法
SELECT <列名1> [<,列名2>]… FROM <表名或视图名>
[WHERE <条件表达式>]
[GROUP BY <列名1> [HAVING <条件表达式>]
[ORDER BY <列名1> [ASC | DESC]]
说明:如果输出所有列,可以指定为"*"。这里简单阐述下(SELECT * 与 SELECT 所有列)的一些区别:
比较项 | SELECT * | SELECT 指明所有列 | 结论 |
1. 执行效率 | 需要检索表中的所有列名 | 不需要检索列名 | 后者效率略高 |
2. 后续新增字段 | 原程序会直接将新字段查出 | 需要重新更改程序中的 SQL 语句 | 视业务情况而定 |
3. 难易程度 | 比较便捷 | 比较麻烦 | 前者有优势 |
4. 字段较多时(比如一两百多个) | 减少网络流量 | 增加网络流量 | 前者有优势(可忽略的) |
提示:如有其他看法,欢迎讨论。
3. 基本语法
1) 查询所有列
SELECT * FROM JNUser;
SELECT * FROM JNOrder;
2) 查询指定列
SELECT UserId, Name FROM JNUser;
3) 定义表和列的别名
SELECT UserId 用户Id, Name 用户名, Age AS "性 别+-", U.City, U.City "City", '关键字作为列名' AS "Select" FROM JNUser U;
说明:
1. 定义表别名时,不能使用 AS 关键字,定义别名后可以使用或不使用别名;
2. 定义列别名时,可以使用 AS 关键字也可以不使用;
3. 定义列别名时,如果无特殊字符(中文或英文),后面直接跟上别名即可;
4. 定义列别名时,使用("")双引号可以解决以下两个问题:
1) 别名中包含特殊字符,如:空格、-、+等字符;
2) 别名中的英文显示为指定的大小写(默认为大写);
3) 别名使用到对象名或保留字(关键字)时;
4) ("")双引号表示列名将按所指定的显示。
4) 算术运算符(+、-、*、/)
SELECT Salary, Salary + 200, Salary - 200, Salary * 12, Salary / 30 FROM JNUser;
5) 比较运算符(>、>=、<、<=、=、!= | <>)
SELECT * FROM JNUser WHERE City <> '上海'; --或者使用 !=
6) 列连接
SELECT UserId || ' - ' || Name || ' - ' || City AS UserDesc FROM JNUser;
提示:|| 在 Oracle 中用于字符串拼接,类似其他语言中字符串拼接的 + 号。
7) 构建表达式
SELECT ('SELECT * FROM ' || TABLE_NAME || ';') AS SEL FROM ALL_TABLES WHERE OWNER = 'USER01';
8) 去除重复行
SELECT DISTINCT Age, City FROM JNUser;
说明:去除每列相同的数据行,只返回任意两行不重复的记录。
9) 处理 NULL 值
SELECT ORDERNO, NVL(REMARKS, '无备注') AS REMARKS FROM JNOrder; --当 REMARKS 字段为 NULL 时,将返回“无备注”
10) 判断 NULL 与非 NULL
SELECT * FROM JNOrder WHERE REMARKS IS NULL; --非空使用 IS NOT NULL
4. WHERE 子句
1) 复合条件
SELECT * FROM JNUser WHERE Salary > 10000; --查出工资大于10000的用户
SELECT * FROM JNUser WHERE Sex = 0 AND Salary > 10000; --查出性别为女性,且工资大于10000的用户
SELECT * FROM JNUser WHERE (Sex = 0 OR Sex = 2) AND Salary > 10000; --查出性别为女性或者未知,且工资大于10000的用户
2) IN 子句
IN 子句表示取出值包含在列举值范围内的记录。
SELECT * FROM JNUser WHERE City IN('武汉', '上海', '北京') ORDER BY City; --查出所在城市在武汉、上海、北京的用户,并按城市升序排序
3) BETWEEN 子句
BETWEEN 子句表示取出值在起始值与结束值之间的记录,且包含起始值与结束值(它是包头包围的闭区间)。
SELECT * FROM JNUser WHERE Salary BETWEEN 10400 AND 16000; --查出工资在10400(包含)至16000(包含)之间的用户
4) NOT 取反
NOT 子句用于对条件表达式进行取反,例如:
SELECT * FROM JNUser WHERE NOT(AGE > 30); --实际查出了年龄小于或等于30的用户
5) EXISTS、NOT EXISTS
EXISTS 子句可以用于判断是否存在结果集,EXISTS 表示当存在结果集时为 true,否则为 false;而 NOT EXISTS 则对 EXISTS 的结果进行取反。
SELECT * FROM JNUser t1 WHERE EXISTS(SELECT 1 FROM JNOrder t2 WHERE t1.userid = t2.userid); --查询下过订单的用户
6) LIKE 模糊匹配
Ø %表示零到多个字符;
Ø _表示任意单个字符;
Ø []表示在列举范围内的字符;
SELECT * FROM JNUser WHERE Remarks LIKE 'h%'; --查出备注以 h 开头的用户(注意:匹配时区分大小写)
SELECT * FROM JNUser WHERE Remarks LIKE '%好%'; --查出备注中包含“好”字的用户
SELECT * FROM JNUser WHERE Name LIKE '张__'; --查出姓张的,并且名为2个字的用户
注意:在实际应用场景中不到万不得已,尽量避免使用 LIKE 模糊查询,因为使用模糊的字段就不能使用索引了,影响查询效率。
7) 是否区分大小写
SELECT * FROM JNUser WHERE REMARKS = 'hello'; --1条记录
SELECT * FROM JNUser WHERE REMARKS = 'Hello'; --0条记录
结论:Oracle 中的值是区分大小写的,但是执行语句或表名、列名、等对象名不区分大小写。
8) 日期比较(查询1988年之后出生的用户)
SELECT * FROM JNUser WHERE BIRTHDAY >= '1988-01-01'; --ORA-01861: 文字与格式字符串不匹配
SELECT * FROM JNUser WHERE BIRTHDAY >= to_date('1988-01-01', 'yyyy-mm-dd'); --使用 to_date() 函数转为日期类型
SELECT * FROM JNUser WHERE to_char(BIRTHDAY, 'yyyy-mm-dd') >= '1988-01-01'; --或者使用 to_char() 函数转为字符类型
SELECT * FROM JNUser WHERE to_char(BIRTHDAY, 'mm') >= '05'; --大于5月份的用户
9) 使用&变量
SELECT * FROM JNUser WHERE City = '&City' AND Salary > &Salary;
说明:该查询方式只适合在 PL/SQL Developer 中使用,提供一个条件参数占位符,用于在窗口中输入参数值。
5. 聚合查询
聚合函数是 Oracle 提供的内置函数,用于计算某一列的聚合计算(如:数量、平均值等)
SELECT COUNT(*) AS COUNT FROM JNUser WHERE City = '上海'; --统计有多少上海用户,*可以改为数字1
SELECT MAX(SALARY) AS MAX FROM JNUser WHERE City = '上海'; --查询上海用户的最高薪资
SELECT MIN(SALARY) AS MIN FROM JNUser WHERE City = '上海'; --查询上海用户的最低薪资
SELECT SUM(SALARY) AS SUM FROM JNUser WHERE City = '上海'; --查询上海用户的总薪资
SELECT AVG(SALARY) AS AVG FROM JNUser WHERE City = '上海'; --查询上海用户的平均薪资
SELECT COUNT(DISTINCT t."OrderId") FROM temp01 t WHERE t."CategoryId" = t1."CategoryId" --统计时进行去重
注意:
1. MAX 和 MIN 函数计算出两个或两个以上的最大值或最小值时,同样之后只返回一个值。
2. COUNT、SUM 和 AVG 函数计算统计时,为 NULL 的值将不参与计算。
6. CASE WHEN THEN 子句
CASE WHEN THEN 定义在 SELECT 与 FROM 之间,用于判断当条件符合 WHEN 时,就返回 THEN 对应的值,否则返回 ELSE 中的值。
1) 第一种写法
SELECT City, (CASE City WHEN '上海' THEN 'SH' WHEN '武汉' THEN 'WH' ELSE 'WZ' END) AS JianCheng FROM JNUser;
2) 第二种写法
SELECT City, (CASE WHEN City = '上海' THEN 'SH' WHEN City = '武汉' THEN 'WH' ELSE 'WZ' END) AS JianCheng FROM JNUser;
7. 排序
排序采用 ORDER BY 子句,包括升序(ASC)或降序(DESC)的排序方式。
SELECT * FROM JNUser ORDER BY Salary; --ASC 升序排序(默认排序方式)
SELECT * FROM JNUser ORDER BY Salary DESC; --DESC 降序排序
SELECT * FROM JNUser ORDER BY Sex ASC, Salary DESC; --首先按性别升序排序,再按薪资降序排序
SELECT * FROM JNUser WHERE Sex = 1 ORDER BY 4 ASC; --查出男性用户所有用户,并按照第4列(AGE)升序排序
SELECT Salary AS Salary2, t.* FROM JNUser t ORDER BY Salary2; --注意:排序的字段可以使用别名,也可以使用原有字段名(Salary)
n NULL 值排序处理
在 Oracle 中排序,NULL 默认为是最大值。所以如果降序排序,为 NULL 的行始终排在前面。如果想改变这个现象,可以使用以下关键字。例如:
--将 NULL 置于最后
SELECT * FROM "G_Brand" ORDER BY "UpdateTime" DESC NULLS LAST;
--将 NULL 置于最前(默认处理)
SELECT * FROM "G_Brand" ORDER BY "UpdateTime" DESC NULLS FIRST;
8. 分组与过滤
分组采用 GROUP BY 子句,注意:包含分组的 SELECT 输出列中,只能包含分组的列和聚合计算的列。GRUOUP BY 分组后还可以跟 HAVING 子句,HAVING 通常用于聚合计算并过滤。
1) 查出工资大于8000的用户,每个性别各占的数量,并按性别倒序排序
SELECT Sex, COUNT(1) AS Count FROM JNUser
WHERE Salary > 8000
GROUP BY Sex
ORDER BY Sex DESC;
2) 查出工资大于8000的用户,每个性别的平均工资大于13000,并按平均工资倒序排序
SELECT Sex, AVG(Salary) AS Avg FROM JNUser
WHERE Salary > 8000
GROUP BY Sex
HAVING AVG(Salary) > 13000
ORDER BY AVG(Salary) DESC;
或者
SELECT * FROM (
SELECT Sex, AVG(Salary) AS Avg FROM JNUser
WHERE Salary > 8000
GROUP BY Sex
HAVING AVG(Salary) > 13000
) t ORDER BY Avg DESC;
3) 查出每个城市里工资大于800的每个性别的平均工资,和最高工资,和人数,并按城市和性别排序
SELECT City, Sex, AVG(Salary) Avg, MAX(Salary) Max, COUNT(1) Count FROM JNUser
WHERE Salary > 800
GROUP BY City, Sex --多个字段同时分组
ORDER BY City, Sex;
9. 多表查询
Ø 注意
1) 在后续的查询示例中,将使用 scott 用户下的系统自带表,作为演示数据。
SELECT * FROM dept; --部门表 4条
SELECT * FROM emp; --员工表 14条
SELECT * FROM salgrade; --工资级别表 5条
SELECT * FROM bonus; --工资表 0条
2) 在讨论多表查询前,我们先搞清楚一件事。就是当我们在查询时,大于一张表的情况下,在 WHERE 子句中没有跟第二张表或后面的其他表做“关联”,将产生笛卡尔积。例如:
SELECT * FROM dept, emp, salgrade WHERE dept.deptno = emp.deptno;
以上这条语句中,WHERE 只对 dept 和 emp 表进行了关联,没有对 salgrade 的关联(或处理),所以结果会产生70(14*5)条数据;如果三张表都不关联,将产生280(4*14*5)条数据。
1) 查出员工部门编号为20的部门、姓名、和薪资
SELECT t1.dname, t2.ename, t2.sal FROM dept t1, emp t2
WHERE t1.deptno = 20 AND t1.deptno = t2.deptno;
或者(使用内连接)
SELECT t1.dname, t2.ename, t2.sal FROM dept t1
INNER JOIN emp t2 ON(t1.deptno = t2.deptno);
提示:提示:在使用多表连接时,选择显示的列尽量指明是属于哪个一个表(比如:t1.dname),一是可读性更好,二是当多张表中有相同列时,不指定表名会报错。
2) 查出员工姓名、薪资和薪资级别
SELECT t1.ename, t1.sal, t2.grade FROM emp t1, salgrade t2
WHERE t1.sal BETWEEN t2.losal AND t2.hisal;
注意:这里的关联方式,实际上并不是关联查询,只是使用到了 salgrade 表进行取值,这样同样不会产生笛卡尔积。
或者(使用子查询)
SELECT ename, sal, (SELECT grade FROM salgrade WHERE sal BETWEEN losal AND hisal) grade FROM emp t1;
3) 查出 SMITH 的上级领导
SELECT t2.* FROM emp t1, emp t2
WHERE t1.ename = 'SMITH' AND t1.mgr = t2.empno;
或者
SELECT t1.* FROM emp t1
WHERE t1.empno = (SELECT mgr FROM emp WHERE ename = 'SMITH');
提示:两种方案区别在于:当有两个人叫 SMITH 时,第一个 SQL 会出现两条记录;而第二个 SQL 会直接报错(因为子查询比允许返回两条相同的记录)。
4) 查出各个员工姓名和他上级领导的姓名
SELECT t1.ename, t2.ename FROM emp t1, emp t2
WHERE t1.mgr = t2.empno;
10. 子查询
子查询是指在一个 SELECT 查询语句中再嵌入另一个 SELECT 查询,或者被成为嵌套查询。子查询可以位于 SELECT 选择列中,也可位于 FROM 之后(派生表子查询),同时还可以为 WHERE 条件子查询,而子查询又分为单行单列和多行多列子查询,下面分别用示例演示:
1) 查出 SMITH 同部门的所有员工
SELECT * FROM emp WHERE deptno = (SELECT deptno FROM emp WHERE ename = 'SMITH');
2) 查询与10号部门所有员工工作岗位相同的其他员工姓名、工资、部门号
SELECT ename, sal, deptno FROM emp
WHERE job IN(SELECT job FROM emp t1 WHERE t1.deptno = 10);
3) 查询平均工资比部门平均工资高的员工(派生表子查询)
SELECT t1.* FROM emp t1, (
SELECT deptno, AVG(sal) avgsal FROM emp GROUP BY deptno
) t2 WHERE t1.deptno = t2.deptno AND t1.sal > t2.avgsal;
4) 查询与 SMITH 部门和工作岗位相同的员工(多列子查询)
SELECT * FROM emp t1 WHERE 1=1
AND t1.deptno = (SELECT deptno FROM emp WHERE ename = 'SMITH')
AND t1.job = (SELECT job FROM emp WHERE ename = 'SMITH');
--或者(注意:这个语法在 MSSQL 中是不支持的)
SELECT * FROM emp t1 WHERE 1=1
AND (t1.deptno, t1.job) = (SELECT deptno, job FROM emp WHERE ename = 'SMITH');
--或者(使用派生表 + 多表查询)
SELECT * FROM emp t1, (
SELECT deptno, job FROM emp WHERE ename = 'SMITH'
) t2 WHERE t1.deptno = t2.deptno AND t1.job = t2.job;
提示:由于第一种方式根据部门和工作岗位执行了两个子查询;而后面两个只查询了一个子查询,所以建议使用后面两种方式。
5) 查询部门信息和员工数量
SELECT t1.*, (SELECT COUNT(*) FROM emp t2 WHERE t2.deptno = t1.deptno) Count FROM dept t1;
--或者
SELECT t1.*, t2.Count FROM dept t1, (
SELECT deptno, COUNT(1) Count FROM emp GROUP BY deptno
) t2 WHERE t1.deptno = t2.deptno(+);
注意:第二种方式,当 dept 中的部门编号中在 emp 中没有时,该部门不会统计出来。此时可以使用 t2.deptno(+) 处理,表示左连接。
11. 连接查询
连接查询中分为内连接和外连接。
Ø 内连接
内连接很类似我们之前讨论的多表连接,例如:
SELECT t1.ename, t2.dname FROM emp t1, dept t2
WHERE t1.deptno = t2.deptno;
而内连接只是在语法形式上存在差别,其实现功能都是一样的,下面使用内连接:
SELECT t1.ename, t2.dname FROM emp t1
INNER JOIN dept t2 ON(t1.deptno = t2.deptno); --由于内连接是默认的连接方式,所以 INNER 关键字可以省略
可以看到,内连接只是语法结构变了,将之前的 WHERE 条件部分,放在了 ON() 中了,同时使用了 INNER JOIN 关键字。
Ø 外连接
外连接又分为左外连接、右外连接和全连接,三责存在什么区别呢?看完示例就明白了。
提示:由于在 scott 用户下,dept、emp、salgrade 这三张表不满测试外连接条件,但可以借助第四张表 bonus(工资表),但是该表中默认没有数据,所以需要先准备测试数据:
INSERT ALL
INTO bonus VALUES('SMITH', 'CLERK', 800.00, 200)
INTO bonus VALUES('WARD', 'SALESMAN', 1250.00, 500)
INTO bonus VALUES('JONES', 'MANAGER', 2975.00, 300)
INTO bonus VALUES('张三丰', '武当', 10000.00, 6000)
SELECT * FROM dual;
COMMIT;
说明:
1. bonus 表一共插入了4条数据,其中前三条可以跟 emp 表关联起来,最后一条(张三丰)是别的公司的员工,所以不再公司的员工表里;
2. emp 表与 bonus 表关联字段为 ename(员工姓名);
3. 有没有发现 bonus 的建表结构似乎不是很合理,为什么需要用 ename 和 job 去关联吗?不知道 Oracle 是买的什么关子!当然这个不是重点。
1) 左外连接
左外连接又称“左连接”,当根据指定的条件连接两张表时,始终显示左表的记录,右表没有连接上的记录,相关字段显示为 NULL。示例:
SELECT t1.empno, t1.ename, t1.job, t2.sal, t2.comm FROM emp t1
LEFT OUTER JOIN bonus t2 ON(t1.ename = t2.ename); --OUTER 关键字可以省略
从结果可以看到,左表显示了全部,右表没关联上的字段显示为 NULL 了。
另外,除了以上语法,左外连接还可以这样写,使用(+)代替:
SELECT t1.empno, t1.ename, t1.job, t2.sal, t2.comm FROM emp t1, bonus t2
WHERE t1.ename = t2.ename(+);
(+)表示该表未连接上时显示为 NULL,结果与前面的语法是一样的。
2) 右外连接
右外连接又称“右连接”,与左连接是相反的,始终显示右表的记录,左表没有连接上的记录,相关字段显示为 NULL。示例:
SELECT t1.empno, t1.ename, t1.job, t2.sal, t2.comm FROM emp t1
RIGHT OUTER JOIN bonus t2 ON(t1.ename = t2.ename); --OUTER 关键字可以省略
从结果可以看到,右表显示了全部,左表没关联上的字段显示为 NULL 了。
同样,右连接也可以使用(+)代替,例如:
SELECT t1.empno, t1.ename, t1.job, t2.sal, t2.comm FROM emp t1, bonus t2
WHERE t1.ename(+) = t2.ename;
与左连接相比,将(+)放在了左表的字段上。
3) 全连接
全连接是左外连接和右外连接结合版本,表示两张表都显示全部,未连接上的记录显示 NULL。示例:
SELECT t1.empno, t1.ename, t1.job, t2.sal, t2.comm FROM emp t1
FULL OUTER JOIN bonus t2 ON(t1.ename = t2.ename)
WHERE t1.ename IN('SMITH', 'WARD', 'JONES', 'BLAKE') OR t2.ename = '张三丰'; --OUTER 关键字可以省略
n 总结
Ø 内连接:只显示两张表连接上的记录;
Ø 左外连接:左表显示所有,右表没连接上的记录显示为 NULL,可以使用右表字段(+)的方式代替;
Ø 右外连接:右表显示所有,左表没连接上的记录显示为 NULL,可以使用左表字段(+)的方式代替;
Ø 全连接:两张表都显示所有,未连接上的记录各显示为 NULL。
Ø 更多 SELECT 语句的使用:Oracle 查询(SELECT)语句(二)