問題 :
1.什么是內連接(inner)和外聯結(outer)
2. SQL server 表連接 (FROM--AND 法, JOIN -- ON 法)的區別.
3.表連接及多表連接的SQL語句執行順序,和性能調優.
1.第一個問題,首先要明白如何使用JOIN 和 ON 關鍵字作表連接。
申明:下文中所用的等價,可能指的是邏輯上的等價(即產生相同的結果集),也可能是執行順序上的等價,甚至是所產生的執行計划或者執行效率等價。因為很多時候用戶只要寫普通的sql ,而sql server 會跟據自己的優化 配置和執行計划,產生執行步驟,這些步驟也許和你寫的sql很符合,也許更優,當然也可能不符合你的需求。這需要很多的積累,我也只是淺嘗輒止,所以沒有能力做過多論述。具體問題具體分析,這里只能提供大體思路。
1)join 的5種方式 :
inner join ; left join; right join; full join; cross join;
其中inner join可以省去inner 關鍵字。 left/right join 與left/right out join 等價。
full join 與 同時 left join 和 right join 等價。
cross join 為將兩張表笛卡爾集
2) JOIN -- ON 語句的執行順序:
例句:
SELECT * FROM A LEFT JOIN B ON A.ID = B.ID AND A<>0 WHERE A.name = 'x'
注意在作on 連接后 的and 子句 和where 子句 。 他們有什么不同!。。。。。。
邏輯上解釋:(不考慮執行計划中執行步驟和作嵌套連接等具體方式,這里只討論如何思考邏輯上的步驟)
執行順序是: FROM --> JOIN --> ON -->AND--> LEFT--> WHERE -->SELECT
A步驟. 先將兩張表根據ON 條件 作連接(邏輯上,相等於將兩張表笛卡爾集后根據ID相等條件篩選數據,實際情況后面分析)
B步驟. 根據ON 后面,WHERE 之前 的 AND 條件篩選數據
C步驟. 跟據LEFT 無論如何,要保證A表的數據完整性。所以在上一步驟產生的結果集中補齊A表因無法比與B表匹配而被AND 條件篩選的掉的數據;
D步驟. 再根據WHERE篩選結果集。
示例:(為了能更好的這一過程,通過實例先思考)
CREATE TABLE EMPLOY (NAME VARCHAR(10), DEPTNO INTEGER ); INSERT INTO EMPLOY (NAME, DEPTNO) VALUES ('張三',10), ('李四',20), ('王五',10), ('趙紅',20); CREATE TABLE DEPARTMENT (DEPTNO INTEGER, DEPTNAME VARCHAR(10) ); INSERT INTO DEPARTMENT (DEPTNO, DEPTNAME) VALUES (10, '市場部'), (20, '技術部'); --查詢一下所有的員工的姓名和部門名為市場部的部門
也許你的SQL 會寫成這樣:
SELECT E.NAME,D.DEPTNAME FROM EMPLOY E LEFT JOIN DEPARTMENT D ON E.DEPTNO=D.DEPTNO WHERE D.DEPTNAME='市場部'
仔細讀題目,是要查詢“所有”員工的姓名,所以肯定要保證員工表的數據完整性。如果使用where,當然不能保證員工表的完整拉。
還記得 在 ON 關鍵字后 ,WHERE 關鍵字前的條件篩選方式么???
SELECT E.NAME,D.DEPTNAME FROM EMPLOY E LEFT JOIN DEPARTMENT D ON E.DEPTNO=D.DEPTNO AND D.DEPTNAME='市場部'
這樣就對了!!
產生的結果很奇怪
張三 市場部
李四 NULL
王五 市場部
趙紅 NULL
為什么結果是這樣呢? 深入理解下前面所說的SQL 語句執行順序
舉個例子:分別執行看看結果,結合上個例子想想(以下ABC步驟意思是前面說的ABCD四個步驟)
--執行A步驟等價的邏輯SQL SELECT * FROM EMPLOY E JOIN DEPARTMENT D ON E.DEPTNO=D.DEPTNO --執行B步驟等價的邏輯SQL SELECT * FROM EMPLOY E JOIN DEPARTMENT D ON E.DEPTNO=D.DEPTNO and D.DEPTNO=40 --執行C步驟等價的邏輯SQL SELECT * FROM EMPLOY E left JOIN DEPARTMENT D ON E.DEPTNO=D.DEPTNO and D.DEPTNO=40
3)表連接在執行計划,或者是真正的執行方式:
首先要理解下執行計划,看看SQL語句如何在 SQL SERVER 內部中真正實現這些復雜操作;其中SQL SERVER JOIN 的三種方式(Nested Loops join,Merge Join,Hash Join)要有所了解。如果不懂,可以去http://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html 看看學習。
我的私人理解:
A. Nested Loops join :外表縣進行逐條掃描,而內表,根據ON的連接條件,快速SEEK內表看是否有符合的數據(SEEK不是SCAN)。這樣產生兩張表JOIN后集合。
B. Merge Join : 用於兩張表差不多大,而且在連接字段上有索引。
C. Hash Join : 兩張表都是數據量很大。
雖然不是太明白具體如何判斷,但是SQL SERVER 會自動判斷使用哪種方式,所以不需要太關心,除非是做DBA的。重點了解下Nested Loops join。
4)FROM , JOIN , ON , AND , WHERE 總結
鋪墊了這么多,終於回到關鍵問題: 平時看到很多SQL 寫法 有的用WHERE and 進行表連接,有的用JOIN ON 作表連接。這里面不能隨便,寫不好即影響結果,又阻礙執行效率。可以查看更多詳細資料:http://blog.csdn.net/shangboerds/article/details/5213264
SELECT E.NAME,D.DEPTNAME FROM EMPLOY E LEFT JOIN DEPARTMENT D ON E.DEPTNO=D.DEPTNO WHERE D.DEPTNAME='市場部' SELECT E.NAME,D.DEPTNAME FROM EMPLOY E LEFT JOIN DEPARTMENT D ON E.DEPTNO=D.DEPTNO AND D.DEPTNAME='市場部' -- 不論邏輯上還是結果上都不等價
現實SQL查詢中,一般都不止兩個表連接,一般是多表連接查詢! 幾個常見錯誤:
1。胡亂使用LEFT join :由於分析過執行步驟, LEFT 關鍵字是要在“兩張”表連接完成后(思考下多表連接),再對表相當於進行掃描部全的過程,所以會耗費很多時間。
2。分不清表連接 (FROM--AND 法, JOIN -- ON 法)的區別;如下兩個SQL:
SELECT * FROM A INNER JOIN B ON A.ID = B.ID AND B<>0 INNER JOIN C ON A.ID = B.ID AND C<>0 SELECT * FROM A INNER JOIN B ON A.ID = B.ID INNER JOIN C ON A.ID = B.ID WHERE B<>0 AND C<>0 --此寫法效率比上面兩種都差,尤其表越多,效果越明顯
思考表連接的的SQL執行順序。。。前者兩張表JOIN 后 馬上篩選部分結果在與另一張表JOIN 。后者先將三張表JOIN后再篩選。所以很明顯前者效率比后者高.
3。再添加一條SQL :
SELECT * FROM A , B , C WHERE A.ID = B.ID AND A.ID = C.ID AND B<>0 AND C<>0
此結果和第一條SQL一樣效率不錯! 從邏輯上看,似乎SQL 會先將表JOIN 后再篩選,但實戰結果。是先篩選再JOIN !因為SQL SERVER 會內部分析,產生一個最優的執行計划,所以不用你操心,自動幫你處理了!而使用JOIN ON 的話,就好像是使用強制命令,告訴數據庫,就是要按你的方式處理結果,數據庫只好服從!! 所以思考SQL寫法不能只說要效率,同時還要注重結果對了,這才是關鍵!
4。本人一次看別人SQL,就是不明白作表連接查詢,為什么WHERE后面要進行大量的WHERE條件篩選,而且都是無關業務邏輯的。在我的傳統觀念看來,執行WHERE 語句是需要對全表進行掃描的,這樣因該會增加查詢時間。現在結合前面所講的,因為不論是(FROM--AND 還是 JOIN--ON)方式,再與第三張表JOIN之前都應該盡量先篩選一部分結果(可能是大部分結果)。這樣速度會大大提升!
表連接人人都會,可真要說的清清楚楚,也許還需要花點功夫和時間去測試和總結吧。說起來因該有很大的文章,本人知識積累還不夠,還希望有人能提點提點,斧正補充一下!!
