天天看點

SQL連接配接查詢深度探險

SQL連接配接查詢深度探險

測試環境:

Windows XP Profession

MySQL 5.0.45

Oracle 9i

DB2 UDB 9.1

測試的SQL腳本如下:此腳本适合MySQL、DB2,如果要在Oracle上執行,需要做個替換BIGINTàINTEGER、VARCHARàVARCHAR2。

CREATE TABLE CUSTOMERS (

   ID <b>BIGINT</b> NOT NULL,

   NAME <b>VARCHAR</b>(<b>15</b>) NOT NULL,

   AGE <b>INT</b>,

   PRIMARY KEY (ID)

);

CREATE TABLE ORDERS (

   ORDER_NUMBER <b>VARCHAR</b>(<b>15</b>) NOT NULL,

   PRICE <b>DOUBLE</b> <b>PRECISION</b>,

   CUSTOMER_ID <b>BIGINT</b>,

COMMIT;

/*

ALTER TABLE ORDERS ADD INDEX FK_CUSTOMER (CUSTOMER_ID), ADD CONSTRAINT FK_CUSTOMER FOREIGN KEY (CUSTOMER_ID) REFERENCES CUSTOMERS (ID);

*/

INSERT INTO CUSTOMERS(ID,NAME,AGE) VALUES(<b>1</b>,'TOM',<b>21</b>);

INSERT INTO CUSTOMERS(ID,NAME,AGE) VALUES(<b>2</b>,'MIKE',<b>24</b>);

INSERT INTO CUSTOMERS(ID,NAME,AGE) VALUES(<b>3</b>,'JACK',<b>30</b>);

INSERT INTO CUSTOMERS(ID,NAME,AGE) VALUES(<b>4</b>,'LINDA',<b>25</b>);

INSERT INTO CUSTOMERS(ID,NAME,AGE) VALUES(<b>5</b>,'TOM',NULL);

INSERT INTO ORDERS(ID,ORDER_NUMBER,PRICE,CUSTOMER_ID) VALUES(<b>1</b>,'TOM_ORDER001',<b>100</b>,<b>1</b>);

INSERT INTO ORDERS(ID,ORDER_NUMBER,PRICE,CUSTOMER_ID) VALUES(<b>2</b>,'TOM_ORDER002',<b>200</b>,<b>1</b>);

INSERT INTO ORDERS(ID,ORDER_NUMBER,PRICE,CUSTOMER_ID) VALUES(<b>3</b>,'TOM_ORDER003',<b>300</b>,<b>1</b>);

INSERT INTO ORDERS(ID,ORDER_NUMBER,PRICE,CUSTOMER_ID) VALUES(<b>4</b>,'MIKE_ORDER001',<b>100</b>,<b>2</b>);

INSERT INTO ORDERS(ID,ORDER_NUMBER,PRICE,CUSTOMER_ID) VALUES(<b>5</b>,'JACK_ORDER001',<b>200</b>,<b>3</b>);

INSERT INTO ORDERS(ID,ORDER_NUMBER,PRICE,CUSTOMER_ID) VALUES(<b>6</b>,'LINDA_ORDER001',<b>100</b>,<b>4</b>);

INSERT INTO ORDERS(ID,ORDER_NUMBER,PRICE,CUSTOMER_ID) VALUES(<b>7</b>,'UNKNOWNORDER',<b>200</b>,NULL);

兩表的資料如下:

CUSTOMERS表資料:

ORDERS表資料

一、交叉連接配接(CROSS JOIN)

交叉連接配接(CROSS JOIN):有兩種,顯式的和隐式的,不帶ON子句,傳回的是兩表的乘積,也叫笛卡爾積。

例如:下面的語句1和語句2的結果是相同的。

語句1:隐式的交叉連接配接,沒有CROSS JOIN。

SELECT O.ID, O.ORDER_NUMBER, C.ID, C.NAME

FROM ORDERS O , CUSTOMERS C

WHERE O.ID=<b>1</b>;

語句2:顯式的交叉連接配接,使用CROSS JOIN。

SELECT O.ID,O.ORDER_NUMBER,C.ID,C.NAME

FROM ORDERS O CROSS JOIN CUSTOMERS C

語句1和語句2的結果是相同的,查詢結果如下:

二、内連接配接(INNER JOIN)

内連接配接(INNER JOIN):有兩種,顯式的和隐式的,傳回連接配接表中符合連接配接條件和查詢條件的資料行。(所謂的連結表就是資料庫在做查詢形成的中間表)。

例如:下面的語句3和語句4的結果是相同的。

語句3:隐式的内連接配接,沒有INNER JOIN,形成的中間表為兩個表的笛卡爾積。

FROM CUSTOMERS C,ORDERS O

WHERE C.ID=O.CUSTOMER_ID;

語句4:顯示的内連接配接,一般稱為内連接配接,有INNER JOIN,形成的中間表為兩個表經過ON條件過濾後的笛卡爾積。

FROM CUSTOMERS C INNER JOIN ORDERS O ON C.ID=O.CUSTOMER_ID;

語句3和語句4的查詢結果:

三、外連接配接(OUTER JOIN):外連不但傳回符合連接配接和查詢條件的資料行,還傳回不符合條件的一些行。外連接配接分三類:左外連接配接(LEFT OUTER JOIN)、右外連接配接(RIGHT OUTER JOIN)和全外連接配接(FULL OUTER JOIN)。

三者的共同點是都傳回符合連接配接條件和查詢條件(即:内連接配接)的資料行。不同點如下:

左外連接配接還傳回左表中不符合連接配接條件單符合查詢條件的資料行。

右外連接配接還傳回右表中不符合連接配接條件單符合查詢條件的資料行。

全外連接配接還傳回左表中不符合連接配接條件單符合查詢條件的資料行,并且還傳回右表中不符合連接配接條件單符合查詢條件的資料行。全外連接配接實際是上左外連接配接和右外連接配接的數學合集(去掉重複),即“全外=左外 UNION 右外”。

說明:左表就是在“(LEFT OUTER JOIN)”關鍵字左邊的表。右表當然就是右邊的了。在三種類型的外連接配接中,OUTER 關鍵字是可省略的。

下面舉例說明:

語句5:左外連接配接(LEFT OUTER JOIN)

SELECT O.ID,O.ORDER_NUMBER,O.CUSTOMER_ID,C.ID,C.NAME

FROM ORDERS O LEFT OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID;

語句6:右外連接配接(RIGHT OUTER JOIN)

FROM ORDERS O RIGHT OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID;

注意:WHERE條件放在ON後面查詢的結果是不一樣的。例如:

語句7:WHERE條件獨立。

FROM ORDERS O LEFT OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID

WHERE O.ORDER_NUMBER&lt;&gt;'MIKE_ORDER001';

語句8:将語句7中的WHERE條件放到ON後面。

FROM ORDERS O LEFT OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID AND O.ORDER_NUMBER&lt;&gt;'MIKE_ORDER001';

從語句7和語句8查詢的結果來看,顯然是不相同的,語句8顯示的結果是難以了解的。是以,推薦在寫連接配接查詢的時候,ON後面隻跟連接配接條件,而對中間表限制的條件都寫到WHERE子句中。

語句9:全外連接配接(FULL OUTER JOIN)。

FROM ORDERS O FULL OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID;

注意:MySQL是不支援全外的連接配接的,這裡給出的寫法适合Oracle和DB2。但是可以通過左外和右外求合集來擷取全外連接配接的查詢結果。下圖是上面SQL在Oracle下執行的結果:

語句10:左外和右外的合集,實際上查詢結果和語句9是相同的。

UNION

語句9和語句10的查詢結果是相同的,如下:

四、聯合連接配接(UNION JOIN):這是一種很少見的連接配接方式。Oracle、MySQL均不支援,其作用是:找出全外連接配接和内連接配接之間差異的所有行。這在資料分析中排錯中比較常用。也可以利用資料庫的集合操作來實作此功能。

語句11:聯合查詢(UNION JOIN)例句,還沒有找到能執行的SQL環境。

FROM ORDERS O UNION JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID

語句12:語句11在DB2下的等價實作。還不知道DB2是否支援語句11呢!

FROM ORDERS O FULL OUTER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID

EXCEPT

FROM ORDERS O INNER JOIN CUSTOMERS C ON C.ID=O.CUSTOMER_ID;

語句13:語句11在Oracle下的等價實作。

MINUS

查詢結果如下:

五、自然連接配接(NATURAL INNER JOIN):說真的,這種連接配接查詢沒有存在的價值,既然是SQL2标準中定義的,就給出個例子看看吧。自然連接配接無需指定連接配接列,SQL會檢查兩個表中是否相同名稱的列,且假設他們在連接配接條件中使用,并且在連接配接條件中僅包含一個連接配接列。不允許使用ON語句,不允許指定顯示列,顯示列隻能用*表示(ORACLE環境下測試的)。對于每種連接配接類型(除了交叉連接配接外),均可指定NATURAL。下面給出幾個例子。

語句14:

SELECT *

FROM ORDERS O NATURAL INNER JOIN CUSTOMERS C;

語句15:

FROM ORDERS O NATURAL LEFT OUTER JOIN CUSTOMERS C;

語句16:

FROM ORDERS O NATURAL RIGHT OUTER JOIN CUSTOMERS C;

語句17:

FROM ORDERS O NATURAL FULL OUTER JOIN CUSTOMERS C;

六、SQL查詢的基本原理:兩種情況介紹。

第一、   單表查詢:根據WHERE條件過濾表中的記錄,形成中間表(這個中間表對使用者是不可見的);然後根據SELECT的選擇列選擇相應的列進行傳回最終結果。

第二、   兩表連接配接查詢:對兩表求積(笛卡爾積)并用ON條件和連接配接連接配接類型進行過濾形成中間表;然後根據WHERE條件過濾中間表的記錄,并根據SELECT指定的列傳回查詢結果。

第三、   多表連接配接查詢:先對第一個和第二個表按照兩表連接配接做查詢,然後用查詢結果和第三個表做連接配接查詢,以此類推,直到所有的表都連接配接上為止,最終形成一個中間的結果表,然後根據WHERE條件過濾中間表的記錄,并根據SELECT指定的列傳回查詢結果。

了解SQL查詢的過程是進行SQL優化的理論依據。

七、ON後面的條件(ON條件)和WHERE條件的差別:

ON條件:是過濾兩個連結表笛卡爾積形成中間表的限制條件。

WHERE條件:在有ON條件的SELECT語句中是過濾中間表的限制條件。在沒有ON的單表查詢中,是限制實體表或者中間查詢結果傳回記錄的限制。在兩表或多表連接配接中是限制連接配接形成最終中間表的傳回結果的限制。

從這裡可以看出,将WHERE條件移入ON後面是不恰當的。推薦的做法是:

ON隻進行連接配接操作,WHERE隻過濾中間表的記錄。

八、總結

連接配接查詢是SQL查詢的核心,連接配接查詢的連接配接類型選擇依據實際需求。如果選擇不當,非但不能提高查詢效率,反而會帶來一些邏輯錯誤或者性能低下。下面總結一下兩表連接配接查詢選擇方式的依據:

1、  查兩表關聯列相等的資料用内連接配接。

2、  Col_L是Col_R的子集時用右外連接配接。

3、  Col_R是Col_L的子集時用左外連接配接。

4、  Col_R和Col_L彼此有交集但彼此互不為子集時候用全外。

5、  求差操作的時候用聯合查詢。

多個表查詢的時候,這些不同的連接配接類型可以寫到一塊。例如:

  SELECT T1.C1,T2.CX,T3.CY

  FROM TAB1 T1

       INNER JOIN TAB2 T2 ON (T1.C1=T2.C2)

       INNER JOIN TAB3 T3 ON (T1.C1=T2.C3)

       LEFT OUTER JOIN TAB4 ON(T2.C2=T3.C3);

  WHERE T1.X &gt;T3.Y;

上面這個SQL查詢是多表連接配接的一個示範。

----全文完。

說明:此測試環境的SQL腳本來自孫衛秦《精通Hibernate》一書中的客戶訂單模型。

本文轉自 leizhimin 51CTO部落格,原文連結:http://blog.51cto.com/lavasoft/38929,如需轉載請自行聯系原作者