Ø 簡介
本文介紹 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 = '上海'; --查詢上海用戶的平均薪資
注意:
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)
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)語句(二)

![clip_image002[1] clip_image002[1]](/image/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvNjU0OTIwLzIwMjAwMS82NTQ5MjAtMjAyMDAxMTUxMDIxNDYxMzEtMTc0ODkxNjI4LmpwZw==.png)
![clip_image004[1] clip_image004[1]](/image/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvNjU0OTIwLzIwMjAwMS82NTQ5MjAtMjAyMDAxMTUxMDIxNDY2NDQtMTk0ODczODY2MS5qcGc=.png)
![clip_image005[1] clip_image005[1]](/image/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvNjU0OTIwLzIwMjAwMS82NTQ5MjAtMjAyMDAxMTUxMDIxNDcwNTgtMTQ5MjMxNTIxNy5wbmc=.png)
![clip_image006[1] clip_image006[1]](/image/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvNjU0OTIwLzIwMjAwMS82NTQ5MjAtMjAyMDAxMTUxMDIxNDc0NzEtNzgzMDk5MzAwLnBuZw==.png)
![clip_image007[1] clip_image007[1]](/image/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvNjU0OTIwLzIwMjAwMS82NTQ5MjAtMjAyMDAxMTUxMDIxNDc4NzEtNjI5NzAzNDUzLnBuZw==.png)
![clip_image009[1] clip_image009[1]](/image/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvNjU0OTIwLzIwMjAwMS82NTQ5MjAtMjAyMDAxMTUxMDIxNDgzMDItMTkxMjk4MjM2Ny5qcGc=.png)



![clip_image013[1] clip_image013[1]](/image/aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2Jsb2cvNjU0OTIwLzIwMjAwMS82NTQ5MjAtMjAyMDAxMTUxMDIxNDk5NTAtMTgyNTIxOTc0MC5wbmc=.png)