先簡單解釋一下笛卡爾積。java
如今,咱們有兩個集合A和B。mysql
A = {0,1} B = {2,3,4}算法
集合 A×B 和 B×A的結果集就能夠分別表示爲如下這種形式:sql
A×B = {(0,2),(1,2),(0,3),(1,3),(0,4),(1,4)};數據庫
B×A = {(2,0),(2,1),(3,0),(3,1),(4,0),(4,1)};學習
以上A×B和B×A的結果就能夠叫作兩個集合相乘的‘笛卡爾積’。優化
從以上的數據分析咱們能夠得出如下兩點結論:spa
1,兩個集合相乘,不知足交換率,既 A×B ≠ B×A;3d
2,A集合和B集合相乘,包含了集合A中元素和集合B中元素相結合的全部的可能性。既兩個集合相乘獲得的新集合的元素個數是 A集合的元素個數 × B集合的元素個數;code
MySQL的多表查詢(笛卡爾積原理)
數據庫錶鏈接數據行匹配時所遵循的算法就是以上提到的笛卡爾積,表與表之間的鏈接能夠當作是在作乘法運算。
好比如今數據庫中有兩張表,student表和 student_subject表,以下所示:
咱們執行如下的sql語句,只是純粹的進行錶鏈接。
SELECT * from student JOIN student_subject; SELECT * from student_subject JOIN student;
看一下執行結果:
表1.0 表1.1
從執行結果上來看,結果符合咱們以上提出的兩點結論(紅線標註部分);
以第一條sql語句爲例咱們來看一下他的執行流程,
1,from語句把student表 和 student_subject表從數據庫文件加載到內存中。
2,join語句至關於對兩張表作了乘法運算,把student表中的每一行記錄按照順序和student_subject表中記錄依次匹配。
3,匹配完成後,咱們獲得了一張有 (student中記錄數 × student_subject表中記錄數)條的臨時表。 在內存中造成的臨時表如表1.0所示。咱們又把內存中表1.0所示的表稱爲‘笛卡爾積表’。
針對以上的理論,咱們提出一個問題,難道錶鏈接的時候都要先造成一張笛卡爾積表嗎,若是兩張表的數據量都比較大的話,那樣就會佔用很大的內存空間這顯然是不合理的。因此,咱們在進行錶鏈接查詢的時候通常都會使用JOIN xxx ON xxx的語法,ON語句的執行是在JOIN語句以前的,也就是說兩張表數據行之間進行匹配的時候,會先判斷數據行是否符合ON語句後面的條件,再決定是否JOIN。
所以,有一個顯而易見的SQL優化的方案是,當兩張表的數據量比較大,又須要鏈接查詢時,應該使用 FROM table1 JOIN table2 ON xxx的語法,避免使用 FROM table1,table2 WHERE xxx 的語法,由於後者會在內存中先生成一張數據量比較大的笛卡爾積表,增長了內存的開銷。
下面引出Mysql的左右鏈接和內鏈接的笛卡爾積...
一個同事跟我討論左鏈接查詢,是否是笛卡爾積。我第一反應,左鏈接確定不是笛卡爾積啊,左鏈接是以左表爲準,左表有m條記錄,則結果集是m條記錄(哈哈,若是是你,你是否是也是這樣的反映),同事聽了,說內鏈接會是笛卡爾積。在數據庫裏試驗了一下,發現,事實比想象中要複雜。
首先說下結論:連接查詢,若是on條件是非惟一字段,會出現笛卡爾積(局部笛卡爾積);若是on條件是表的惟一字段,則不會出現笛卡爾積。
下面是具體的試驗:(以真三國無雙v3.9d蜀國陣容爲例...)
文中會有兩張表,user表和job表,表數據以下,其中user爲5條記錄,job爲4條記錄
USER: job:
1.交叉鏈接
若是A表有m(5)條記錄,m1條符合on條件,B表有n(4)條記錄,有n1條符合on條件,無條件交叉鏈接的結果爲: m*n=5*4=20
SELECT * FROM `user` CROSS JOIN job;
這種等同於(交叉查詢等於不加on的內鏈接)
SELECT * FROM `user` , job;
sql執行結果:總共20條記錄
結論:交叉鏈接,會產生笛卡爾積。
2.內鏈接(能夠當作左鏈接的特殊狀況,只保留符合主表中on條件的記錄)
(1)內鏈接惟一字段
若是A表有m(5)條記錄,m1(4)條符合on條件,B表有n(4)條記錄,有n1(3)條符合on條件,內鏈接惟一字段結果爲:Max(m1,n1)=4
1,2,2,6,7 和 1,2,7,8對比,以user表爲主表,由於主表中有4條符合條件的記錄(1,2,2,7),而job表有3條符合條件的記錄(1,2,7),取二者中的最大的,因此爲4條
SELECT * FROM `user` u JOIN job j ON u.JOB_ID=j.ID;
sql執行結果爲:4條記錄
結論:假如,內鏈接查詢,on條件是A表或者B表的惟一字段,則結果集是兩表的交集,不是笛卡爾積。
(2)內鏈接非惟一字段
若是A表有m(5)條記錄,m1(2)條符合on條件,B表有n(4)條記錄,有n1(3)條符合on條件,則結果集是Max(m1,n1)=3條
1,2,2,6,7 和 1,1,7,8對比,以user表爲主表,由於主表中有2條符合條件的記錄(1,7),而job表有3條符合條件的記錄(1,1,7),取二者中的最大的,因此爲3條
SELECT * FROM `user` u JOIN job j ON u.valid=j.valid;
結論:假如,on條件是表中非惟一字段,則結果集是兩表匹配到的結果集的笛卡爾積(局部笛卡爾積) 。
3.外鏈接
(1)左鏈接
a.左鏈接惟一字段
假如A表有m(5)條記錄,B表有n(4)條記錄,則結果集是m=5
1,2,2,6,7 和 1,2,7,8對比,以user表爲主表,由於主表中有4條符合條件的記錄(1,2,2,7),而job表有3條符合條件的記錄(1,2,7),取二者中的最大的,因此取4條,而後再加上user表中沒有在job表中找到對應關係的記錄(即對應的job表都爲null,5-4=1),因此最終結果爲4+1=5條
SELECT * FROM USER u LEFT JOIN job j ON u.JOB_ID=j.id;
SQL查詢結果:5條記錄
結論:on條件是惟一字段,則結果集是左表記錄的數量。
b.左鏈接非惟一字段
1,2,2,6,7 和 1,1,7,8對比,以user表爲主表,由於主表中有2條符合條件的記錄(1,7),而job表有3條符合條件的記錄(1,1,7),取二者中的最大的,因此取3條,而後在加上user表在job表中沒有匹配的記錄(即對應的job表都爲null,爲5-2=3),因此最終結果爲3+3=6條
SELECT * FROM `user` u LEFT JOIN job j ON u.VALID=j.VALID;
結論:左鏈接非惟一字段,是局部笛卡爾積。
c.當on 條件爲假時的內鏈接:
SELECT * FROM `user` u LEFT JOIN job j ON 0;
sql查詢結果:5條
結論:當on條件爲假的時候,即user在job表中一條符合記錄的都沒有,那麼即爲:user表中的全部記錄條數,因此爲5條,job表中的值都爲null
(2)右鏈接
同左鏈接,這裏就不贅述了
全外鏈接
mysql不支持
1.全匹配:
不管哪一種查詢,首先計算出on匹配記錄(FROM user INNER JOIN job ON ...或者使用 FROM user,job where...),匹配記錄的查詢結果爲:若A表有m條記錄,符合on查詢條件的爲m1條,B表有n條記錄,符合on條件的爲n1條,那麼匹配記錄爲MAX(m1,n1);
2.左鏈接:
結果集爲:MAX(m1,n1)+(m-m1);
若是m1 > n1,則不會產生笛卡爾積,由於不管不匹配的記錄(m-m1),仍是匹配的記錄MAX(m1,n1),都是從左表中取記錄,因此不會出現重複的記錄;反之,若是m1 < n1,則必定會產生笛卡爾積,由於MAX(m1,n1)是從右表中取的,而根據笛卡爾積的原理,右表中的每條記錄都會和左表中的全部記錄匹配一次,因此符合on條件的n1條記錄也必定會和左表中的全部記錄都匹配一次,而左表中符合記錄只有m1條,因此形成笛卡爾積的條數爲(n1-m1)條
即用內鏈接的記錄(MAX(m1,n1)),加上左表沒有知足on條件的記錄(m-m1),因此爲:MAX(m1,n1)+(m-m1);
3.有鏈接
結果集爲:MAX(m1,n1)+(n-n1);
若是m1 < n1,則不會產生笛卡爾積,由於不管不匹配的記錄(n-n1),仍是匹配的記錄MAX(m1,n1),都是從右表中取記錄,因此不會出現重複的記錄;反之,若是m1 > n1,則必定會產生笛卡爾積,由於MAX(m1,n1)是從左表中取的,因此形成笛卡爾積的記錄條數爲(m1-n1)條
即用內鏈接的記錄(MAX(m1,n1)),加上右表沒有知足on條件的記錄(n-n1),因此爲:MAX(m1,n1)+(n-n1);
下面再來談下:
不少同窗在學習 Mysql 表關聯的時候弄不清 ON
與 WHERE
的區別,不知道條件應該寫在 ON
裏面仍是 WHERE
裏面,做者在工做的時候也入過坑,總以爲條件寫在哪裏查詢結果都是同樣的,最後出錯壞了事,差點惹了大禍。因此今天簡單易懂的總結一下他們的區別,你們共同窗習。
咱們先準備兩個表,並造一些數據:
表t1:
CREATE TABLE `t1` ( `id` BIGINT(20) NOT NULL DEFAULT '0' COMMENT '主鍵id', `name` CHAR(100) NOT NULL DEFAULT '' COMMENT '姓名', `age` INT(11) NOT NULL DEFAULT '0' ) ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO `t1`(`id`,`name`,`age`) VALUES (1,'C羅',33),(2,'梅西',31),(3,'內馬爾',29);
表t2:
CREATE TABLE `t2` ( `id` BIGINT(20) NOT NULL DEFAULT '0' COMMENT '主鍵id', `goals` INT(11) NOT NULL DEFAULT '0' COMMENT '進球數', `matches` INT(11) NOT NULL DEFAULT '0' COMMENT '比賽編號' ) ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO `t2`(`id`,`goals`,`matches`) VALUES (1,3,1),(1,5,2),(2,0,1),(2,8,2);
查詢結果如圖:
表t1記錄:
表t2記錄:
口訣:先執行 ON
,後執行 WHERE
;ON
是創建關聯關係,WHERE
是對關聯關係的篩選。
記住這句話就能夠準確地判斷查詢結果了,咱們經過兩個 sql 來進行分析:
SQL1:
SELECT t1.id,t2.id,t1.name,t1.age,t2.`matches` FROM t1 LEFT JOIN t2 ON t1.id = t2.id WHERE matches = 2; -- 條件放在 WHERE
前提是 LEFT JOIN
,因此左邊的數據在建立關聯關係時會保留,根據口訣,先執行 ON
創建關聯關係,造成子表,最後在子表中經過 WHERE
篩選,過程以下:
左表符合記錄的t1.id = t2.id的記錄有2條,右表符合記錄的有4條,因此MAX(m1,n1)爲4條,其中左表沒有符合on條件的記錄爲(3-2=1)條,因此LEFT JOIN的結果總共有5條,最後一條左表沒有匹配上右表記錄,因此右表的屬性都爲null,以下:
可是最終結果從5條記錄中再經過where進行篩選,即matches爲2,因此結果只有2條(注意先LEFT JOIN,而後再是where,因此此時5條記錄中不符合where條件的記錄會被排除,即最終的結果再也不是左表全部的記錄):
SQL2:
SELECT t1.id,t2.id,t1.name,t1.age,t2.`matches` FROM t1 LEFT JOIN t2 ON t1.id = t2.id AND matches = 2; -- 條件放在 ON
SQL2沒有 WHERE
,那麼 ON
創建的關聯關係就是最終結果(由於沒有where條件進行最終篩選,全部最終結果爲內聯記錄加上左表中沒有符合on條件的記錄):
符合ON條件的記錄,改成內聯INNER JOIN ,查詢基礎數據:MAX(m1,n1)
SELECT t1.id,t2.id,t1.name,t1.age,t2.`matches` FROM t1 INNER JOIN t2 ON t1.id = t2.id AND matches = 2;
因此MAX(m1,n1)爲2條,結果爲:
再由基礎數據加上左表沒有匹配上的記錄數(只有id爲3的沒有匹配上,即(n-n1) = 1)只有1條,因此最終結果爲:
最終結果爲:MAX(m1,n1)+(m-m1) = 2 + (id爲3的記錄) = 3條
經過這兩個 sql 能夠很好的區分 WHERE
和 ON
的區別了,但願你們使用的時候多注意這一點,避免犯錯!