oracle 多表查詢語句

4.1、多表查詢的基本概念

        在之前所進行的查詢之中可以發現FROM子句之中只會存在有一張數據表,所以之前都只是針對於單表查詢操作,而所謂的多表查詢指的是同時從多張數據表之中取出數據實現的查詢,重點修改的是FROM子句,多表查詢的語法如下:

SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,...

FROM 數據表 [別名] , 數據表[別名] ,...

[WHERE 條件(s)]

[ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...]

        那麼下面先通過一個簡單的查詢來觀察一下多表查詢所帶來的問題。那麼首先來介紹一個“COUNT()”函數,此函數的主要功能是統計一張數據表之中所包含的數據量。

範例:統計emp表的數據量 —— 14條

SELECT COUNT(*) FROM emp ;

範例:統計dept表的數據量 —— 4條

SELECT COUNT(*) FROM dept ;

範例:將emp表和dept表實現多表查詢

SELECT * FROM emp ,dept ;

        此時的查詢符合於之前給出的多表查詢的語法要求。但是結果卻出現了56行的記錄,而這56行記錄之中發現每行的記錄都出現了重複,即:56行記錄= emp表的14行記錄 * dept表的4行記錄。

        之所以出現這樣的結果主要是由於數據庫的數學公式所導致的。

        

以上的結果實際上就屬於兩個集合的“積”,而這種積在數據庫之中被稱爲“笛卡爾積”。那麼現在該如何去消除掉這樣的“笛卡爾積”呢?唯一的方案就是使用關聯字段。

        此時應該採用的是emp表中的deptno字段與dept表中的deptno字段進行比較之後纔可以得出正確的顯示結果。

範例:解決笛卡爾積

SELECT *

FROM emp ,dept

WHERE emp.deptno=dept.deptno ;

        此時雖然消除了顯示的笛卡爾積,但是笛卡爾積依然是存在的,因爲這個是由數據庫本身的數學公式所決定的先天性問題。

        在進行多表查詢的時候一定會存在有關聯列,如果表之中存在有同名的列,那麼一定要使用“表名稱.字段”進行標記。可是如果表名稱過長“yuzhou_yinhexi_diqiu_yazhou_zhongguo_dalu_beijing”,那麼往往會爲其定義一個別名。

範例:使用別名

SELECT *

FROM emp e,dept d

WHERE e.deptno=d.deptno ;

範例:要求查詢出每個僱員的編號、姓名、職位、工資、部門名稱、部門位置

        · 確定要使用的數據表:

                  |- emp表:僱員的編號、姓名、職位、工資;

                  |- dept表:部門名稱、部門位置;

        · 確定已知的關聯條件:

                  |- 僱員和部門:emp.deptno = dept.deptno。

第一步:查詢出每個僱員的編號、姓名、職位、工資,只需要emp一張數據表即可

SELECT e.empno,e.ename,e.job,e.sal

FROM emp e ;

第二步:加入部門表,一旦加入了部門表,那麼就表示需要增加一個消除笛卡爾積的條件

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.losal AND salgrade.hisal;

第一步:查詢僱員數據

SELECT e.empno,e.ename,e.job,e.sal,e.hiredate

FROM emp e ;

第二步:加入工資等級表,此時使用的是一個範圍的查詢

SELECT e.empno,e.ename,e.job,e.sal,e.hiredate,s.grade

FROM emp e ,salgrade s

WHERE e.sal BETWEEN s.losal AND s.hisal ;

範例:查詢每個僱員的編號、姓名、職位、僱傭日期、工資、工資等級、所在部門名稱及位置

        · 確定要使用的數據表:

                  |- emp表:僱員的編號、姓名、職位、僱傭日期、工資;

                  |- salgrade表:工資等級;

                  |- dept表:部門名稱及位置。

        · 確定已知的關聯條件:只要是消除笛卡爾積的條件之間都使用AND連接

                  |- 僱員和工資等級:emp.sal BETWEEN salgrade.losal AND salgrade.hisal;

                  |- 僱員和部門:emp.deptno = dept.deptno。

第一步:查詢每個僱員的編號、姓名、職位、僱傭日期、工資

SELECT e.empno,e.ename,e.job,e.sal,e.hiredate

FROM emp e ;

第二步:增加工資等級的判斷

SELECT e.empno,e.ename,e.job,e.sal,e.hiredate,s.grade

FROM emp e ,salgrade s

WHERE e.sal BETWEEN s.losal AND s.hisal ;

第三步:增加部門表信息

SELECTe.empno,e.ename,e.job,e.sal,e.hiredate,s.grade,d.dname,d.loc

FROM emp e ,salgrade s,dept d

WHERE e.sal BETWEEN s.losal AND s.hisal

        AND e.deptno=d.deptno ;

        但是此時一定要注意, 表關聯的越多實際上產生笛卡爾積的數量也越多,就拿以上的查詢來講,實際上最終的數據量:emp表的14行 *dept表的4行 * salgrade表5行 = 280條數據(其中只有14行有用)。

說明:關於數據量龐大的兩個說明(以SH用戶登錄)

        · 說明一:在使用NOT IN查詢的時候裏面不能有NULL

如果說現在拿到一張數據表(選擇的costs表,這張表第一次接觸)。如果是一位菜鳥使用此表,往往會直接發出如下的一行指令:

SELECT * FROM costs ;

        於是此時就悲哀了,如果此表之中的數據量較大,那麼最輕的後果是你查看不方便,而嚴重的後果是死機(中國銀行的數據庫死機一秒種會有一羣人被幹掉的)。但是如果是有經驗的開發者,往往都先使用COUNT()函數確定一下表中的數據量。如果數據量較大,那麼使用特定的操作取出特定的幾行數據,如果數據量小,則隨便操作。

SELECT COUNT(*) FROM costs ;

        那麼回到NOT IN之中不能出現null的問題,如果NOTIN裏面有了null則表示不爲空,那麼有些字段的數據是永恆不能爲空的,那麼就表示查詢全部了。如果表中的數據量龐大了,那麼直接造成死機。

        · 說明二:以上的查詢只是消除了顯示中的笛卡爾積,但是笛卡爾積仍然存在,下面來驗證一下笛卡爾積影響。

SELECT COUNT(*) FROM costs;       è    82112條記錄

SELECT COUNT(*) FROM sales;       è    918843條記錄

SELECT COUNT(*)

FROM costs c,sales s

WHERE c.prod_id=s.prod_id;               è    75,448,036,416參與計算,保留1,165,337,550

        雖然最終的操作已經成功的消除掉了顯示的笛卡爾積,但是遺憾的是整個運算執行過程非常的緩慢,所以在實際的工作之中,尤其是在你進行數據表設計的時候應該儘可能的避免查詢有可能出現的笛卡爾積問題。

        結論:在數據量大的時候絕對不要採用多表查詢,而且就算是數據量小,也別用多表查詢。

最基礎的解決性能問題的方案(面試的時候別說,丟人):

例如:現在要求查詢出每個僱員的姓名、職位、工資、部門名稱

        這樣的查詢一定要使用emp和dept兩張表共同完成,但是如果假設emp表中有200W個僱員,而dept表中有5W個部門,那麼此時如果直接關聯產生的笛卡爾積相當的龐大,所以在這個時候最好的解決方案就是在設計初期,在emp表裏面重復保存一個部門名稱(重複的)。

4.2、連接方式

        對於多表查詢來講其有兩種表的連接方式:

                  · 內連接:也被稱爲等值連接,在之前的所有查詢都屬於內連接;

                  · 外連接:左外連接、右外連接、全外連接。

        那麼爲了更好的觀察出內連接與外連接區別,首先爲emp表中增加一行沒有部門的僱員。

範例:增加一個沒有部門的數據

INSERT INTO emp(empno,ename,job,hiredate,sal)

VALUES (7777,'劉小忙','CLERK',SYSDATE,701) ;

        此時新增加的數據所在的部門爲null。

範例:觀察內連接

SELECT *

FROM emp e,dept d

WHERE e.deptno=d.deptno ;

        因爲7777的部門編號爲null,所以過濾條件(WHERE e.deptno=d.deptno)不滿足。

        如果要想讓沒有部門的僱員或者是沒有僱員的部門數據顯示,則可以採用如下的方式實現外連接:

                  · 左外連接:字段 = 字段(+),“(+)”在等號右邊表示左外連接;

                  · 右外連接:字段(+) = 字段,“(+)”在等號左邊表示右外連接;

範例:觀察左外連接 —— 7777的僱員數據顯示了,對應的部門信息爲null

SELECT *

FROM emp e,dept d

WHERE e.deptno=d.deptno(+) ;

範例:觀察右外連接 —— 40部門顯示了,對應的僱員信息爲null

SELECT *

FROM emp e,dept d

WHERE e.deptno(+)=d.deptno ;

        一般而言,如果現在需要查詢的數據沒有出現,纔會使用到外連接,沒有必要去強制性的去分清楚到底是左還是右。

範例:查詢每個僱員的編號、姓名、職位、領導姓名

        實際上在emp表之中存在有一個mgr的字段,這個字段保存的是每一位僱員對應的領導編號。

        · 確定所需要的數據表:

                  |- emp表:僱員的編號、姓名、職位;

                  |- emp表:領導姓名;

        · 確定已知的關聯字段:

                  |- 僱員和領導:emp.mgr = memp.empno(僱員的領導編號 = 僱員編號)。

第一步:查詢emp表

SELECT e.empno,e.ename,e.job

FROM emp e ;

第二步:查詢出領導的姓名 è 內連接,等值連接,有null的不顯示

SELECT e.empno,e.ename,e.job,m.ename

FROM emp e ,emp m

WHERE e.mgr=m.empno;

第三步:加入外連接控制,讓所有的僱員數據顯示

SELECT e.empno,e.ename,e.job,m.ename

FROM emp e ,emp m

WHERE e.mgr=m.empno(+);

        在實際的工作之中外連接在多表查詢的操作裏面概念非常的重要,而且以上的表屬於自身關聯查詢。

4.3、SQL:1999語法支持

        SQL語法標準實際上一直在進行更新,從1999年之後對於數據表的關聯查詢給了一個標準的操作語法(因爲“(+)”符號只有Oracle可以使,那麼其它的數據庫不支持此類符號,只能夠使用SQL:1999語法完成),此類語法定義如下:

SELECT * | 字段 [別名]

FROM 表 [CROSS JOIN 表] [NATURAL JOIN 表]

[JOIN] [USING(字段)] [ON(條件)]

[LEFT | RIGTH | FULL | OUTER JOIN 表] ;

        以上給出的是一個完整語法,實際上這些語法都可以進行細分。

1、   交叉連接,其主要的目的是爲了產生笛卡爾積

語法:

SELECT * | 字段 [別名] FROM 表 CROSS JOIN 表 ;

範例:

SELECT * FROM emp CROSS JOIN dept ;

2、   自然連接,主要是消除掉笛卡爾積(內連接)

語法:

SELECT * | 字段 [別名] FROM 表 NATURAL JOIN 表 ;

範例:

SELECT * FROM emp NATURAL JOIN dept ;

3、   使用USING子句,用戶指定關聯字段

語法:

SELECT * | 字段 [別名] FROM 表JOIN 表 USING(字段);

範例:

SELECT * FROM emp JOIN dept USING(deptno) ;

4、   使用ON子句指定關聯條件

語法:

SELECT * | 字段 [別名] FROM 表JOIN 表 ON(條件);

範例:

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

5、   外連接

語法:

SELECT * | 字段 [別名] FROM 表 LEFT | RIGTH | FULL OUTER JOIN 表];

範例:

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

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

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

        如果要想使用全外連接只能夠利用以上的語法完成。

4.4、查詢結果連接

        在數學之中存在交集、並集、差集的概念,那麼此概念在SQL語句之中也同樣存在。在SQL語法之中提供了:UNION、UNIONALL、INTERSECT、MINUS幾個符號實現結合操作,這幾個符合可以連接多個SQL,使用的基本語法如下:

SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,...

FROM 數據表 [別名] , 數據表 [別名] ,...

[WHERE 條件(s)]

[ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...]

        [UNION | UNION ALL | INTERSECT | MINUS]

SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,...

FROM 數據表 [別名] , 數據表 [別名] ,...

[WHERE 條件(s)]

[ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...]

        [UNION | UNION ALL | INTERSECT | MINUS]

SELECT [DISTINCT] * | 列名稱 [別名] , 列名稱 [別名] ,...

FROM 數據表 [別名] , 數據表 [別名] ,...

[WHERE 條件(s)]

[ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...]

        ....

範例:驗證UNION操作,不顯示重複記錄

SELECT * FROM emp WHERE deptno=10

        UNION

SELECT * FROM emp ;

範例:驗證UNION ALL操作,顯示所有數據,包含重複數據

SELECT * FROM emp WHERE deptno=10

        UNION ALL

SELECT * FROM emp ;

範例:驗證INTERSECT操作,返回相同的部分,交集

SELECT * FROM emp WHERE deptno=10

        INTERSECT

SELECT * FROM emp ;

範例:驗證MINUS操作,返回不同的部分,是一個差集

SELECT * FROM emp

        MINUS

SELECT * FROM emp WHERE deptno=10 ;

        在工作之中有部分的查詢會利用以上的方式以簡化查詢的複雜度。

轉載自 weico 作者:MLDN李興華  地址:http://weibo.com/p/1035052004834713/wenzhang?cfs=600&Pl_Core_ArticleList__66_filter=&Pl_Core_ArticleList__66_page=11#Pl_Core_ArticleList__66

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章