在MySQL中,聯接是一種對錶的引用,mysql
多表聯接類型:sql
1.笛卡爾積(交叉聯接):在MySQL中爲CROSS JOIN或省略JOIN,如:數據庫
select * from course, teachcourse; -- 隱式交叉聯接安全
或者select * from course join teachcourseoracle
或 select * from course cross join teachcourse;性能
返回結果爲被鏈接的兩張表的乘積。所以當有WHERE,ON或USING條件時通常不建議使用。由於數據多太時查詢會很慢。通常使用LEFT [OUTER] JOIN或者 RIGHT [OUTER] JOIN。測試
對於交叉聯接,選擇主表影響到查詢的效率。以下,course,teachcourse的位置不一樣,所要做的全表掃描表也不一樣,通常將小表用於全表掃描。例:優化
mysql> desc select * from course, teachcourse where course.cid = teachcourse.cid;ui
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+-------+blog
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+-------+
| 1 | SIMPLE | course | ALL | cid_class | NULL | NULL | NULL | 36 | |
| 1 | SIMPLE | teachcourse | ref | cid | cid | 4 | testdb.course.cid | 1 | |
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+-------+
2 rows in set (0.00 sec)
mysql> desc select * from teachcourse,course where course.cid = teachcourse.cid;
+----+-------------+-------------+------+---------------+-----------+---------+------------------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+---------------+-----------+---------+------------------------+------+-------+
| 1 | SIMPLE | teachcourse | ALL | cid | NULL | NULL | NULL | 36 | |
| 1 | SIMPLE | course | ref | cid_class | cid_class | 4 | testdb.teachcourse.cid | 1 | |
+----+-------------+-------------+------+---------------+-----------+---------+------------------------+------+-------+
2 rows in set (0.00 sec)
2.內聯接INNER JOIN:在MySQL中也叫等值聯接。在MySQL中CROSS和INNER JOIN被劃爲一類。
在內聯接的數據記錄中,不會存在字段爲NULL的狀況。能夠簡單認爲,內連接的結果就是在左聯接(或右聯接)的結果中去除存在字段爲NULL的記錄後獲得的結果。
INNER JOIN產生的結果集是A和B的交集。(圖片來源互聯網)
例: select * from course, teachcourse where course.cid = teachcourse.cid; -- 隱式
等同於:select * from course inner join teachcourse on course.cid=teachcourse.cid; ---顯式
主表的選擇將影響到查詢的效率,同交叉聯接,以下:
mysql> desc select * from course, teachcourse where course.cid = teachcourse.cid;
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+-------+
| 1 | SIMPLE | course | ALL | cid_class | NULL | NULL | NULL | 36 | |
| 1 | SIMPLE | teachcourse | ref | cid | cid | 4 | testdb.course.cid | 1 | |
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+-------+
2 rows in set (0.00 sec)
mysql> desc select * from teachcourse,course where course.cid = teachcourse.cid;
+----+-------------+-------------+------+---------------+-----------+---------+------------------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+---------------+-----------+---------+------------------------+------+-------+
| 1 | SIMPLE | teachcourse | ALL | cid | NULL | NULL | NULL | 36 | |
| 1 | SIMPLE | course | ref | cid_class | cid_class | 4 | testdb.teachcourse.cid | 1 | |
+----+-------------+-------------+------+---------------+-----------+---------+------------------------+------+-------+
2 rows in set (0.00 sec)
天然聯接:在鏈接條件中使用等於=運算符比較被鏈接列的列值,但刪除鏈接表中重複列.
3.外聯接,分爲左外聯接和右外聯接,即除返回符合條件的結果外,還要返回左表(左聯接)或右表(右聯接)中不符合聯接條件的結果,相對應的使用NULL對應。
例:
注意到上圖中,uid,period爲空的項。表示在course中的記錄在teachcourse中沒有相應的紀錄。 其他記錄都是既在左表又在右表的紀錄。
工做原理:
從左表讀出一條,選出全部與on匹配的右表紀錄(n條)進行鏈接,造成n條紀錄(包括重複的行),若是右邊沒有與on條件匹配的表,那鏈接的字段都是null.而後繼續讀下一條。
若是須要找出全部在左表而不在右表的記錄,能夠用右表沒有on匹配則顯示null的規律來查找。如:
能夠說,所謂的聯接表就是數據庫在作查詢造成的中間表,注意:where條件放在on後面查詢的結果是不同的。所以,推薦在寫鏈接查詢的時候,on後面只跟鏈接條件,而對中間表限制的條件都寫到where子句中。
如上語句:select * from course left join teachcourse on course.cid=teachcourse.cid where teachcourse.cid is null; 這裏的Where子句的做用是對聯接後產生的中間表進行過濾。因此並不等同select * from course left join teachcourse on course.cid=teachcourse.cid and teachcourse.cid is null;
select * from course left join teachcourse on course.cid=teachcourse.cid and teachcourse.cid is null;產生的結果也難以理解:
這兩條語句的執行計劃分別是:
mysql> desc select * from course left join teachcourse on course.cid=teachcourse.cid where teachcourse.cid is null;
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+--------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+--------------------+
| 1 | SIMPLE | course | ALL | NULL | NULL | NULL | NULL | 36 | |
| 1 | SIMPLE | teachcourse | ref | cid | cid | 4 | testdb.course.cid | 1 | Using where; Not exists|
+----+-------------+-------------+------+---------------+------+---------+-------------------+------+-------------------+
2 rows in set (0.00 sec)
mysql> desc select * from course left join teachcourse on course.cid=teachcourse.cid and teachcourse.cid is null;
+----+-------------+-------------+------+---------------+------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+---------------+------+---------+-------+------+-------+
| 1 | SIMPLE | course | ALL | NULL | NULL | NULL | NULL | 36 | |
| 1 | SIMPLE | teachcourse | ref | cid | cid | 4 | const | 3 | |
+----+-------------+-------------+------+---------------+------+---------+-------+------+-------+
2 rows in set (0.00 sec)
mysql是不支持全外的鏈接的,可是能夠經過左外和右外求合集來獲取全外鏈接的查詢結果。
SELECT * FROM TableA FULL OUTER JOIN TableB(圖片來源互聯網)
Full outer join 產生A和B的並集。可是須要注意的是,對於沒有匹配的記錄,則會以null作爲值。
SELECT * FROM TableA LEFT OUTER JOIN TableB ON TableA.name = TableB.name(圖片來源互聯網)
Left outer join 產生表A的徹底集,而B表中匹配的則有值,沒有匹配的則以null值取代。
SELECT * FROM TableA LEFT OUTER JOIN TableB ON TableA.name = TableB.name WHERE TableB.id IS null(圖片來源互聯網)
產生在A表中有而在B表中沒有的集合。
SELECT * FROM TableA FULL OUTER JOIN TableB ON TableA.name = TableB.name WHERE TableA.id IS null OR TableB.id IS null
產生A表和B表都沒有出現的數據集。(圖片來源互聯網)
4.USING子句,若是鏈接的兩個錶鏈接條件的兩個列具備相同的名字的話可使用USING
語句 select * from course left join teachcourse using(cid) where teachcourse.cid is null
等同於 select * from course left join teachcourse on course.cid=teachcourse.cid where teachcourse.cid is null;
多表查詢的時候,須要根據查詢的狀況,想好使用哪一種鏈接方式效率更高。若是選擇不當,非但不能提升查詢效率,反而會帶來一些邏輯錯誤或者性能低下。下面總結一下兩錶鏈接查詢選擇方式的依據:
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 tb1 t1
inner join tb2 t2 on (t1.c1=t2.c2)
inner join tb3 t3 on (t1.c1=t2.c3)
left outer join tab4 on(t2.c2=t3.c3);
where t1.x >t3.y;
6)當 MySQL 在從一個表中檢索信息時,你能夠提示它選擇了哪個索引。
若是 EXPLAIN 顯示 MySQL 使用了可能的索引列表中錯誤的索引,這個特性將是頗有用的。
經過指定 USE INDEX (key_list),你能夠告訴 MySQL 使用可能的索引中最合適的一個索引在表中查找記錄行。
可選的二選一句法 IGNORE INDEX (key_list) 可被用於告訴 MySQL 不使用特定的索引。如:
mysql> SELECT * FROM table1 USE INDEX (key1,key2)
-> WHERE key1=1 AND key2=2 AND key3=3;
mysql> SELECT * FROM table1 IGNORE INDEX (key3)
-> WHERE key1=1 AND key2=2 AND key3=3;
如何優化LEFT JOIN和RIGHT JOIN
在MySQL中,A LEFT JOIN B join_condition執行過程以下:
1) 根據表A和A依賴的全部表設置表B。
2) 根據LEFT JOIN條件中使用的全部表(除了B)設置表A。
3) LEFT JOIN條件用於肯定如何從表B搜索行。(換句話說,不使用WHERE子句中的任何條件)。
4) 能夠對全部標準聯接進行優化,只是只有從它所依賴的全部表讀取的表例外。若是出現循環依賴關係,MySQL提示出現一個錯誤。
5) 進行全部標準WHERE優化。
6) 若是A中有一行匹配WHERE子句,但B中沒有一行匹配ON條件,則生成另外一個B行,其中全部列設置爲NULL。
7) 若是使用LEFT JOIN找出在某些表中不存在的行,而且進行了下面的測試:WHERE部分的col_name IS NULL,其中col_name是一個聲明爲 NOT NULL的列,MySQL找到匹配LEFT JOIN條件的一個行後中止(爲具體的關鍵字組合)搜索其它行。
RIGHT JOIN的執行相似LEFT JOIN,只是表的角色反過來。
聯接優化器計算表應聯接的順序。LEFT JOIN和STRAIGHT_JOIN強制的表讀順序能夠幫助聯接優化器更快地工做,由於檢查的表交換更少。
請注意這說明若是執行下面類型的查詢,MySQL進行全掃描b,由於LEFT JOIN強制它在d以前讀取:
SELECT * FROM a, b LEFT JOIN c ON (c.key=a.key) LEFT JOIN d ON (d.key=a.key) WHERE b.key=d.key;
在這種狀況下修復時用a的相反順序,b列於FROM子句中:
SELECT * FROM b, a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d ON (d.key=a.key) WHERE b.key=d.key;
MySQL能夠進行下面的LEFT JOIN優化:若是對於產生的NULL行,WHERE條件總爲假,LEFT JOIN變爲普通聯接。
例如,在下面的查詢中若是t2.column1爲NULL,WHERE 子句將爲false:
SELECT * FROM t1 LEFT JOIN t2 ON (column1) WHERE t2.column2=5;
所以,能夠安全地將查詢轉換爲普通聯接:
SELECT * FROM t1, t2 WHERE t2.column2=5 AND t1.column1=t2.column1;
這樣能夠更快,由於若是可使查詢更佳,MySQL能夠在表t1以前使用表t2。爲了強制使用表順序,使用STRAIGHT_JOIN。
附:聯接順序:
SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a LEFT JOIN t3 ON t2.b=t3.b WHERE…
順序:a:左鏈接按前後順序,先是表a和表b鏈接,再與表c鏈接。
b:先聯接操做,再用where過濾。
c:上句也等同於:
SELECT * FROM t1 LEFT JOIN (t2, t3) ON (t2.a=t1.a AND t2.b=t3.b) WHERE…
也等同於:
SELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3) ON t1.a=t2.a AND t2.b=t3.b WHERE…
例:
許多時候,在咱們同時使用inner join和outer join的時候必定要對鏈接的順序作慎重考慮。
附:SQL中關於聯接的概念:
(一) 內聯接
內聯接查詢操做列出與聯接條件匹配的數據行,它使用比較運算符比較被聯接列的列值。內聯接分三種:
1)等值聯接:在聯接條件中使用等於號(=)運算符比較被聯接列的列值,其查詢結果中列出被聯接表中的全部列,包括其中的重複列。
2)不等聯接: 在聯接條件使用除等於運算符之外的其它比較運算符比較被聯接的列的列值。這些運算符包括>、>=、<=、<、!>、!<和<>。
3)天然聯接:在聯接條件中使用等於(=)運算符比較被聯接列的列值,但它使用選擇列表指出查詢結果集合中所包括的列,並刪除聯接表中的重複列。
等值鏈接中不要求相等屬性值的屬性名相同,而天然鏈接要求相等屬性值的屬性名必須相同,即兩關係只有在同名屬性才能進行天然鏈接;等值鏈接不將重複屬性去掉,而天然鏈接去掉重複屬性,也能夠說,天然鏈接是去掉重複列的等值鏈接。
(二) 外鏈接
外鏈接分爲左外鏈接(LEFT OUTER JOIN或LEFT JOIN)、右外鏈接(RIGHT OUTER JOIN或RIGHT JOIN)和全外鏈接(FULL OUTER JOIN或FULL JOIN)三種。
內鏈接時,返回查詢結果集合中的僅是符合查詢條件( WHERE 搜索條件或 HAVING 條件)和鏈接條件的行。而採用外鏈接時,它返回到查詢結果集合中的不只包含符合鏈接條件的行,並且還包括左表(左外鏈接時)、右表(右外鏈接時)或兩個邊接表(全外鏈接)中的全部數據行。
三者的共同點是都返回符合鏈接條件和查詢條件(即:內鏈接)的數據行。不一樣點以下:
* 左外鏈接還返回左表中不符合鏈接條件單符合查詢條件的數據行。
* 右外鏈接還返回右表中不符合鏈接條件單符合查詢條件的數據行。
* 全外鏈接還返回左表中不符合鏈接條件單符合查詢條件的數據行,而且還返回右表中不符合鏈接條件單符合查詢條件的數據行。全外鏈接實際是上左外鏈接和右外鏈接的數學合集(去掉重複),即「全外=左外union 右外」。
(三) 交叉鏈接(CROSS JOIN)
交叉鏈接不帶WHERE 子句,它返回被鏈接的兩個表全部數據行的笛卡爾積,返回到結果集合中的數據行數等於第一個表中符合查詢條件的數據行數乘以第二個表中符合查詢條件的數據行數。
(四) 聯合鏈接(union join)
這是一種不多見的鏈接方式。oracle、mysql均不支持,其做用是:找出全外鏈接和內鏈接之間差別的全部行。這在數據分析中排錯中比較經常使用。也能夠利用數據庫的集合操做來實現此功能。
語句11:聯合查詢(union join)例句,尚未找到能執行的sql環境。
select t.uid, c.name, c.class from course as c union join teachcourse as t on c.cid=t.cid;
語句12:語句11在db2下的等價實現。還不知道db2是否支持語句11呢!
select t.cid, c.name, c.class from course c full outer join teachcourse t on c.cid=t.cid exception select t.cid, c.name, c.class from course c inner join teachcourse t on c.cid=t.cid;
語句13:語句11在oracle下的等價實現。
select t.cid, c.name, c.class from course c full outer join teachcourse t on c.cid=t.cid minus select t.cid, c.name, c.class from course c inner join teachcourse t on c.cid=t.cid;
不管哪一種鏈接都不能對text、ntext和image數據類型列進行直接鏈接,但能夠對這三種列進行間接鏈接。
關於sql查詢的基本原理:
* 單表查詢:根據where條件過濾表中的記錄,造成中間表(這個中間表對用戶是不可見的);而後根據select的選擇列選擇相應的列進行返回最終結果。
* 兩錶鏈接查詢:對兩表求積(笛卡爾積)並用on條件和鏈接類型進行過濾造成中間表;而後根據where條件過濾中間表的記錄,並根據select指定的列返回查詢結果。
* 多表鏈接查詢:先對第一個和第二個表按照兩錶鏈接作查詢,而後用查詢結果和第三個表作鏈接查詢,以此類推,直到全部的表都鏈接上爲止,最終造成一箇中間的結果表,而後根據where條件過濾中間表的記錄,並根據select指定的列返回查詢結果。
關於on條件和where條件的區別:
on條件:是過濾兩個連接表笛卡爾積造成中間表的約束條件。
where條件:在有on條件的select語句中是過濾中間表的約束條件。在沒有on的單表查詢中,是限制物理表或者中間查詢結果返回記錄的約束。在兩表或多表鏈接中是限制鏈接造成最終中間表的返回結果的約束。
使用的表:
create table teacher(
id int unsigned not null auto_increment,
uid int unsigned not null default 0,
name varchar(50) not null default '',
age tinyint not null default 0,
sex tinyint not null default 0,
primary key(id)
);
alter table teacher add index uid_age(uid, age);
create table course(
id int unsigned not null auto_increment,
cid int unsigned not null default 0,
name varchar(50) not null default '',
class int unsigned not null default 0,
primary key(id)
);
alter table course add index cid_class(cid, class);
create table teachcourse(
uid int unsigned not null default 0,
cid int unsigned not null default 0,
period int unsigned not null default 0
);
alter table teachcourse add foreign key(uid) references teacher(uid);
alter table teachcourse add foreign key(cid) references course(cid);