1. 程式人生 > >Oracle資料庫之七 多表查詢

Oracle資料庫之七 多表查詢

七、多表查詢

​ 對於查詢在之前已經學過了簡單查詢、限定查詢、查詢排序,這些都屬於 SQL 的標準語句,而上一章的單行函式,主要功能是為了彌補查詢的不足。

​ 而從多表查詢開始就正式進入到了複雜查詢部分。

7.1、基本語法

  • 多表查詢就是在一條查詢語句中,從多張表裡一起取出所需要的資料。如果要想進行多表查詢,直接在 FROM 子句之後跟上多個表即可,語法如下:

    SELECT [DISTINCT] *|列名稱 [AS][列別名],列名稱 [AS][列別名],...
    FROM 表名稱1[表別名1],表名稱2[表別名2],...
    [WHERE 條件(s)]
    [ORDER BY 排序的欄位1 ASC|DESC,排序的欄位2 ASC|DESC,...];
  • 下面就將採用 emp 表和 dept 表一起進行多表查詢,查詢之前多做一個步驟,先確定 emp 和 dept 表中的資料量分別有多少,可以使用 COUNT() 函式統計

範例:統計 emp 表中的資料量 (14 行記錄)

SELECT COUNT(*) 
FROM emp;

範例:統計 dept 表中的資料量 (4行記錄)

SELECT COUNT(*) 
FROM dept;

範例:現在查詢所有的僱員和部門的全部詳細資訊

SELECT COUNT(*) 
FROM emp,dept;
  • 發現此時結果一共返回了56行記錄,這就是笛卡爾積 造成的問題。現在就發現了,笛卡爾積的出現,可以讓查詢結果變得非常的龐大,如果現在兩張表的資料量都很大,那麼這種龐大是很可怕的,所以現在必須想辦法消除掉笛卡爾積。
  • 一般而言,如果要想進行笛卡爾積的消除,往往會使用關聯欄位,由於多張表之間可能會存在重名的欄位,所以進行重名欄位的訪問的時候看,前面需要加上表名稱,採用 “ 表名稱 欄位 ” 的方式來進行訪問。

範例:利用等值條件來處理笛卡爾積

SELECT * 
FROM emp,dept
WHERE emp.deptno = dept.deptno;
  • 這時的結果就可以發現已經消除掉笛卡爾積,但是這時積依然存在,只是不顯示了而已
  • 已經清楚基本概念後,下面就可以針對於資料量做一個分析。在 Oracle 中存在了一個 sh 使用者,當然此使用者儲存在了 pdbmldn 插入式資料庫之中了
    • 1、開啟 sqlplus :執行 - 輸入 sqlplus /nolog
    • 2、管理員連線資料庫:conn sys/change_on_install AS SYSDBA;
    • 3、切換到 pdbmldn 資料庫:ALTER SESSION SET CONTAINER=pdbmldn;
    • 4、開啟 pdbmldn 資料庫:ALTER DATABASE pdbmldn OPEN
    • 馬上就要使用 sh 使用者進行操作,現在可以使用 sh 使用者下的 sales 和 costs 表,這兩張表的數量都比較大。

範例:檢視 sh.sales 表的資料量 (918843條記錄)

SELECT COUNT(*)
FROM sh.sales;

範例:檢視 sh.costs 表的資料量 (82112條記錄)

SELECT COUNT(*)
FROM sh.costs;

範例:如果現在直接將這兩張表進行多表查詢,那麼來觀察問題

SELECT COUNT(*)
FROM sh.sales,sh.costs;
  • 一旦開始執行之後,那麼這個等待的過程會很長,但是這時是顯示所有資料量(包含笛卡爾積的資料量),而後再開始消除,利用等值關聯。最終會返回的資料量:75,448,036,416
  • 雖然消除掉了所有顯示的笛卡爾積,但是資料庫的原理機制及表示笛卡爾積會永遠存在
  • 多表查詢會產生笛卡爾積,所以效能較差,可以利用等值關聯欄位消除笛卡爾積

7.2、多表查詢例項

  • 雖然多表查詢本身存在了效能問題,但並不表示多表查詢無法使用,需要一些更合理的做法來解決多表查詢問題

範例:查詢每個僱員的編號、姓名、職位、基本工資、部門名稱、部門位置資訊

  • 確定所需要的資料表
    • emp 表:查詢每個僱員的編號、姓名、職位、基本工資;
    • dept 表:部門名稱,部門位置。
  • 確定已知的關聯欄位
    • 部門與僱員關聯:emp.deptno = dept.deptno
  • 隨後還需要按照一個 SQL 語句的執行步驟編寫:FROM , WHERE , SELECT 。
SELECT emp.empno,emp.ename ,emp.job,emp.sal,dept.dname,dept.loc
FROM emp,dept
WHERE emp.deptno = dept.deptno
  • 但是在此處就會存在一個問題,上面程式裡都是採用了表名稱訪問的列名稱,如果現在表名稱很長:yuzhou_yinhexi_diqiu_yazhou_zhongguo_beijing 。所以往往在多表查詢的時候為查詢的資料定義別名,而別名也是在 FROM 子句之中定義的,上面的程式可以改寫為:
SELECT e.empno, e.ename, e.job, e.sal, d.dname, d.loc
FROM emp e, dept d
WHERE e.deptno=d.deptno;
  • 在以後的程式編寫之中幾乎都會使用到別名

範例:查詢出每個僱員的編號、姓名、僱傭日期、基本工資、工資等級

  • 確定所需要的資料表
    • emp 表:查詢每個僱員的編號、姓名、僱傭日期、基本工資;
    • salgrade 表:工資等級。
  • 確定已知的關聯欄位
    • 僱員和工資等級:emp.sal BETWEEN salgrade.local AND salgrade.hisal ;
SELECT e.empno, e.ename, e.hiredate, e.sal, s.grade
FROM emp e, salgrade s 
WHERE e.sal BETWEEN s.losal AND s.hisal;
  • 在之前的查詢裡面,發現只是顯示了數字1,2,3,4,5,現在希望可以將其替換為中文,如果要替換,肯定使用 DECODE() 函式。

範例:為了更加清楚的顯示出工資等級的資訊,現在希望可以按如下格式進行替換顯示:

​ grade = 1 : 顯示為 “ E等工資 ”

​ grade = 2 : 顯示為 “ D等工資 ”

​ grade = 3 : 顯示為 “ C等工資 ”

​ grade = 4 : 顯示為 “ B等工資 ”

​ grade = 5 : 顯示為 “ A等工資 ”

SELECT e.empno, e.ename, e.hiredate, e.sal,
    DECODE(s.grade,1,'E等工資',2,'D等工資',3,'C等工資',4,'B等工資',5,'A等工資') grade
FROM emp e, salgrade s 
WHERE e.sal BETWEEN s.losal AND s.hisal;
  • 前面講的都是針對於兩張表進行多表查詢,而且多表查詢裡面都只使用了一個條件來消除笛卡爾積;如果現在是多個消除笛卡爾積的條件,那麼往往使用 AND 將這些條件連線在一起

範例:查詢出每個僱員的姓名、職位、基本工資、部門名稱、工資等級

  • 確定所需要的資料表
    • emp 表:每個僱員的姓名、職位、基本工資;
    • dept 表:部門名稱
    • salgrade 表:工資等級。
  • 確定已知的關聯欄位
    • 僱員和部門:emp.deptno = dept.deptno
    • 僱員和工資等級:emp.sal BETWEEN salgrade.local AND salgrade.hisal ;
SELECT e.ename, e.job, e.sal, d.dname,
    DECODE(s.grade,1,'E等工資',2,'D等工資',3,'C等工資',4,'B等工資',5,'A等工資') grade
FROM emp e, dept d, salgrade s 
WHERE e.deptno = d.deptno AND e.sal BETWEEN s.losal AND s.hisal;
  • 多表查詢之中,每當增加一個關聯表都需要設定消除笛卡爾積的條件

7.3、表的連線操作

  • 對於資料表的連線操作在資料庫中一共定義了兩種:
    • 內連線:也稱為等值連線(或稱為連線,還可以被稱為普通連線或者自然連線),是最早的一種連線方式,內連線是從結果表中刪除與其他被連線表中沒有匹配行的所有元組,所以當匹配條件不滿足時內連線可能會丟失資訊。在之前所使用的連線方式都屬於內連線,而在 WHERE 子句之中設定的消除笛卡爾積的條件就是採用了等值判斷的方式進行的。
    • 外連線:內連線中只能夠顯示等值滿足的條件,如果不滿足的條件則無法顯示,如果現在希望特定表中的資料可以全部顯示,就利用外連線,外連線分為三種:左外連線(簡稱:左連線)、右外連線(簡稱:右連線)、全外連線(簡稱:全連線,在 SQL :1999 語法部分講解)
  • 在之前所編寫的表連線操作都屬於內連線的定義範疇
  • 為了更好的觀察各個連線方式的區別,首先需要在 emp 表中增加一條資料,這個增加語法會在後面解釋
INSERT INTO emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) 
    VALUES(8888,'李興華','CLERK',7369,SYSDATE,800,100,null);
  • 這時增加完的資料是一個沒有部門的僱員,即此僱員的部門編號是 null ,增加完成之後檢視一下 emp 表中當前的全部資料
SELECT * FROM emp;
  • 此處沒有部門編號,下面就演示等值連線所帶來的效果

範例:使用等值連線

SELECT *
FROM emp e, dept d
WHERE e.deptno = d.deptno;
  • 通過此時的結果可以發現兩個問題:
    • 問題一:沒有部門的僱員沒有顯示
    • 問題二:有一個40部門沒有顯示
  • 所以現在就可以發現,使用內連線只有滿足連線條件的資料才會全部顯示。可是如果說現在希望 emp 或是 dept 表中的資料顯示完整,就可以利用外連線進行。
  • 外連線現在主要使用兩種:
    • 左外連線:左關係屬性 = 右關係屬性(+) ,+ 放在了等號的右邊,表示左連線;
    • 右外連線:左關係屬性(+) = 右關係屬性 ,+ 放在了等號的左邊,表示右連線。

範例:使用左外連線,顯示僱員編號是8888的資訊

SELECT *
FROM emp e, dept d 
WHERE e.deptno = d.deptno(+);

範例:使用右外連線,顯示部門編號為40的資訊

SELECT *
FROM emp e, dept d 
WHERE e.deptno(+) = d.deptno;
  • 上面結果中因為40部門沒有僱員,所以所有的僱員資訊都為 null 。
  • 個人總結:使用外連線的環境,如果所需要的資料資訊沒有顯示出來,那麼就使用外連線,而具體是左外還是右外,個人認為沒必要去記,去試就行!

7.4、自身關聯

  • 在 emp 表中存在有一個 mgr 欄位,這個欄位表示的是僱員的領導
SELECT * FROM emp;
  • 現在如果要顯示僱員的領導資訊,那麼肯定利用僱員表和僱員表自己的連線操作完成。利用僱員表中的領導編號,找到對應此編號的僱員資訊。

範例:查詢出每個僱員的編號、姓名及其上級領導的編號、姓名

  • 確定所需要的資料表
    • emp 表:僱員的編號、姓名
    • emp 表:找到領導的編號、姓名
  • 確定已知的關聯欄位
    • 僱員和領導:emp.mgr = memp.empno (僱員的領導編號 = 領導的資訊)
  • 步驟一:直接進行自身連線的操作
SELECT e.empno eno, e.ename ename, m.empno mno, m.ename mname
FROM emp e, emp m 
WHERE e.mgr = m.empno;

​ 現在表中一共有15條記錄,但是隻有14條的記錄顯示,等值連線在沒有條件滿足的時候,是不可能有資料顯示的。

​ 在 emp 表中 king 這個僱員是沒有領導的,這個時候就必須考慮外連線。

  • 步驟二:使用左外連線
SELECT e.empno eno, e.ename ename, m.empno mno, m.ename mname
FROM emp e, emp m 
WHERE e.mgr = m.empno(+);

​ 對於沒有領導資訊的僱員,對應的領導資訊,全部使用 null 進行表示

範例:查詢出在1981年僱員的全部僱員的編號、姓名、僱傭日期(按照年-月-日顯示)、工作、領導姓名、僱員月工資、僱員年工資(基本工資+獎金)、僱員工資等級、部門編號、部門名稱、部門位置、並且要求這些僱員的月基本工資在1500~3500之間,將最後的結果按照年工資的降序排列,如果年工資相等,則按工作進行排序

  • 確定所需要的資料表
    • emp 表:編號、姓名、僱傭日期、工作、月工資、計算年工資;
    • emp 表:領導姓名;
    • dept 表:部門編號、名稱、位置;
    • salgrade 表:工資等級。
  • 確定已知的關聯欄位
    • 僱員和領導:emp.mgr = memp.empno;
    • 僱員和部門:emp.deptno = dept.deptno;
    • 僱員和工資等級:emp.sal BETWEEN salgrade.losal AND salgrade.hisal .
  • 步驟一:查詢出所有在1981年僱傭的僱員編號、姓名、僱傭日期、工作、月工資、年工資、並且月薪在1500~3500之間。只需要 emp 單張表即可實現。
SELECT e.empno, e.ename, e.hiredate, e.sal, (e.sal+NVL(e.comm,0))*12 income
FROM emp e 
WHERE TO_CHAR(e.hiredate,'yyyy') = '1981' AND e.sal BETWEEN 1500 AND 3500;
  • 步驟二:加入領導資訊,使用自身關聯
SELECT e.empno, e.ename, e.hiredate, e.job, e.sal, (e.sal+NVL(e.comm,0))*12 income,
    m.ename mname
FROM emp e, emp m 
WHERE TO_CHAR(e.hiredate,'yyyy') = '1981' AND e.sal BETWEEN 1500 AND 3500
    AND e.mgr = m.empno(+);
  • 步驟三:加入部門資訊
SELECT e.empno, e.ename, e.hiredate, e.job, e.sal, (e.sal+NVL(e.comm,0))*12 income,
    m.ename mname, d.deptno dno, d.dname dname, d.loc 
FROM emp e, emp m, dept d  
WHERE TO_CHAR(e.hiredate,'yyyy') = '1981' AND e.sal BETWEEN 1500 AND 3500
    AND e.mgr = m.empno(+)
    AND e.deptno = d.deptno;
  • 步驟四:查詢出工資等級
SELECT e.empno, e.ename, e.hiredate, e.job, e.sal, (e.sal+NVL(e.comm,0))*12 income,
    m.ename mname, d.deptno dno, d.dname dname, d.loc, s.grade
FROM emp e, emp m, dept d,salgrade s   
WHERE TO_CHAR(e.hiredate,'yyyy') = '1981' AND e.sal BETWEEN 1500 AND 3500
    AND e.mgr = m.empno(+)
    AND e.deptno = d.deptno
    AND e.sal BETWEEN s.losal AND s.hisal;
SELECT e.empno, e.ename, e.hiredate, e.job, e.sal, (e.sal+NVL(e.comm,0))*12 income,
    m.ename mname, d.deptno dno, d.dname dname, d.loc, s.grade,
    DECODE(s.grade,1,'E等工資',2,'D等工資',3,'C等工資',4,'B等工資',5,'A等工資') 工資等級
FROM emp e, emp m, dept d,salgrade s   
WHERE TO_CHAR(e.hiredate,'yyyy') = '1981' AND e.sal BETWEEN 1500 AND 3500
    AND e.mgr = m.empno(+)
    AND e.deptno = d.deptno
    AND e.sal BETWEEN s.losal AND s.hisal;
  • 步驟五:進行排序,由於 SELECT 是在 ORDER BY 子句之前執行,所以在 SELECT 子句之中所定義的別名,ORDER BY 子句是可以直接使用的。
SELECT e.empno, e.ename, e.hiredate, e.job, e.sal, (e.sal+NVL(e.comm,0))*12 income,
    m.ename mname, d.deptno dno, d.dname dname, d.loc, s.grade,
    DECODE(s.grade,1,'E等工資',2,'D等工資',3,'C等工資',4,'B等工資',5,'A等工資') 工資等級
FROM emp e, emp m, dept d,salgrade s   
WHERE TO_CHAR(e.hiredate,'yyyy') = '1981' AND e.sal BETWEEN 1500 AND 3500
    AND e.mgr = m.empno(+)
    AND e.deptno = d.deptno
    AND e.sal BETWEEN s.losal AND s.hisal;
ORDER BY income DESC, e.job;
  • 通過這一稍微複雜點的題目,可以發現,所有的分析必須要分步進行,而且這些分析過程,需要大量的練習才可以鞏固。
  • 自身關聯屬於一張表自己關聯自己的情況,此時依然會產生笛卡爾積。

7.5、SQL:1999 語法的支援

  • 在之前使用的 “ (+) ” 標記只適合在 Oracle 資料庫之中應用,如果是其他的資料庫,是無法使用的
  • SQL:1999 語法
SELECT [DISTINCT] *|列名稱 [AS][列別名],列名稱 [AS][列別名],...
FROM 表1 表別名1 [CROSS JOIN 表2 表別名2]|
[NATURAL JOIN 表2 表別名2]|
[JOIN 表2 USING(關聯列名稱)]|
[JOIN 表2 ON(關聯條件)]|
[LEFT|RIGHT|FULL OUTER JOIN 表2 ON(關聯條件)]
[WHERE 條件(s)]
[ORDER BY 排序的欄位1 ASC|DESC, 排序的欄位2 ASC|DESC,...];
  • 實際上以上給出的是綜合語法,下面將拆分進行講解

7.5.1 交叉連線

  • 交叉連線(CROSS JOIN)作用於兩個關係上,並且第一個關係的每個元組與第二個關係的所有元組進行連線,這樣的操作形式與笛卡爾積是完全相同的,交叉連線的語法如下所示:
SELECT [DISTINCT] *|列名稱 [AS][列別名],列名稱 [AS][列別名],...
FROM 表1 表別名1 [CROSS JOIN 表2 表別名2]|
[WHERE 條件(s)]
[ORDER BY 排序的欄位1 ASC|DESC, 排序的欄位2 ASC|DESC,...];
  • 交叉連線的主要功能就是產生笛卡爾積

範例:使用交叉連線查詢資訊

SELECT * 
FROM emp CROSS JOIN dept;
  • 一般而言,在進行多表連線的時候都一定會存在關聯欄位以消除笛卡爾積,而關聯欄位的名稱一般都會一樣,如果不一樣,也會有部分相同,現在討論的是一樣的情況,例如 deptno 欄位這就表示一樣,就可以利用自然連線來消除掉笛卡爾積

7.5.2 自然連線

  • 自然連線(NATURAL JOIN)運算作用於兩個關係,最終會通過兩個關係產生出一個關係作為結果。與交叉連線(笛卡爾積)不同的是,自然連線只考慮那些在兩個關係模式中都出現的屬性上取值相同的元組對。自然連線的操作語法如下:
SELECT [DISTINCT] *|列名稱 [AS][列別名],列名稱 [AS][列別名],...
FROM 表1 表別名1 [NATURAL JOIN 表2 表別名2]|
[WHERE 條件(s)]
[ORDER BY 排序的欄位1 ASC|DESC, 排序的欄位2 ASC|DESC,...];

範例:使用自然連線查詢資訊

SELECT * 
FROM emp NATURAL JOIN dept;
  • 這個時候會把連線的欄位放在第一列上進行顯示,而且這種方式就是一種內連線的方式。

7.5.3 USING 子句

  • 通過自然連線可以直接使用關聯欄位消除掉笛卡爾積,那麼如果現在的兩張表中沒有存在這種關聯欄位的話,就可以通過 USING 子句完成笛卡爾積的消除,USING 子句的語法如下:
SELECT [DISTINCT] *|列名稱 [AS][列別名],列名稱 [AS][列別名],...
FROM 表1 表別名1 [JOIN 表2 USING(關聯列名稱)]|
[WHERE 條件(s)]
[ORDER BY 排序的欄位1 ASC|DESC, 排序的欄位2 ASC|DESC,...];

範例:使用 USING 子句查詢資訊

SELECT * 
FROM emp JOIN dept USING(deptno);

7.5.4 ON 子句

  • 在之前編寫等值連線時,採用了關聯欄位進行笛卡爾積的消除,那麼使用者在 SQL:1999 語法之中通過 ON 子句就可以由使用者手工設定一個關聯條件,ON 子句語法如下:
SELECT [DISTINCT] *|列名稱 [AS][列別名],列名稱 [AS][列別名],...
FROM 表1 表別名1 [JOIN 表2 ON(關聯條件)]|
[WHERE 條件(s)]
[ORDER BY 排序的欄位1 ASC|DESC, 排序的欄位2 ASC|DESC,...];
  • USING 是設定連線欄位,而 ON 是設定連線條件

範例:使用 ON 子句查詢資訊

SELECT * 
FROM emp e JOIN salgrade s ON(e.sal BETWEEN s.losal AND s.hisal);

7.5.5 外連線

  • 在資料的查詢操作中資料的外連線一共分為三種形式:左外連線、右外連線、全外連線,而此時連線的語法如下:
SELECT [DISTINCT] *|列名稱 [AS][列別名],列名稱 [AS][列別名],...
FROM 表1 表別名1 [LEFT|RIGHT|FULL OUTER JOIN 表2 ON(關聯條件)]
[WHERE 條件(s)]
[ORDER BY 排序的欄位1 ASC|DESC, 排序的欄位2 ASC|DESC,...];
  • 對於外連線,在之前使用的是 “ (+) ”,這個標記只能夠實現左外或者右外連線,但是對於全外連線無法使用,而全外連線只能夠依靠 SQL:1999 語法之中規定的內容。

範例:實現右外連線

SELECT * 
FROM emp e RIGHT OUTER JOIN dept d ON (e.deptno = d.deptno);

範例: 實現左外連線

SELECT * 
FROM emp e LEFT OUTER JOIN dept d ON (e.deptno = d.deptno);

範例:實現全外連線

SELECT * 
FROM emp e FULL OUTER JOIN dept d ON (e.deptno = d.deptno);
  • 通過以上的分析可以發現,給出的所有語法裡面,只有全外連線只能夠通過 SQL:1999 語法實現,但是對於這種全外連線,使用的情況並不多。
  • 而且個人建議,如果使用的是 Oracle 資料庫,就使用 “ (+) ” 標記控制左右連線,不使用它實現內連線。

7.6、資料的集合運算

  • 資料的集合操作指的是查詢結果的操作
  • 集合運算是一種二目運算子,一共包括四種運算子:並、差、交、笛卡爾積,其中對於笛卡爾積在之前已經演示過了,所以本次主要是看 並、交、差 三種操作。操作集合的語法如下
查詢語句
    [UNION | UNION ALL | INTERSECT | MINUS]
查詢語句
    ...
  • 要實現集合的運算,主要使用四種運算子:
    • UNION (並集):返回若干個查詢結果的全部內容,但是重複元組不顯示;
    • UNION ALL (並集):返回若干個查詢結果的全部內容,重複元組也會顯示;
    • MINUS (差集):返回若干個查詢結果中的不同部分
    • INTERSECT (交集):返回若干個查詢結果中的相同部分

7.6.1 並集操作

  • 並集操作是將多個查詢的結果連線到一起,而對於並操作提供了兩種操作符:UNION(不顯示重複),UNION ALL(顯示重複)

範例:並集操作:UNION , UNION ALL

  • 第一個查詢
SELECT * FROM dept;
  • 第二個查詢
SELECT * FROM dept WHERE deptno = 10;

​ 這個時候兩個查詢結果返回的列的結構相同。

  • 使用 UNION
SELECT * FROM dept
    UNION
SELECT * FROM dept WHERE deptno = 10;

​ 第一個查詢已經包含了第二個查詢的內容,所以重複資料不顯示了。

範例:使用 UNION ALL 顯示全部

SELECT * FROM dept
    UNION ALL
SELECT * FROM dept WHERE deptno = 10;
  • 提示:在以後進行查詢操作編寫過程中,建議儘量使用UNION 或 UNION ALL 來代替 OR

範例:查詢所有銷售人員和辦事人員的資訊

  • 實現一:
SELECT * FROM emp WHERE job = 'SALESMAN' OR job = 'CLERK';
  • 實現二:
SELECT * FROM emp WHERE job IN ('SALESMAN', 'CLERK');
  • 實現三:
SELECT * FROM emp WHERE job = 'SALESMAN'
    UNION
SELECT * FROM emp WHERE job = 'CLERK'

7.6.2 差集操作

範例:使用 MINUS 執行差集操作

SELECT * FROM dept
    MINUS
SELECT * FROM dept WHERE deptno = 10;
  • 結果就顯示3行,deptno=10 的不顯示了

7.6.3 交集操作

範例:使用 INTERSECT 執行交集操作

SELECT * FROM dept
    INTERSECT
SELECT * FROM dept WHERE deptno = 10;
  • 結果只顯示 deptno=10 一行了
  • 集合操作時,各個查詢語句返回的結構要求一致。

說明:本學習資料是根據李興華的Oracle開發實戰經典整理