首先,先簡單解釋一下笛卡爾積。html
如今,咱們有兩個集合A和B。java
A = {0,1} B = {2,3,4}程序員
集合 A×B 和 B×A的結果集就能夠分別表示爲如下這種形式:算法
A×B = {(0,2),(1,2),(0,3),(1,3),(0,4),(1,4)};sql
B×A = {(2,0),(2,1),(3,0),(3,1),(4,0),(4,1)};數據庫
以上A×B和B×A的結果就能夠叫作兩個集合相乘的‘笛卡爾積’。json
從以上的數據分析咱們能夠得出如下兩點結論:數組
1,兩個集合相乘,不知足交換率,既 A×B ≠ B×A;ide
2,A集合和B集合相乘,包含了集合A中元素和集合B中元素相結合的全部的可能性。既兩個集合相乘獲得的新集合的元素個數是 A集合的元素個數 × B集合的元素個數;jsonp
數據庫錶鏈接數據行匹配時所遵循的算法就是以上提到的笛卡爾積,表與表之間的鏈接能夠當作是在作乘法運算。
好比如今數據庫中有兩張表,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 的語法,由於後者會在內存中先生成一張數據量比較大的笛卡爾積表,增長了內存的開銷。
根據上一篇博客(http://www.cnblogs.com/cdf-opensource-007/p/6502556.html),及本篇博客的分析,咱們能夠總結出一條查詢sql語句的執行流程。
From
ON
JOIN
WHERE
GROUP BY
SELECT
HAVING
ORDER BY
LIMIT
最後,針對兩張數據庫錶鏈接的底層實現,我用java代碼模擬了一下,感興趣的能夠看一下,可以幫助咱們理解:
package com.opensource.util; import java.util.Arrays; public class DecareProduct { public static void main(String[] args) { //使用二維數組,模擬student表 String[][] student ={ {"0","jsonp"}, {"1","alice"} }; //使用二維數組,模擬student_subject表 String[][] student_subject2 ={ {"0","0","語文"}, {"1","0","數學"} }; //模擬 SELECT * from student JOIN student_subject; String[][] resultTowArray1 = getTwoDimensionArray(student,student_subject2); //模擬 SELECT * from student_subject JOIN student; String[][] resultTowArray2 = getTwoDimensionArray(student_subject2,student); int length1 = resultTowArray1.length; for (int i = 0; i <length1 ; i++) { System.out.println(Arrays.toString(resultTowArray1[i])); } System.err.println("-----------------------------------------------"); int length2 = resultTowArray2.length; for (int i = 0; i <length2 ; i++) { System.out.println(Arrays.toString(resultTowArray2[i])); } } /** * 模擬兩張錶鏈接的操做 * @param towArray1 * @param towArray2 * @return */ public static String[][] getTwoDimensionArray(String[][] towArray1,String[][] towArray2){ //獲取二維數組的高(既該二維數組中有幾個一維數組,用來指代數據庫表中的記錄數) int high1 = towArray1.length; int high2 = towArray2.length; //獲取二維數組的寬度(既二位數組中,一維數組的長度,用來指代數據庫表中的列) int wide1 = towArray1[0].length; int wide2 = towArray2[0].length; //計算出兩個二維數組進行笛卡爾乘積運算後得到的結果集數組的高度和寬度,既笛卡爾積表的行數和列數 int resultHigh = high1 * high2; int resultWide = wide1 + wide2; //初始化結果集數組,既笛卡爾積表 String[][] resultArray = new String[resultHigh][resultWide]; //迭代變量 int index = 0; //先對第二二維數組遍歷 for (int i = 0; i < high2; i++) { //拿出towArray2這個二維數組的元素 String[] tempArray = towArray2[i]; //循環嵌套,對第towArray1這個二維數組遍歷 for (int j = 0; j < high1; j++) { //初始化一個長度爲'resultWide'的數組,做爲結果集數組的元素,既笛卡爾積表中的一行 String[] tempExtened = new String[resultWide]; //拿出towArray1這個二維數組的元素 String[] tempArray1 = towArray1[j]; //把tempArray1和tempArray兩個數組的元素拷貝到結果集數組的元素中去。(這裏用到了數組擴容) System.arraycopy(tempArray1, 0, tempExtened, 0, tempArray1.length); System.arraycopy(tempArray, 0, tempExtened, tempArray1.length, tempArray.length); //把tempExtened放入結果集數組中 resultArray[index] = tempExtened; //迭代加一 index++; } } return resultArray; } }
執行結果:
最後說一點,咱們做爲程序員,研究問題仍是要仔細深刻一點的。當你對原理了解的有夠透徹,開發起來也就駕輕就熟了,不少開發中的問題和疑惑也就迎刃而解了,並且在面對其餘問題的時候也可作到舉一反三。固然在開發中沒有太多的時間讓你去研究原理,開發中要以實現功能爲前提,可等項目上線的後,你有大把的時間或者空餘的時間,你大可去刨根問底,深刻的去研究一項技術,我以爲這對一名程序員的成長是很重要的事情。