sql各種Join用法詳解
在關係代數中,連線運算是由一個笛卡爾積運算和一個選取運算構成的。首先用笛卡爾積完成對兩個資料集合的乘運算,然後對生成的結果集合進行選取運算,確保只把分別來自兩個資料集合並且具有重疊部分的行合併在一起。連線的全部意義在於在水平方向上合併兩個資料集合(通常是表),併產生一個新的結果集合,其方法是將一個數據源中的行於另一個數據源中和它匹配的行組合成一個新元組。SQL提供了多種型別的連線方式,它們之間的區別在於:從相互交疊的不同資料集合中選擇用於連線的行時所採用的方法不同。
不同的 SQL JOIN
連線型別 |
定義 |
|
Inner Join |
內連線是最常見的一種連線,它也被稱為普通連線,只連線匹配的行(僅對滿足連線條件的CROSS中的列)。它又分為等值連線(連線條件運算子為"=")和不等值連線(連線條件運算子不為"=",例如between...and)。 |
|
Outer Join |
Full Outer Join |
FULL JOIN 會從左表 和右表 那裡返回所有的行。如果其中一個表的資料行在另一個表中沒有匹配的行,那麼對面的資料用NULL代替 |
Left Outer Join |
LEFT JOIN返回左表的全部行和右表滿足ON條件的行,如果左表的行在右表中沒有匹配,那麼這一行右表中對應資料用NULL代替。 |
|
Right Outer Join |
RIGHT JOIN返回右表的全部行和左表滿足ON條件的行,如果右表的行在左表中沒有匹配,那麼這一行左表中對應資料用NULL代替。 |
|
Cross Join |
CROSS對兩個表執行笛卡爾乘積。它為左錶行和右錶行的每種可能的組合返回一行。返回(左錶行數*右錶行數)行的表。 |
|
Appendix |
Natural Join |
自然連線是一種特殊的等值連線,在連線條件中使用等於(=)運算子比較被連線列的列值,但它使用選擇列表指出查詢結果集合中所包括的列,並刪除連線表中的重複列。 |
自連線 |
某個表和其自身連線,常用在同一表內不同資料間對同一列的比較 |
self join 常用在同一表內不同資料間對同一列的比較
select a.emp_no,a.emp_name,b.emp_no,b.emp_name,a.date_hired from
employee ajoin employee b
on (a.emp_no!=b.emp_no and a.date_hired=b.date_hired)
order by a.date_hired
這樣會重複資料,只要加上一句 and a.emp_name>b.emp_name
UNION
UNION 操作符用於合併兩個或多個 SELECT 語句的結果集。
請注意,UNION 內部的 SELECT 語句必須擁有相同數量的列。列也必須擁有相似的資料型別。
同時,每條 SELECT 語句中的列的順序必須相同。
SQL UNION 語法
SELECT column_name(s) FROM table_name1 UNION SELECT column_name(s) FROM table_name2註釋:預設地,UNION 操作符選取不同的值。如果允許重複的值,請使用 UNION ALL。
SQL UNION ALL 語法
UNION ALL 命令和 UNION 命令幾乎是等效的,不過 UNION ALL 命令會列出所有的值。
SELECT column_name(s) FROM table_name1 UNION ALL SELECT column_name(s) FROM table_name2另外,UNION 結果集中的列名總是等於 UNION 中第一個 SELECT 語句中的列名。
UNION 例項:
列出所有在中國和美國的不同的僱員名:
SELECT E_Name FROM Employees_ChinaUNION
SELECT E_Name FROM Employees_USAUNION ALL例項:
列出在中國和美國的所有的僱員:
SELECT E_Name FROM Employees_ChinaUNION ALL
SELECT E_Name FROM Employees_USA下面轉自:http://blog.sina.com.cn/s/blog_7e5f32ff0102wbwc.html
效能優化
1.顯示(explicit) inner join VS 隱式(implicit) inner join
如:
select * from table a inner join table b on a.id = b.id;
VS
select a.*, b.* from table a, table b where a.id = b.id;
我在資料庫中比較(10w資料)得之,它們用時幾乎相同,第一個是顯示的inner join,後一個是隱式的inner join。
2.left join/right join VS inner join
儘量用inner join.避免 LEFT JOIN 和 NULL.
在使用left join(或right join)時,應該清楚的知道以下幾點:
(1). on與 where的執行順序
ON 條件(“A LEFT JOIN B ON 條件表示式”中的ON)用來決定如何從 B 表中檢索資料行。如果 B 表中沒有任何一行資料匹配 ON 的條件,將會額外生成一行所有列為 NULL 的資料,在匹配階段 WHERE 子句的條件都不會被使用。僅在匹配階段完成以後,WHERE 子句條件才會被使用。它將從匹配階段產生的資料中檢索過濾。
所以我們要注意:在使用Left (right) join的時候,一定要在先給出儘可能多的匹配滿足條件,減少Where的執行。如:
PASS
select * from A inner join B on B.name = A.name left join C on C.name = B.name left join D on D.id = C.id where C.status>1 and D.status=1;
Great
select * from A inner join B on B.name = A.name left join C on C.name = B.name and C.status>1 left join D on D.id = C.id and D.status=1
從上面例子可以看出,儘可能滿足ON的條件,而少用Where的條件。從執行效能來看第二個顯然更加省時。
(2).注意ON 子句和 WHERE 子句的不同
如作者舉了一個列子:
mysql> SELECT * FROM product LEFT JOIN product_details ON (product.id = product_details.id) AND product_details.id=2; +----+--------+------+--------+-------+ | id | amount | id | weight | exist | +----+--------+------+--------+-------+ | 1 | 100 | NULL | NULL | NULL | | 2 | 200 | 2 | 22 | 0 | | 3 | 300 | NULL | NULL | NULL | | 4 | 400 | NULL | NULL | NULL | +----+--------+------+--------+-------+ 4 rows in set (0.00 sec) mysql> SELECT * FROM product LEFT JOIN product_details ON (product.id = product_details.id) WHERE product_details.id=2; +----+--------+----+--------+-------+ | id | amount | id | weight | exist | +----+--------+----+--------+-------+ | 2 | 200 | 2 | 22 | 0 | +----+--------+----+--------+-------+ 1 row in set (0.01 sec)
從上可知,第一條查詢使用 ON 條件決定了從 LEFT JOIN的 product_details表中檢索符合的所有資料行。第二條查詢做了簡單的LEFT JOIN,然後使用 WHERE 子句從 LEFT JOIN的資料中過濾掉不符合條件的資料行。
(3).儘量避免子查詢,而用join
往往效能這玩意兒,更多時候體現在資料量比較大的時候,此時,我們應該避免複雜的子查詢。如下:
PASS
insert into t1(a1) select b1 from t2 where not exists(select 1 from t1 where t1.id = t2.r_id);
Great
insert into t1(a1) select b1 from t2 left join (select distinct t1.id from t1 ) t1 on t1.id = t2.r_id where t1.id is null;
由於客戶資料量越來越大,在實踐中讓我發現mysql的exists與inner join 和 not exists與 left join 效能差別驚人。
我們一般在做資料插入時,想插入不重複的資料,或者盤點資料在一個表,另一個表否有存在相同的資料會用not exists和exists,例如:
Sql程式碼
- insert into t1(a1) select b1 from t2 where not exists(select 1 from t1 where t1.id = t2.r_id);
如果t1的資料量很大時,效能會非常慢。經過實踐,用以下方法能提高很多。
Sql程式碼Sql程式碼
- insert into t1(a1)
- select b1 from t2
- left join (select distinct t1.id from t1 ) t1 on t1.id = t2.r_id
- where t1.id is null;
- select * from t1 where exists(select 1 from t2 where t1.id=t2.r_id);
替換為:
Sql程式碼
- select t1.* from t1
- inner join (select distinct r_id from t2) t2 on t1.id= t2.r_id
這是實踐的得出的結果。不知否有其他更好的方法,或則這個只是特例而已。
補充:MySQL STRAIGHT_JOIN 與 NATURAL JOIN的使用
長話短說:straight_join實現強制多表的載入順序,從左到右,如:
...A straight_join B on A.name = B.name
straight_join完全等同於inner join 只不過,join語法是根據“哪個表的結果集小,就以哪個表為驅動表”來決定誰先載入的,而straight_join 會強制選擇其左邊的表先載入。
往往我們在分析mysql處理效能時,如(Explain),如果發現mysql在載入順序不合理的情況下,可以使用這個語句,但往往mysql能夠自動的分析並處理好。
更多內容參考:八.參考: