Mysql中的索引關係和正規化
索引的定義是:系統根據某種演算法,將已有的資料(未來可能新增的資料)單獨建立一個檔案,這個檔案能夠實現快速的匹配資料,並且能夠快速的找到對應的表中的記錄。
索引的意義:
1. 提升查詢資料的效率
2. 約束資料的有效性(唯一性等)
增加索引是有前提條件的:索引本身會產生索引檔案(有時候有可能比資料檔案還大),會非常耗費磁碟空間。如果某個欄位需要作為查詢的條件經常使用,那麼使用索引。或者如果某個欄位需要進行資料的有效性約束,也可能使用索引。
Mysql提供了多種索引。
1. 主鍵索引:primary key
2. 唯一索引:unique key
3. 全文索引:fulltext index
4. 普通索引:index
全文索引:針對文章內部的關鍵字進行索引,全文索引最大的問題在於如何確定關鍵字。英文關鍵字很容易,英文單詞與單詞之間有空格。但是中午很難,中文沒有空格,而且中文可以各種隨意組合,中文的分詞(sphinx)最難。索引涉及到SQL優化的問題,很複雜的課題,這裡先放放。
關係就是實體與實體之間的關係,我們將這種關係分為三種:一對一,一對多,多對多。一般我們指的關係是表與表之間關係。
一對一
一張表的一條記錄一定只能與另外一張表的一條記錄進行對應,反之亦然。
下面用學生表來舉例。
學生表:姓名,性別,年齡,身高,體重,婚姻狀況,籍貫,家庭住址,緊急聯絡人。
Id(P) |
姓名 |
性別 |
年齡 |
身高 |
體重 |
婚姻狀況 |
籍貫 |
家庭住址 |
緊急聯絡人 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
表設計成以上這種形式,不是不可以,它確實符合要求。但是其中性別,年齡,體重生屬於常用資料,但是婚姻狀況,籍貫,住址,聯絡人屬於不常用資料。如果每次查詢都是查詢所有資料,那麼這些不常用資料會影響查詢效率,實際這些不常用資料又不用。這種情況,解決方案就是,將常用資料和不常用資料分離,分離成兩張表。
常用資訊表
Id(P) |
姓名 |
性別 |
年齡 |
身高 |
體重 |
|
|
|
|
|
|
|
|
|
|
|
|
不常用資訊表
婚姻狀況 |
籍貫 |
家庭住址 |
緊急聯絡人 |
|
|
|
|
|
|
|
|
如果兩張表分成這樣,那麼問題就來,如果我們需要查詢某個人的家庭住址,那麼怎麼查詢呢?也就是兩個表之間沒有關聯關係。解決辦法就是,讓不常用資訊和常用資訊一定能夠對應得上,找一個具有唯一性的欄位來共同連線兩張表。通過分析和觀察,我們可以拿ID這個欄位來將兩張表共同連線起來。
所以不常用資訊表可以改成這樣。
Id(P) |
婚姻狀況 |
籍貫 |
家庭住址 |
緊急聯絡人 |
|
|
|
|
|
|
|
|
|
|
因為id是主鍵,是唯一的(上面表中P表示主鍵,以下也是P代表主鍵),這樣兩張表就有了關係,在常用表中的一條記錄,永遠只能在不常用表中匹配一條記錄,反過來,在不常用表中一條記錄,永遠只能在常用表中匹配一條記錄,這個關係就是一對一。
一對多(多對一)
定義:一張表中有一條記錄可以對應另外一張表中的多條記錄,但是反過來,另外一張表的一條記錄只能對應第一張表的一條記錄,這種關係就是一對多,或者多對一。
下面通過一個國家有多個省份來舉例一對多的關係
國家表
ID(P) |
名稱 |
全球所屬位置 |
|
|
|
省份表
ID(P) |
名稱 |
所在國家位置 |
|
|
|
以上關係,一個國家可以在省份表中找到多條記錄(也有可能是一條),但是一個省份表永遠只能找到一個國家。這就是典型的一對多關係。但是以上設計,解決了實體的設計表問題,但是沒有解決關係問題,省份找不出國家,國家找不到省份。
解決方案:在某一張表中增加一個字,能夠找到另外一張表的中的記錄。應該在省份表中增加一個欄位指向國家表,因為省份表的記錄只能匹配到一條國家記錄。
修改後的省份表如下
ID(P) |
名稱 |
所在國家位置 |
國家ID(P) |
|
|
|
|
多對多
定義:一張表(A)中的一條記錄能夠對應另外一張表(B)中的多條記錄,同時B表中的一條記錄也能在對應A表中的多條記錄,這就是多對多關心。
以下通過教師教學,老師和學生表來設計舉例。
老師表
T_Id(P) |
姓名 |
性別 |
|
|
|
學生表
S_Id(P) |
姓名 |
性別 |
|
|
|
以上設計方案實現了實體的設計,但是沒有維護實體之間的關係。一個老師教過多個學生,一個學生也被多個老師教過,這種場景以上表就體現不了。
解決方案:不管在哪張表增加欄位,都會出現一個問題,該欄位要儲存多個數據,而且是與該表有關係的欄位,不符合表設計規範,這種情況,我們增加一個中間關係表。
具體設計如下:
老師表
T_Id(P) |
姓名 |
性別 |
|
|
|
學生表
S_Id(P) |
姓名 |
性別 |
|
|
|
中間關係表:
ID |
T_Id(老師) |
S_ID(學生) |
1 |
|
|
2 |
|
|
增加中間表之後,中間表與老師表形成一對多關係,而且中間關係表是多表。這樣就能夠唯一找到一表的關係。同樣的學生表與中間表也是一對多的關係。
學生找老師的過程:1 找出學生id-> 2中間表尋找匹配記錄(多條)-> 3老師表匹配(一條)
老師找學生過程: 1 找出學生id –> 2中間表尋找匹配記錄(多條)-> 3學生表匹配(一條)
這樣就從一對多,然後到多對一,從而就得到結果就是多對多的效果。這種多對多,在現實專案中最常見的就是電子商務網站上的訂單和會員之間的關係。
正規化(Normal Format),是一種離散數學中的知識,是為了解決一種資料的儲存與優化的問題,儲存資料的儲存之後,凡是能夠通過關係尋找出來的資料,堅決不再重複儲存終極目標是為了減少資料的冗餘。正規化是一種分層結構的規範,分為六層:每一次層都比上一層更加嚴格,若要滿足下一層正規化,前提是滿足上一層正規化。
六層正規化:1NF, 2NF, 3NF, 4NF, 5NF, 6NF, 1NF是最底層,要求最低,6NF是最高層,最嚴格。
Mysql屬於關係型資料庫,有空間浪費,也是需要考慮節省儲存空間,這個與正規化所有解決的問題不謀而合。在設計資料庫的時候,會利用正規化來指導設計。但是資料庫不單是要解決空間問題,還要保證效率問題。正規化只為解決空間問題,所以資料庫的設計又不可能完全按照正規化的要求去設計和實現,一般情況下,只有前三種正規化需要滿足。正規化只是指導意義,沒有強制規範要求。
第一正規化
定義:在設計表儲存資料的時候,如果表中設計的欄位儲存的資料,在取出來使用之前還需要額外的處理(拆分),那麼說表的設計不滿足第一正規化,第一正規化要求欄位的資料具有原子性,也就是不可拆分。
舉例下面講師表
講師 |
性別 |
班級 |
課程 |
時間 |
代課時間(開始和結束) |
張三 |
男 |
204 |
Java基礎 |
30天 |
2018-01-01,2018-01-31 |
|
|
|
|
|
|
看上面這個表格,其實是可以儲存資料,但是不符合第一正規化。理由是這樣的,如果要查詢一個老師是從什麼時間開始代課和什麼時間結束代課。這樣查詢結果應該是兩個欄位,但是當前表只能給出一個欄位,也就是查詢之後還需要拆分資料。
解決方案,把代課時間分拆兩個欄位,一個開始一個結束。
講師 |
性別 |
班級 |
課程 |
時間 |
開始 |
結束 |
張三 |
男 |
204 |
Java基礎 |
30天 |
2018-01-01 |
2018-01-31 |
|
|
|
|
|
|
|
第二正規化
定義:在資料表設計的過程中,如果有複合主鍵(多欄位主鍵),且表中有欄位並不是由整個主鍵來確定,而是依賴主鍵中的某個欄位(主鍵部分),存在欄位依賴主鍵部分的問題稱之為部分依賴,第二正規化就是解決表資料中不允許出現部分依賴。
舉例:講師代課表
講師(P) |
性別 |
教室 |
班級(P) |
時間 |
開始 |
結束 |
張三 |
男 |
204 |
Java基礎 |
30天 |
2018-01-01 |
2018-01-31 |
|
|
|
|
|
|
|
以上表中有兩個P。表示講師和班級兩個欄位組成複合主鍵約束。一個老師在一個班永遠只帶一個階段的課。代課時間,開始和結束時間都與當前的代課主鍵有關係,但是性別並不依賴班級,教室不依賴講師,性別只依賴講師,教室只依賴班級。這個就形成了部分依賴,符合第二正規化。
解決方案1:可以將性別和講師單獨成表,班級和教室單獨成表。
解決方案2:取消複合主鍵,使用邏輯主鍵。
我們來介紹方案2
ID(P) |
講師 |
性別 |
教室 |
班級 |
時間 |
開始 |
結束 |
1 |
張三 |
男 |
204 |
Java基礎 |
30天 |
2018-01-01 |
2018-01-31 |
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
當前增加了ID這個邏輯主鍵,取消講師和班級複合主鍵。
第三正規化
要滿足第三正規化,必須滿足第二正規化。理論上講,應該一張表中的所有欄位都應該直接依賴主鍵(邏輯主鍵,代表的是業務主鍵),如果表設計中存在一個欄位並不直接依賴主鍵,而是通過某個非主鍵欄位依賴,最終實現依賴主鍵,把這種不是直接依賴主鍵,而是依賴非主鍵欄位的依賴關係稱之為傳遞依賴。
舉例:講師代課表
ID(P) |
講師 |
性別 |
教室 |
班級 |
時間 |
開始 |
結束 |
1 |
張三 |
男 |
204 |
Java基礎 |
30天 |
2018-01-01 |
2018-01-31 |
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
以上表設計方案中,性別依賴講師存在,講師依賴主鍵;教室依賴班級,而班級依賴主鍵。性別和教室都存在傳遞依賴。
解決方案:將存在傳遞依賴的欄位以及依賴欄位本身單獨取出,形成單獨表,然後在需要對應的資訊的時候,使用對應實體表的主鍵加進來。
講師代課表
ID(P) |
講師ID |
班級ID |
時間 |
開始 |
結束 |
1 |
1 |
10 |
30天 |
2018-01-01 |
2018-01-31 |
2 |
2 |
20 |
|
|
|
|
|
|
|
|
|
講師表
ID |
講師 |
性別 |
1 |
張三 |
男 |
班級表
ID |
班級 |
教室 |
10 |
Java基礎 |
201 |
講師表中ID=講師,同樣班級表中ID=班級,因為講師可能存在名稱重名的,所以增加ID這個邏輯主鍵。
正規化逆規範化
有時候在設計表的時候,如果一張表中有幾個欄位是需要從另外的表中去獲取資訊,理論上講,的確可以獲取到想要的資料,但是就是查詢效率低一點,會可以的在某些表中,不去儲存另外表的主鍵(邏輯主鍵),而是直接儲存想要的資料資訊,這樣一來,在查詢資料的時候,一張表可以直接提供資料,而不需要多表查詢,但是會導致資料冗餘增加,這個方式就是正規化逆規範化。
舉例:還是講師代課表
ID(P) |
講師ID |
班級ID |
時間 |
開始 |
結束 |
1 |
張三 |
Java基礎 |
30天 |
2018-01-01 |
2018-01-31 |
2 |
李四 |
Python基礎 |
|
|
|
|
|
|
|
|
|
上面我們不儲存講師和班級ID,直接儲存講師名稱和班級名稱,這樣我們就在一張表就可以獲取查詢講師和班級資訊。
所謂逆規範化就是磁碟的利用率和查詢效率的博弈。總結,正規化只需要瞭解1NF,2NF,3NF,可以鎖第四正規化以後限制條件越多,效率反而越低。