Mysql內鏈接、左鏈接會出現笛卡爾積的理解

先簡單解釋一下笛卡爾積。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的多表查詢(笛卡爾積原理)

  1. 先肯定數據要用到哪些表。
  2. 將多個表先經過笛卡爾積變成一個表。
  3. 而後去除不符合邏輯的數據(根據兩個表的關係去掉)。
  4. 最後當作是一個虛擬表同樣來加上條件便可。

數據庫錶鏈接數據行匹配時所遵循的算法就是以上提到的笛卡爾積,表與表之間的鏈接能夠當作是在作乘法運算。

好比如今數據庫中有兩張表,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不支持

總結:左右鏈接是否產生笛卡爾積,和on的條件是否爲惟一索引沒有關係,只和左表、右表二者各自匹配on條件的記錄條數有關係(左鏈接和右鏈接能夠這麼作:1:由INNER JOIN查詢全匹配記錄;2:查詢左表或者右表沒有符合on條件的記錄;3.全匹配記錄+沒有匹配上的記錄就是左鏈接或者右鏈接查詢的結果集)

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 的區別

不少同窗在學習 Mysql 表關聯的時候弄不清 ONWHERE 的區別,不知道條件應該寫在 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,後執行 WHEREON 是創建關聯關係,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 能夠很好的區分 WHEREON 的區別了,但願你們使用的時候多注意這一點,避免犯錯!

相關文章
相關標籤/搜索