SQL Server 數(shù)據(jù)庫(kù)表設(shè)計(jì)的18條軍規(guī)
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
前言對(duì)于后端開(kāi)發(fā)同學(xué)來(lái)說(shuō),訪問(wèn)數(shù)據(jù)庫(kù),是代碼中必不可少的一個(gè)環(huán)節(jié)。 系統(tǒng)中收集到用戶的核心數(shù)據(jù),為了安全性,我們一般會(huì)存儲(chǔ)到數(shù)據(jù)庫(kù),比如:mysql,oracle等。 后端開(kāi)發(fā)的日常工作,需要不斷的建庫(kù)和建表,來(lái)滿足業(yè)務(wù)需求。 通常情況下,建庫(kù)的頻率比建表要低很多,所以,我們這篇文章主要討論建表相關(guān)的內(nèi)容。 如果我們?cè)诮ū淼臅r(shí)候不注意細(xì)節(jié),等后面系統(tǒng)上線之后,表的維護(hù)成本變得非常高,而且很容易踩坑。 今天就跟大家一起聊聊,數(shù)據(jù)庫(kù)建表的18個(gè)小技巧。 文章中介紹的很多細(xì)節(jié),我在工作中踩過(guò)坑,并且實(shí)踐過(guò)的,非常有借鑒意義,希望對(duì)你會(huì)有所幫助。 1.名字建表的時(shí)候,給 1.1 見(jiàn)名知意名字就像 好的名字,言簡(jiǎn)意賅,見(jiàn)名知意,讓人心情愉悅,能夠提高溝通和維護(hù)成本。 壞的名字,模擬兩可,不知所云。而且顯得雜亂無(wú)章,看得讓人抓狂。 反例:
你看了可能會(huì)一臉懵逼,這是什么騷操作? 正例:
1.2 大小寫名字盡量都用 反例:
全部大寫,看起來(lái)有點(diǎn)不太直觀。而一部分大寫,一部分小寫,讓人看著更不爽。 正例:
名字還是使用全小寫字母,看著更舒服。 1.3 分隔符很多時(shí)候,名字為了讓人好理解,有可能會(huì)包含多個(gè)單詞。 那么,多個(gè)單詞間的 反例:
單詞間沒(méi)有分隔,或者單詞間用駝峰標(biāo)識(shí),或者單詞間用空格分隔,或者單詞間用@分隔,這幾種方式都不太建議。 正例:
強(qiáng)烈建議大家在單詞間用 1.4 表名對(duì)于表名,在言簡(jiǎn)意賅,見(jiàn)名知意的基礎(chǔ)之上,建議帶上 如果是訂單相關(guān)的業(yè)務(wù)表,可以在表名前面加個(gè)前綴: 例如:order_pay、order_pay_detail等。 如果是商品相關(guān)的業(yè)務(wù)表,可以在表名前面加個(gè)前綴: 例如:product_spu,product_sku等。 這樣做的好處是為了方便歸類,把相同業(yè)務(wù)的表,可以非常快速的聚集到一起。 另外,還有有個(gè)好處是,如果哪天有非訂單的業(yè)務(wù),比如:金融業(yè)務(wù),也需要建一個(gè)名字叫做pay的表,可以取名:finance_pay,就能非常輕松的區(qū)分。 這樣就不會(huì)出現(xiàn) 1.5 字段名稱
比如有些表,使用flag表示狀態(tài),另外的表用status表示狀態(tài)。 可以統(tǒng)一一下,使用status表示狀態(tài)。 如果一個(gè)表使用了另一個(gè)表的主鍵,可以在另一張表的名后面,加 在product_sku表中有個(gè)字段,是product_spu表的主鍵,這時(shí)候可以取名:product_spu_id或product_spu_sys_no。 還有創(chuàng)建時(shí)間,可以統(tǒng)一成:create_time,修改時(shí)間統(tǒng)一成:update_time。 刪除狀態(tài)固定為:delete_status。 其實(shí)還有很多公共字段,在不同的表之間,可以使用全局統(tǒng)一的命名規(guī)則,定義成相同的名稱,以便于大家好理解。 1.6 索引名在數(shù)據(jù)庫(kù)中,索引有很多種,包括:主鍵、普通索引、唯一索引、聯(lián)合索引等。 每張表的主鍵只有一個(gè),一般使用: 普通索引和聯(lián)合索引,其實(shí)是一類。在建立該類索引時(shí),可以加 唯一索引,可以加 2.字段類型在設(shè)計(jì)表時(shí),我們?cè)谶x擇 時(shí)間格式的數(shù)據(jù)有:date、datetime和timestamp等等可以選擇。 字符類型的數(shù)據(jù)有:varchar、char、text等可以選擇。 數(shù)字類型的數(shù)據(jù)有:int、bigint、smallint、tinyint等可以選擇。 說(shuō)實(shí)話,選擇很多,有時(shí)候是一件好事,也可能是一件壞事。 如何選擇一個(gè) 如果字段類型選大了,比如:原本只有1-10之間的10個(gè)數(shù)字,結(jié)果選了 其實(shí),1-10之間的10個(gè)數(shù)字,每個(gè)數(shù)字 這樣會(huì)白白浪費(fèi)7個(gè)字節(jié)的空間。 如果字段類型擇小了,比如:一個(gè)18位的id字段,選擇了 所以選擇一個(gè)合適的字段類型,還是非常重要的一件事情。 以下原則可以參考一下:
3.字段長(zhǎng)度前面我們已經(jīng)定義好了 比如:varchar(20),biginit(20)等。 那么問(wèn)題來(lái)了, 答:在mysql中除了 biginit(n) 這個(gè) 假如我們定義的字段類型和長(zhǎng)度是:bigint(4),bigint實(shí)際長(zhǎng)度是 現(xiàn)在有個(gè)數(shù)據(jù)a=1,a顯示4個(gè)字節(jié),所以在不滿4個(gè)字節(jié)時(shí)前面填充0(前提是該字段設(shè)置了zerofill屬性),比如:0001。 當(dāng)滿了4個(gè)字節(jié)時(shí),比如現(xiàn)在數(shù)據(jù)是a=123456,它會(huì)按照實(shí)際的長(zhǎng)度顯示,比如:123456。 但需要注意的是,有些mysql客戶端即使?jié)M了4個(gè)字節(jié),也可能只顯示4個(gè)字節(jié)的內(nèi)容,比如會(huì)顯示成:1234。 所以bigint(4),這里的4表示顯示的長(zhǎng)度為4個(gè)字節(jié),實(shí)際長(zhǎng)度還是占8個(gè)字節(jié)。 4.字段個(gè)數(shù)我們?cè)诮ū淼臅r(shí)候,一定要對(duì) 我之前見(jiàn)過(guò)有人創(chuàng)建的表,有幾十個(gè),甚至上百個(gè)字段,表中保存的數(shù)據(jù)非常大,查詢效率很低。 如果真有這種情況,可以將一張 建議每表的字段個(gè)數(shù),不要超過(guò) 5. 主鍵在創(chuàng)建表時(shí),一定要?jiǎng)?chuàng)建 因?yàn)橹麈I自帶了主鍵索引,相比于其他索引,主鍵索引的查詢效率最高,因?yàn)樗恍枰乇怼?/p> 此外,主鍵還是天然的 在 但在 除此之外,主鍵建議保存跟業(yè)務(wù)無(wú)關(guān)的值,減少業(yè)務(wù)耦合性,方便今后的擴(kuò)展。 不過(guò)我也見(jiàn)過(guò),有些一對(duì)一的表關(guān)系,比如:用戶表和用戶擴(kuò)展表,在保存數(shù)據(jù)時(shí)是一對(duì)一的關(guān)系。 這樣,用戶擴(kuò)展表的主鍵,可以直接保存用戶表的主鍵。 6.存儲(chǔ)引擎在 之前我們還在創(chuàng)建表時(shí),還一直糾結(jié)要選哪種存儲(chǔ)引擎?
而 以前的建議是:讀多寫少的表,用myisam存儲(chǔ)引擎。而寫多讀多的表,用innodb。 但雖說(shuō)mysql對(duì)innodb存儲(chǔ)引擎性能的不斷優(yōu)化,現(xiàn)在myisam和innodb查詢性能相差已經(jīng)越來(lái)越小。 所以,建議我們?cè)谑褂?code style="margin: 0px 2px;padding: 2px 4px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(40, 202, 113);line-height: 1.8em;letter-spacing: 0em;background: none 0% 0% / auto no-repeat scroll padding-box border-box rgba(27, 31, 35, 0.05);width: auto;height: auto;border-style: none;border-width: 3px;border-color: rgb(0, 0, 0) rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.4);border-radius: 4px;font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace">mysql8以后的版本時(shí),直接使用默認(rèn)的 7. NOT NULL在創(chuàng)建字段時(shí),需要選擇該字段是否允許為 我們?cè)诙x字段時(shí),應(yīng)該盡可能明確該字段 為什么呢? 我們主要以innodb存儲(chǔ)引擎為例,myisam存儲(chǔ)引擎沒(méi)啥好說(shuō)的。 主要有以下原因:
因此,建議我們?cè)诙x字段時(shí),能定義成NOT NULL,就定義成NOT NULL。 但如果某個(gè)字段直接定義成NOT NULL,萬(wàn)一有些地方忘了給該字段寫值,就會(huì) 這也算合理的情況。 但有一種情況是,系統(tǒng)有新功能上線,新增了字段。上線時(shí)一般會(huì)先執(zhí)行sql腳本,再部署代碼。 由于老代碼中,不會(huì)給新字段賦值,則insert數(shù)據(jù)時(shí),也會(huì)報(bào)錯(cuò)。 由此,非常有必要給NOT NULL的字段設(shè)置默認(rèn)值,特別是后面新增的字段。 例如:
8.外鍵在mysql中,是存在 外鍵存在的主要作用是:保證數(shù)據(jù)的 例如:
有個(gè)班級(jí)表class。 然后有個(gè)student表:
其中student表中的cid字段,保存的class表的id,這時(shí)通過(guò) 這時(shí),如果你直接通過(guò)student表的id刪除數(shù)據(jù),會(huì)報(bào)異常:
必須要先刪除class表對(duì)于的cid那條數(shù)據(jù),再刪除student表的數(shù)據(jù)才行,這樣能夠保證數(shù)據(jù)的一致性和完整性。
如果只有兩張表的關(guān)聯(lián)還好,但如果有十幾張表都建了外鍵關(guān)聯(lián),每刪除一次主表,都需要同步刪除十幾張子表,很顯然性能會(huì)非常差。 因此,互聯(lián)網(wǎng)系統(tǒng)中,一般建議不使用外鍵。因?yàn)檫@類系統(tǒng)更多的是為了性能考慮,寧可犧牲一點(diǎn)數(shù)據(jù)一致性和完整性。 除了 9. 索引在建表時(shí),除了指定 例如:
在創(chuàng)建商品表時(shí),使用spu_id(商品組表)和brand_id(品牌表)的id。 像這類保存其他表id的情況,可以增加普通索引:
后面查表的時(shí)候,效率更高。 但索引字段也不能建的太多,可能會(huì)影響保存數(shù)據(jù)的效率,因?yàn)樗饕枰~外的存儲(chǔ)空間。 建議單表的索引個(gè)數(shù)不要超過(guò): 如果在建表時(shí),發(fā)現(xiàn)索引個(gè)數(shù)超過(guò)5個(gè)了,可以刪除部分 順便說(shuō)一句:在創(chuàng)建聯(lián)合索引的時(shí)候,需要使用注意 對(duì)于數(shù)據(jù)重復(fù)率非常高的字段,比如:狀態(tài),不建議單獨(dú)創(chuàng)建普通索引。因?yàn)榧词辜恿怂饕绻鹠ysql發(fā)現(xiàn) 如果你對(duì)索引失效問(wèn)題比較感興趣,可以看看我的另一篇文章《聊聊索引失效的10種場(chǎng)景,太坑了》,里面有非常詳細(xì)的介紹。 10.時(shí)間字段
但如果哪天我們要通過(guò)時(shí)間范圍查詢數(shù)據(jù),效率會(huì)非常低,因?yàn)檫@種情況沒(méi)法走索引。
而 但它們有略微區(qū)別。
優(yōu)先推薦使用
11.金額字段mysql中有多個(gè)字段可以表示浮點(diǎn)數(shù):float、double、decimal等。 而 一般我們是這樣定義浮點(diǎn)數(shù)的:decimal(m,n)。 其中 假如我們定義的金額類型是這樣的:decimal(10,2),則表示整數(shù)長(zhǎng)度是8位,并且保留2位小數(shù)。 12. json字段我們?cè)谠O(shè)計(jì)表結(jié)構(gòu)時(shí),經(jīng)常會(huì)遇到某個(gè)字段保存的數(shù)據(jù)值不固定的需求。 舉個(gè)例子,比如:做異步excel導(dǎo)出功能時(shí),需要在異步任務(wù)表中加一個(gè)字段,保存用戶通過(guò)前端頁(yè)面選擇的查詢條件,每個(gè)用戶的查詢條件可能都不一樣。 這種業(yè)務(wù)場(chǎng)景,使用傳統(tǒng)的數(shù)據(jù)庫(kù)字段,不太好實(shí)現(xiàn)。 這時(shí)候就可以使用MySQL的json字段類型了,可以保存json格式的結(jié)構(gòu)化數(shù)據(jù)。 保存和查詢數(shù)據(jù)都是非常方便的。 MySQL還支持按字段名稱或者字段值,查詢json中的數(shù)據(jù)。 13.唯一索引
你可以給單個(gè)字段,加唯一索引,比如:組織機(jī)構(gòu)code。 也可以給多個(gè)字段,加一個(gè)聯(lián)合的唯一索引,比如:分類編號(hào)、單位、規(guī)格等。 單個(gè)的唯一索引還好,但如果是聯(lián)合的唯一索引,字段值出現(xiàn)null時(shí),則唯一性約束可能會(huì)失效。 關(guān)于唯一索引失效的問(wèn)題,感興趣的小伙伴可以看看我的另一篇文章《明明加了唯一索引,為什么還是產(chǎn)生重復(fù)數(shù)據(jù)?》。
14.字符集mysql中支持的 這4種字符集情況如下:
而 從目前來(lái)看,mysql的字符集使用最多的還是: 其中 但utf-8有個(gè)問(wèn)題:即無(wú)法存儲(chǔ)emoji表情,因?yàn)閑moji表情一般需要4個(gè)字節(jié)。 由此,使用utf-8字符集,保存emoji表情時(shí),數(shù)據(jù)庫(kù)會(huì)直接報(bào)錯(cuò)。 所以,建議在建表時(shí)字符集設(shè)置成: 15. 排序規(guī)則不知道,你關(guān)注過(guò)沒(méi),在mysql中創(chuàng)建表時(shí),有個(gè) 例如:
它是用來(lái)設(shè)置 字符排序規(guī)則跟字符集有關(guān),比如:字符集如果是 其中utf8mb4_general_ci排序規(guī)則,對(duì)字母的大小寫不敏感。說(shuō)得更直白一點(diǎn),就是不區(qū)分大小寫。 而utf8mb4_bin排序規(guī)則,對(duì)字符大小寫敏感,也就是區(qū)分大小寫。 說(shuō)實(shí)話,這一點(diǎn)還是非常重要的。 假如order表中現(xiàn)在有一條記錄,name的值是大寫的YOYO,但我們用小寫的yoyo去查,例如:
如果字符排序規(guī)則是utf8mb4_general_ci,則可以查出大寫的YOYO的那條數(shù)據(jù)。 如果字符排序規(guī)則是utf8mb4_bin,則查不出來(lái)。 由此,字符排序規(guī)則一定要根據(jù)實(shí)際的業(yè)務(wù)場(chǎng)景選擇,否則容易出現(xiàn)問(wèn)題。 16.大字段我們?cè)趧?chuàng)建表時(shí),對(duì)一些特殊字段,要額外關(guān)注,比如: 比如:用戶的評(píng)論,這就屬于一個(gè)大字段,但這個(gè)字段可長(zhǎng)可短。 但一般會(huì)對(duì)評(píng)論的總長(zhǎng)度做限制,比如:最多允許輸入500個(gè)字符。 如果直接定義成 當(dāng)然,我還見(jiàn)過(guò)更大的字段,即該字段直接保存合同數(shù)據(jù)。 一個(gè)合同可能會(huì)占 在mysql中保存這種數(shù)據(jù),從系統(tǒng)設(shè)計(jì)的角度來(lái)說(shuō),本身就不太合理。 像合同這種非常大的數(shù)據(jù),可以保存到 17.冗余字段我們?cè)谠O(shè)計(jì)表的時(shí)候,為了性能考慮,提升查詢速度,有時(shí)可以冗余一些字段。 舉個(gè)例子,比如:訂單表中一般會(huì)有userId字段,用來(lái)記錄用戶的唯一標(biāo)識(shí)。 但很多訂單的查詢頁(yè)面,或者訂單的明細(xì)頁(yè)面,除了需要顯示訂單信息之外,還需要顯示用戶ID和用戶名稱。 如果訂單表和用戶表的數(shù)據(jù)量不多,我們可以直接用userId,將這兩張表join起來(lái),查詢出用戶名稱。 但如果訂單表和用戶表的數(shù)據(jù)量都非常多,這樣join是比較消耗查詢性能的。 這時(shí)候我們可以通過(guò)冗余字段的方案,來(lái)解決性能問(wèn)題。 我們可以在訂單表中,可以再加一個(gè)userName字段,在系統(tǒng)創(chuàng)建訂單時(shí),將userId和userName同時(shí)寫值。 當(dāng)然訂單表中歷史數(shù)據(jù)的userName是空的,可以刷一下歷史數(shù)據(jù)。 這樣調(diào)整之后,后面只需要查詢訂單表,即可查詢出我們所需要的數(shù)據(jù)。 不過(guò)冗余字段的方案,有利也有弊。 對(duì)查詢性能有利。 但需要額外的存儲(chǔ)空間,還可能會(huì)有數(shù)據(jù)不一致的情況,比如用戶名稱修改了。 我們?cè)趯?shí)際業(yè)務(wù)場(chǎng)景中,需要綜合評(píng)估,冗余字段方案不適用于所有業(yè)務(wù)場(chǎng)景。 18.注釋我們?cè)谧霰碓O(shè)計(jì)的時(shí)候,一定要把表和相關(guān)字段的注釋加好。 例如下面這樣的:
表和字段的注釋,都列舉的非常詳細(xì)。 特別是有些狀態(tài)類型的字段,比如:valid_status字段,該字段表示有效狀態(tài), 1:有效 0:無(wú)效。 讓人可以一目了然,表和字段是干什么用的,字段的值可能有哪些。 最怕的情況是,你在表中創(chuàng)建了很多status字段,每個(gè)字段都有1、2、3、4、5、6、7、8、9等多個(gè)值。 沒(méi)有寫什么注釋。 誰(shuí)都不知道1代表什么含義,2代表什么含義,3代表什么含義。 可能剛開(kāi)始你還記得。 但系統(tǒng)上線使用一年半載之后,可能連你自己也忘記了這些status字段,每個(gè)值的具體含義了,埋下了一個(gè)巨坑。 由此,我們?cè)谧霰碓O(shè)計(jì)時(shí),一定要寫好相關(guān)的注釋,并且經(jīng)常需要更新這些注釋。 該文章在 2024/4/9 23:51:22 編輯過(guò) |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |