DDL
DDL是DBMS的核心組件,是SQL的重要組成部分. DDL的正確性和穩定性是整個SQL發型的重要基礎.
DDL的基礎語法及設計工具
DDL的英文是Data Definition Language,也就是數據定義語言.定義了數據庫的結構和數據表的結構.常用的功能急救室增刪改,對應的命令分別是CREATE、DROP和ALTER.
- 對數據庫進行定義
CREATE DATABASE nba; // 創建名為nba的數據庫
DROP DATABASE nba; // 刪除名為nba的數據庫
- 對數據表進行定義
CREATE TABLE table_name; // 創建表,table_name指表名
創建表的結構呢? 舉個實際的例子, 我們創建一個球員表, 表名為player, 里面有兩個字段, 一個是player_id, 它是int類型,另一個是player_name字段是varchar(255)類型, 兩個字段都不能為空, 並且player_id是遞增的.
接下來創建表的語句這么就是:
CREATE TABLE player(
player_id int(11) NOT NULL AUTO_INCREMENT,
player_name varchar(255) NOT NULL
);
注意的是每個字段定義的語句最后使用 , 作為結束符, 最后一個字段的定義結束之后沒有逗號的 , 並且語句最后是以 ; 結尾的. 數據類型中int(11)代表整數類型, 顯示長度是11位, 括號中的參數11代表的是最大有效顯示長度, 與類型包含的數值大小無關. varchar(255)代表的是最大長度為255的可變字符串類型. NOT NULL表名整個字段不能為空值,是一種數據約束. AUTO_INCREMENT代表主鍵自動增長.(一般情況下使用可視化工具類創建和操作數據庫和數據庫表,比如Navicat)
接下來針對player表,設計下面字段:
其中player_id是數據表player的主鍵, 且自動增長, 也就是player_id會從1開始, 然后每次加一, 不必為它賦值. player_id、team_id、player_name這三個字段均不為空, height字段可以為空.
使用Navicat工具創建表並導出的SQL文件如下所示:
DROP TABLE IF EXISTS `player`;
CREATE TABLE `player`(
`player_id` int(11) NOT NULL AUTO_INCREMENT,
`team_id` int(11) NOT NULL,
`team_name` varchar(255) CHARACTER SET utf8 collate utf8_general_ci NOT NULL ,
`height` float(3,2) NULL DEFAULT0.00,
PRIMARY KEY(`player_id`) USING BTREE,
UNIQUE INDEX `player_name`(`player_name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
可以看到整個SQL文件中的DDL處理, 首先刪除player表(如果數據庫中存在該表的話), 然后再創建player表, 里面的字段名和表名都使用了反引號,這是為了避免名稱與MYSQL保留字段相同,對數據庫表和字段名都加上反引號.
其中player_name字段的字符集是utf8, 排序規則是utf8_general_ci, 代表對大小寫不敏感, 如果設置為utf8_bin, 表示對大小寫敏感.
因為player_id設置為了主鍵, 所以在DDL中使用PRIMARY KEY進行規定,同時索引方法采用BTREE.
對player_name字段進行索引, 在設置索引時, 可以設置UNIQUE INDEX(唯一索引), 也可以設置為其它索引方式, 比如NORMAL INDEX(普通索引), 這里我們采用UNIQUE INDEX. 唯一索引和普通索引的區別在於對字段進行了唯一性約束. 在索引方式上, 可以選擇BTREE和HASH, 這里采用BTREE方法進行索引.
整個數據表的存儲規則采用InnoDB, 是MYSQL5.5之后的默認存儲引擎, 將字符集設置為utf8, 排序規則設置為utf8_general_ci, 行格式為Dynamic, 就可以定義數據表的最后約定了:
ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
修改表結構
創建完表之后, 可以對表結構進行修改, 使用DDL命令來完成.
- 添加字段
ALTER TABLE player ADD (age int(11));
- 修改字段名, 將age字段改成player_age
ALTER TABLE player RENAME COLUMN age to player_age;
- 修改字段的數據類型
ALTER TABLE player MODIFT (player_age float(3,1));
- 刪除字段, 刪除剛才添加的player_age字段
ALTER TABLE player DROP CLOUMN player_age;
數據表的常見約束
在創建數據表的時候, 會對字段進行約束, 約束的目的在於保證RDBMS里面的數據的准確性和一致性.
- 主鍵約束
主鍵起的作用是唯一標識一條記錄, 不能重復, 不能為空, 即UNIQUE + NOT NULL. 一個數據表的主鍵只能有一個. 但是主鍵可以是一個字段, 也可以是多個字段符合組成. 上面的player_id就是主鍵.
- 外鍵約束
外鍵起的作用是確保表與表之間引用的完整性. 一個表中的外鍵對應了另外一張表中的主鍵. 外鍵可以重復並且可以為空. 比如player_id是player表的主鍵,如果想設置一個球員比分表player_score, 可以再player_score中設置player_id為外鍵,關聯到player表.
- 唯一約束
唯一約束就是表明字段在表中的數值唯一, 主要是對除主鍵以外的其他字段(主鍵自帶數值唯一BUFF). 上面對player_name進行了唯一性約束,也就是說球員的姓名不能相同. 注意的是唯一性約束和普通索引(NORMAL INDEX)之間的區別: 唯一性約束相當於創建了一個約束和普通索引, 目的是保證字段的正確性, 而普通索引只是提升數據檢索的速度, 並不對字段的唯一性進行約束.
- NOT NULL約束
對字段定義了NOT NULL, 表明字段不能為空, 必須有取值.
- DEFAULT
表明字段的默認值, 如果在插入數據的時候該字段沒有取值, 就設置為默認值.
- CHECK約束
用來檢查特定字段取值范圍的有效性, CHECK約束的結果不能為FALSE.
設計數據表的原則
"三少一多的原則":
- 數據表的個數越少越好
RDBMS的核心在於對實體和聯系的定義, 也就是E-R圖(Entity Relation Diagram), 數據表越少, 說明實體和聯系設計得越簡潔, 即方便理解有方便操作.
- 數據表中的字段個數越少越好
字段個數越多, 數據冗余的可能性越大. 設置字段個數少的前提是各個字段相互獨立, 而不是某個字段的取值可以由其它字段計算出來. 當然字段個數少是相對的, 通常會在數據冗余和檢索效率中進行平衡.
- 數據表中聯合主鍵的字段個數越少越好
設置主鍵是為了確定唯一性, 當一個字段無法確定唯一性, 就需要采用聯合主鍵的方式. 聯合主鍵中的字段越多, 占用的所以索引空間越大, 會加大理解難度, 會增加運行時間和索引空間.
- 使用主鍵和外鍵越多越好
數據庫的設計實際上就是定義各種表, 一級各種字段間的關系, 關系越多, 證明實體之間的冗余度越低, 利用度越高, 這樣做的好處在於不僅保證數據表之間的獨立性, 還能提升相互之間的關聯使用率. (不過在我現在的公司, 基本上沒有使用外鍵, 不知道是因為影響效率還是什么, 外鍵的意義起不到作用)
作者的意思是大型項目中后期,可以采用業務層來實現,取消外鍵提高效率。不過在SQL學習之初,包括在系統最初設計的時候,還是建議你采用規范的數據庫設計,也就是采用外鍵來對數據表進行約束。因為這樣可以建立一個強一致性,可靠性高的數據庫結構,也不需要在業務層來實現過多的檢查。當然在項目后期,業務量增大的情況下,你需要更多考慮到數據庫性能問題,可以取消外鍵的約束,轉移到業務層來實現。而且在大型互聯網項目中,考慮到分庫分表的情況,也會降低外鍵的使用。
建議是 不過在SQL學習,以及項目早期,還是建議你使用外鍵。在項目后期,你可以分析有哪些外鍵造成了過多的性能消耗。一般遵循2/8原則,會有20%的外鍵造成80%的資源效率,你可以只把這20%的外鍵進行開放,采用業務層邏輯來進行實現,當然你需要保證業務層的實現沒有錯誤。不同階段,考慮的問題不同。當用戶和業務量增大的時候,對於大型互聯網應用,也會通過減少外鍵的使用,來減低死鎖發生的概率,提高並發處理能力。