MySQL PK,FK,视图,索引,引擎总结【12000字概览MySQL】用于查漏补缺
Java養成計劃----學習打卡第七十四,五,六天
內容導航
- 主鍵的注意要點
- 外鍵約束 foreign key FK
- 父子的順序
- 外鍵約束的創建格式
- 存儲引擎
- SHOW ENGINES\G
- 常用引擎
- MYISAM引擎
- InnoDB引擎 --- 支持事務保證安全
- MEMORY引擎
- 事務
- 事務提交COMMIT
- 事務回滾ROLLBACK
- 事務的特性ACID
- 事務的隔離性
- 事務的隔離級別
- 查看事務的隔離級別 SELECT @@TRANSACTION_ISOLATION
- 設置事務的隔離級別SET GLOBAL TRANSACTION ISOLATION LEVEL ……
- GLOBAL和SESSION和缺省
- 索引
- 索引的實現原理
- 添加索引的情況
- 如何添加索引和刪除索引
- 查看是否使用了索引
- 索引失效
- 索引的分類
- 視圖view
- 創建視圖 CREATE VIEW view_name AS SELECT ……
- 視圖的作用
- DBA命令
- 導出數據庫到文件
- 數據庫設計范式
- 數據庫設計第一范式 PK,atomicity
- 數據庫第二范式 : 非PK字段完全依賴PK
- 數據庫第三范式 : 直接依賴PK,不傳遞依賴
- MySQL設計原則
- MySQL搜索題
- Q.哪些人的薪水在部門平均薪水之上
- Q.取得公司中所有人的薪水等級平均值
Java(打卡第七十四,五,六天)
主要內容: 主鍵PK,外鍵,引擎,事務,索引,視圖,數據庫范式,MySQL題目
在大概將內容過一遍之后,會詳細就一些細節進行分析,當然,還有JDBC的內容,使用JDBC連接MySQL數據庫來創建項目,之后的java項目就會運用數據庫來進行管理數據
表級約束主要是對于多個字段聯合起來添加約束,可以使用表級約束給一個字段添加主鍵約束,也可以同時添加多個字段,也就是復合主鍵,復合主鍵就是將字段聯合起來共同識別重復;
在實際看法中,盡量采用單一主鍵,因為主鍵是記錄的唯一標識,單一主鍵容易達到,復合主鍵達到較為復雜
主鍵的注意要點
主鍵的數量只能為一個: 定義中就強調了是唯一標識,所以一個記錄,只能有一個主鍵,可以看一下多個主鍵的報錯
mysql> CREATE TABLE temp(-> name VARCHAR(25) PRIMARY KEY,-> sex CHAR(1) PRIMARY KEY-> ); ERROR 1068 (42000): Multiple primary key definedERROR 1068 (42000): Multiple primary key defined ----- 定義多個主鍵
復合主鍵也是一個主鍵,只是約束時將復合的字段看成一個字段操縱
也就是說: 一張表只能一個主鍵
主鍵值的類型定長 : 主鍵值的類型盡量為int,bigint,char等類型 ;不建議使用varchar類型的來做主鍵,主鍵值一般是數字,一般是定長的。
主鍵的分類:自然和業務
- 主鍵除了按照字段是否聯合分為單一主鍵和業務主鍵
- 按照主鍵值類型也可以分為自然主鍵和業務主鍵
- 自然主鍵: 主鍵是一個自然數,和業務沒有關系,沒有實際的意義
- 業務主鍵: 主鍵值和業務緊密關聯,例如銀行賬號為主鍵
在實際開發中,使用自然主鍵較多,因為主鍵只要做到不重復就可以,不需要由意義,所以world表和film表的主鍵類型都是自然主鍵;業務主鍵不好 ----- 主鍵和業務關系緊密,一旦業務變動,可能會影響主鍵值,所以盡量 使用自然主鍵
AUTO_INCREMENT: 自然主鍵自動遞增,就不需要專門來進行字段的插入了
mysql> CREATE TABLE t_student(-> ID INT PRIMARY KEY AUTO_INCREMENT,-> name VARCHAR(25)-> ); Query OK, 0 rows affected (0.04 sec)mysql> INSERT INTO t_student (name) VALUES ('Li'); Query OK, 1 row affected (0.01 sec)mysql> INSERT INTO t_student (name) VALUES ('Di'); Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO t_student (name) VALUES ('Linda'); Query OK, 1 row affected (0.01 sec)mysql> INSERT INTO t_student (name) VALUES ('Wang'); Query OK, 1 row affected (0.01 sec)mysql> SELECT * FROM t_student; +----+-------+ | ID | name | +----+-------+ | 1 | Li | | 2 | Di | | 3 | Linda | | 4 | Wang | +----+-------+ 4 rows in set (0.00 sec)當加上了auto_increment之后,自然主鍵INT類型的就可以進行自然的遞增了,這個時候插入數據就可以不用再寫ID字段了
外鍵約束 foreign key FK
外鍵約束: 一種約束
外鍵字段: 使用外鍵約束修飾的字段
外鍵值: 外鍵字段下的每一個記錄
這里引入外鍵約束,來一個例子,就是設計數據庫表來存儲學生和其班級的信息
學生信息包含學號和姓名,班級信息包含班級編號和班級名稱
- 第一種方案: 設計成一張表來存儲t_student
這種方案的缺點就是數據大量重復,造成空間的浪費,數據冗余,設計是失敗的
- 第二種方案,設計成為兩張表
t_class
classno classname --------------------------- 100 英才一班 101 英才二班t_student
no name -------------- 1 Lucy 2 JACK 3 Wang 4 Liu 5 Linda 6 Zhang 7 Sun 8 Ge 9 Hu 10 Hong現在的數據重復最少,空間沒有浪費,但是有了一個新的問題: 兩個表沒有任何的關系,沒有聯結點
之前的emp表和dept表的連接點就是都有deptno,有一個字段可以產生關系,這里沒有產生任何關系,那么也就要讓它們產生關系,這里也就新加一個字段cno代表班級編號【簡單為依據】
no name cno (FK引用t_class種的classno) ---------------------------- 1 Lucy 100 2 JACK 100 3 Wang 100 4 Liu 100 5 Linda 100 6 Zhang 101 7 Sun 101 8 Ge 101 9 Hu 101 10 Hong 101現在兩張表就順利產生關聯了,雖然有了重復,單相比第一張表來說重復已經大量減少了
- 背景介紹完了,問題就是如果數據龐大t_student表的cno字段的值可能會寫誤,在t_class表中沒有記錄,這樣表連接就出現問題? 那么如何避免這種情況
這時,就可以使用外鍵約束了FK,加了FK,那么該字段值只能為關聯字段的值域中的值,不能為其它值
給cno加上外鍵約束之后,cno就是外鍵字段,cno字段中的每一個值就是外鍵值
注意: 添加外鍵后,兩張表就具有了父子關系,被引用的表為父表,引用的表為子表
這里t_class就是父表,t_student就是子表
父子的順序
先有支持的,才能使用,父給子提供支持
- 創建表,插入數據的順序為 : 先創建父表,再創建子表
- 刪除表,刪除數據的順序 : 先刪除子表,再刪除父表
外鍵約束的創建格式
外鍵約束這里就采用表級約束的方式來創建約束
FOREIGN KEY(col_name) REFERENCES table_name(col_name2)這里表示的就是col_name和table_name表中的col_name2字段關聯,給col_name字段加上了外鍵
這里可以實現一下上面的表
mysql> CREATE TABLE t_class(-> classno INT PRIMARY KEY,-> classname VARCHAR(25)-> ); Query OK, 0 rows affected (0.04 sec)mysql> CREATE TABLE t_student(-> id INT PRIMARY KEY,-> name VARCHAR(25),-> cno INT,-> FOREIGN KEY(cno) REFERENCES t_class(classno)-> ); Query OK, 0 rows affected (0.04 secFOREIGN KEY(cno) REFERENCES t_class(classno) 就是加上外鍵,同時記得給每一張 表加上主鍵,沒有主鍵的表沒有意義
加了外鍵約束的表,父表的刪除就不能使用TRUNCATE,只能使用DELETE FROM
mysql> TRUNCATE t_class; ERROR 1701 (42000): Cannot truncate a table referenced in a foreign key constraint (`cfengbase`.`t_student`, CONSTRAINT `t_student_ibfk_1`) mysql> DELETE FROM t_class; Query OK, 2 rows affected (0.01 sec)mysql> SELECT * FROM t_class; Empty set (0.00 sec)annot truncate a table referenced in a foreign key constraint (cfengbase.t_student, CONSTRAINT t_student_ibfk_1)
這里可以實驗一下加了外鍵約束的效果
mysql> UPDATE t_class SET classname = 'Class one' WHERE classno = 100; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0mysql> SELECT * FROM t_class; +---------+-----------+ | classno | classname | +---------+-----------+ | 100 | Class one | | 101 | Class two | +---------+-----------+ 2 rows in set (0.01 sec)mysql> INSERT INTO t_student (id,name,cno) VALUES (1,'Linda',100); Query OK, 1 row affected (0.02 sec)mysql> INSERT INTO t_student (id,name,cno) VALUES (2,'Wang',102); ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`cfengbase`.`t_student`, CONSTRAINT `t_student_ibfk_1` FOREIGN KEY (`cno`) REFERENCES `t_class` (`classno`))ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (cfengbase.t_student, CONSTRAINT t_student_ibfk_1 FOREIGN KEY (cno) REFERENCES t_class (classno))
這里的cno只能為classno值域中的值,所以這里的只能為100或者101,這里102就報錯了
- 注意: 外鍵值可以為NULL,NULL代表啥都沒有,沒有在值域之外
- 注意: 外鍵被引用的字段,這里的classno不一定是主鍵,但是一定要有UNIQUE約束,因為匹配不能匹配多個數據,只能一個
存儲引擎
存儲引擎是MySQL中的一個術語,Oracle中也有,但是叫法不同
- 存儲引擎其實就是一個表存儲/組織數據的方式
- 不同的存儲引擎,表存儲數據的方式不同
那如何給表增加一個存儲引擎呢?
首先我們看一下表的詳細結構
這里有一個語句ENGINE = InnoDB,這就是引擎;還有一個是CHARSET ,表明這張表的字符編碼方式
在建立表的時候,在最后的小括號‘(’后面可以使用 ENGINE = ……來指定引擎ENGINE 來指定引擎,CHARSET來指定表的字符編碼方式
mysql> CREATE TABLE temp(-> id int PRIMARY KEY AUTO_INCREMENT,-> name VARCHAR(25)-> )ENGINE = InnoDB,CHARSET = utf8b4; ERROR 1115 (42000): Unknown character set: 'utf8b4' mysql> CREATE TABLE temp(-> id int PRIMARY KEY AUTO_INCREMENT,-> name VARCHAR(25)-> )ENGINE = InnoDB,CHARSET = utf8mb4; Query OK, 0 rows affected (0.05 sec)==MySQL默認的引擎是InnoDB,默認的字符編碼方式為utf8mb4
SHOW ENGINES\G
該命令可以查看mysql支持那些存儲引擎,也可以將\G去掉
mysql> SHOW ENGINES; +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | Engine | Support | Comment | Transactions | XA | Savepoints | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO | | MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO | | CSV | YES | CSV storage engine | NO | NO | NO | | FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL | | PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO | | MyISAM | YES | MyISAM storage engine | NO | NO | NO | | InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES | | BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO | | ARCHIVE | YES | Archive storage engine | NO | NO | NO | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ 9 rows in set (0.00 sec)可以看到有9個存儲引擎可以使用,其中InnoDB是默認的,版本不同,支持的情況不同,這里8.0.27支持8個
常用引擎
MYISAM引擎
引擎不同,表的組織方式不同,MYISAM引擎組織的表的特征是
-
使用三個文件表示每一個表
- 格式文件 ----- 存儲表結構的定義(mytable.frm)
- 數據文件 ------ 存儲表行的內同(mytable.MYD)
- 索引文件 ----- 存儲表上索引 (mytable.MYI):索引就對應書的目錄,所以可以縮小掃描范圍,提高查詢效率; 對一張表來說,只要是主鍵或者加了UNIQUE的都可以自動創建索引
-
靈活處理AUTO_INCREMENT修飾的字段
-
可被轉為壓縮、只讀表來節省存儲空間 ----- 就是可以節省空間
InnoDB引擎 — 支持事務保證安全
InnoDB是MySQL默認的引擎,其特點是 支持事務,支持數據庫崩潰后自動恢復機制,所以其特點就是安全
效率較低以保證數據安全,但是不能壓縮,不能轉化為只讀,不能很好的節省存儲空間
它管理的表有以下特點
- 每一個InnoDB表在數據庫目錄中只有一個文件,為.frm格式文件【也是只有結構】
- InnoDB表空間tablespace被用于存儲表的內容 表空間tablespace是一個邏輯名稱,其中存數據,索引
- 提供用來記錄事務性活動的日志文件 ---- 安全
- 用COMMIT提交,ROLLBACK,SAVEPOINT 支持事務處理
- 全ACID兼容
- 在MySQL服務器崩潰后自動恢復
- 多版本和行級鎖定
- 支持外鍵和引用的完整新,包括級聯刪除和更新
MEMORY引擎
使用MEMORY存儲引擎管理的表具有以下特征
- 在數據庫目錄中,每個表都以.frm格式的文件表示
- 表數據和索引被存儲在內存中 ------ 目的就是快,查詢快
- 表級鎖機制
- 不能包含TEXT或BLOB字段
不需要和硬盤交互
MEMORY存儲引擎之前被稱為HELP 引擎, 其優點就是查詢效率最高, 缺點就是—極度不安全,數據關機之后消失,因為數據和索引都是內存中
內存是直接抽取, 而從硬盤上取是機械行為
事務
- 之前一直都提到事務保證安全,那么是什么是事務呢?
? 一個事務其實就是一個完整的業務邏輯,是一個最小的工作單元,不可再分
- 什么是完整的業務邏輯?
比如張三轉賬給李四3萬元 ,那么這個業務分為兩個部分 : 張三賬戶減少3萬元; 李四賬戶加上3萬元;這就是一個完整的業務邏輯
上面的操作都是最小的工作單元,不可以再劃分,幾個操作要么同時成功,要么同時失敗,才能保證業務的正確性,比如上面的轉賬,可以看成兩個update語句,這兩個語句要么同時成功,要么同時失敗,不然賬戶的余額就不準確了
- 注意 :只有DML語句才有事務的說法,其他的語句和事務無關,也就是UPDATE,INSERT,DELETE!
因為只有DML語句才是對表中的數據進行增刪改,只要操作涉及數據的增刪改,那么就要考慮數據的安全的,因為數據的重要性不言而喻
這也就是為什么TRUNCATE不支持事務,DELETE支持事務
- 事務的本質
假設所有的業務只需要一條DML語句就可以完成,那么就可以不存在事務了,正式因為需要多條DML語句配合,所以事務才有存在的價值,其實事務就是一個機制 : 多條【批量】DML語句同時成功或者同時失敗
- 事務如何保證多條語句同時成功?
上面提到InnoDB引擎提供了一組用來記錄事務性活動的日志文件,
這是什么意思?
比如現在要完成對數據的操縱事務開啟 insert insert update update delete 事務結束在該事務執行的過程中,每一條DML的操作都會記錄到事務性活動的日志文件中 在事務執行過程中,可以提交事務,也可以回滾事務- 事務提交 COMMIT
? 清空事務性活動的日志文件,將數據全部徹底持久化到數據庫表中
? 清空事務標志著事務結束,并且是一種全都成功的結束
- 事務回滾 ROLLBACK
? 將之前的所有DML操作撤銷,并且清空事務性活動的日志文件
? 回滾也標志著事務的結束,是全部失敗的結束
事務提交COMMIT
mysql默認情況下是默認支持事務提交的,自動提交不符合開發要求,因為業務一般不是一條DML語句解決,所以正常的情況下,需要使用START TRANSACTION來結束默認機制
也就是說,如果不使用TCL,那么Mysql就是每執行一條DML語句就會執行一次事務提交
所以之前的操作,每次執行之后都會顯示ok,這時事務日志也就清空了,操作就是不能rollback的
因為rollback只能返回到上次的事務提交點
關閉自動提交機制就可以使用start transaction
START TRANSACTION;mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec)事務回滾ROLLBACK
事務回滾也是一個事務的結束,事務就是一個完整的業務邏輯,因為COMMIT也是一個事務的結束,一個事務的開啟時START TRANSACTION,結束之后如果不開啟事務,又回回到默認的執行一條語句就提交一次
這里完整演示一下
//開始事務和提交事務 mysql> TRUNCATE temp; Query OK, 0 rows affected (0.06 sec)mysql> SELECT * FROM temp; Empty set (0.00 sec)mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec)mysql> INSERT INTO temp (name) VALUES ('Linda'); Query OK, 1 row affected (0.01 sec)mysql> INSERT INTO temp (name) VALUES ('Li'); Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO temp (name) VALUES ('La'); Query OK, 1 row affected (0.00 sec)mysql> COMMIT; Query OK, 0 rows affected (0.00 sec)mysql> SELECT * FROM temp; +----+-------+ | id | name | +----+-------+ | 1 | Linda | | 2 | Li | | 3 | La | +----+-------+ 3 rows in set (0.00 sec) //開始事務和回滾事務;回到上次的提交點 mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec)mysql> INSERT INTO temp (name) VALUES ('Liu'); Query OK, 1 row affected (0.01 sec)mysql> INSERT INTO temp (name) VALUES ('Ha'); Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO temp (name) VALUES ('Lin'); Query OK, 1 row affected (0.00 sec)mysql> SELECT * FROM temp; +----+-------+ | id | name | +----+-------+ | 1 | Linda | | 2 | Li | | 3 | La | | 4 | Liu | | 5 | Ha | | 6 | Lin | +----+-------+ 6 rows in set (0.01 sec)mysql> ROLLBACK; Query OK, 0 rows affected (0.01 sec)mysql> SELECT * FROM temp; +----+-------+ | id | name | +----+-------+ | 1 | Linda | | 2 | Li | | 3 | La | +----+-------+ 3 rows in set (0.00 sec)事務的特性ACID
- A 原子性(atomicity) 說明事務是最小的工作單元,不可再分
- C 一致性(consistency) 所有的事務要求,再同一個事務中,所有的操作必須同時成功或者同時失敗
- I 隔離性 (isolation) A事務和B事務之間具有一定的隔離 ,A事務和B事務同時操作一張表會被隔離
- D(durability) 持久性 事務是最終結束的一個保障,事務提交,相當于將沒有保存到硬盤上的數據保存到硬盤上
事務的隔離性
兩個事務同時操作一張表,就類似多個線程操作一個對象,是需要使用手段保證安全的,java中是synchronized對象鎖,事務與事務的隔離就像兩個教室的墻壁,墻壁越厚,隔離的級別越高
事務的隔離級別
- 讀未提交 : read uncommitted : 事務A可以讀取到事務B沒有提交到的數據 — 這種隔離級別最低,存在的問題就是 : dirty read 臟讀現象 — 讀取到了臟數據,這種級別很少使用,一般不采用;因為未提交可能會rollback,那么數據不準確
- 讀已提交 : read committed : 事務A只能讀取到事務B提交之后的數據,這種隔離級別解決了臟讀現象-----但是問題是不可重復讀取數據 這種隔離級別是比較真實的數據,每一次讀取的數據都是絕對的真實,ORACLE就是默認讀已提交
- 不可重復讀 : 因為事務B在不斷提交,比如事務開啟后,事務A開始讀取的數據是3條,事務B又提交了,數據變多了,第二次讀取的就是4條了,3和4不相當,這就是不可重復讀
- 可重復讀: repeatable read : 事務A開啟之后,不管開啟多久,每次在事務A中讀取的數據都是一致的,即使事務B將數據修改,并且提交哦,事務A讀取的數據還是沒有發生改變,這就是可重復讀 ----- 解決了不可重復讀,但是存在的問題就是每一次讀取到的數據都是幻象,幻影讀,不夠真實 【因為讀取的數據早就改變了】MySQL中默認的事務隔離級別就是這個 ---- 就是讀取的A剛開啟的數據,A做的修改都讀取不到
- 序列化/串行化:serializable : 類似線程中的synchronized對象鎖,也就是事務A開啟之后,事務B不能執行,稱為事務同步,每一次讀取的數據都是最真實的,但是效率最低 ---- 序列化解決了所有的問題,比如臟讀,幻影讀,表示的是事務排隊,不能并發
總結一下就是 : 讀未提交 可以讀取還未提交的數據 ;讀已提交 可以讀取已經提交的數據 ;可重復讀提交之后也讀不到 ,永遠讀取的都是事務開啟時的數據【幻象】 序列化 只能執行一個事務了,另外一個事務不執行
查看事務的隔離級別 SELECT @@TRANSACTION_ISOLATION
要查看事務的隔離級別,那就要使用命令
- 查看全局的事務隔離級別 SELECT @@GLOBAL.TRANSACTION_ISOLATION;
- 查看會話的事務隔離級別 SELECT @@SESSION.TRANSACTION_ISOLATION;
設置事務的隔離級別SET GLOBAL TRANSACTION ISOLATION LEVEL ……
設置事務的隔離級別就是英語的翻譯,加上一個全局global , set global transaction isolation level,設置全局事務隔離級別
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.01 sec) //這里修改的是全局的事務隔離級別,查詢時也要查詢全局的 mysql> SELECT @@GLOBAL.TRANSACTION_ISOLATION; +--------------------------------+ | @@GLOBAL.TRANSACTION_ISOLATION | +--------------------------------+ | READ-COMMITTED | +--------------------------------+ 1 row in set (0.00 sec)GLOBAL和SESSION和缺省
這時三個不同的事務隔離級別的應用范圍
- SESSION : 會話,表示的是當前session會話【當前cmd窗口】內的所有事務
- GLOBAL: 全局 ,表示的是所有的session 全局的所有的事務 ,但是==已經存在的會話session不受影響】
- 缺省 : 表示的是當前會話session 內的下一個還沒有開始的事務【當前cmd窗口的下一個事務】
這里可以分別驗證一下幾個事務隔離級別
//可以解釋為什么剛剛修改了全局的事務之后查看當前的事務隔離級別,發現還是REPEATABLE-READ,這就是因為GLOBAL不會應用到當前存在的session,退出之后重新開啟會話發現應用了
mysql> SELECT @@TRANSACTION_ISOLATION; +-------------------------+ | @@TRANSACTION_ISOLATION | +-------------------------+ | READ-COMMITTED | +-------------------------+ 1 row in set (0.00 sec)這里驗證事務的隔離性,打開兩個cmd窗口,執行多個會話,并行
//驗證READ-UNCOMMITTED 事務A 事務B --------------------------------------------------------- USE cfengbase;USE cfengbase; START TRANSACTION; START TRANSACTION; SELECT * FROM temp; -->empty INSERT INTO temp (name) VALUES ('Linda'); SELECT * FROM temp; +----+-------+ | id | name | +----+-------+ | 1 | Linda | +----+-------+ 1 row in set (0.00 sec)可以發現事務B只是執行了,還沒有提交事務,事務A就可以看到事務B的操作了,但是這樣就臟讀了,因為事務B如果是ROLLBACK,那么數據就不好
接下來驗證read-committed
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.00 sec)事務A 事務B ------------------------------------------------------------------------------- USE cfengbase;USE cfengbase; START TRANSACTION; START TRANSACTION; SELECT * FROM temp; -->empty INSERT INTO temp (name) VALUES ('Linda'); SELECT * FROM temp; ---> emptyCOMMIT; SELECT * FROM temp; +----+-------+ | id | name | +----+-------+ | 1 | Linda | +----+-------+ 1 row in set (0.00 sec)這里就是只能讀取到事務B提交的數據,之前沒有提交的時候查詢不到,并且要成功的事務
再驗證默認的repeatable- read
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; Query OK, 0 rows affected (0.00 sec)INSERT INTO temp (name) VALUES ('Linda');事務A 事務B ------------------------------------------------------------------------------- USE cfengbase;USE cfengbase; START TRANSACTION; START TRANSACTION; SELECT * FROM temp;+----+-------+ | id | name | +----+-------+ | 1 | Linda | +----+-------+INSERT INTO temp (name) VALUES ('Hu');INSERT INTO temp (name) VALUES ('Wang');INSERT INTO temp (name) VALUES ('Liu');SELECT * FROM temp;+----+-------+| id | name |+----+-------+| 1 | Linda || 4 | Hu || 5 | Wang || 6 | Liu |+----+-------+COMMIT; SELECT * FROM temp; +----+-------+ | id | name | +----+-------+ | 1 | Linda | +----+-------+可以發現事務A能查找到的都是事務B最開始的狀態,并沒有因為B的提交而發生改變,但是這是幻影讀,不知道數據改變了多少
如果并行事務結束了,查找數據
mysql> SELECT * FROM temp; +----+-------+ | id | name | +----+-------+ | 1 | Linda | | 4 | Hu | | 5 | Wang | | 6 | Liu | +----+-------+ 4 rows in set (0.01 sec)//因為事務的提交是確確實實修改了數據,所以只是并行的時候查詢不到修改而已這里再驗證一下序列化,事務排隊serializable
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;事務A 事務B ------------------------------------------------------------------------------- USE cfengbase;USE cfengbase; START TRANSACTION; START TRANSACTION; INSERT INTO temp (name) VALUES ('Linda'); SELECT * FROM temp; ---> 光標一直閃動等待COMMIT;+----+-------+ | id | name | +----+-------+ | 1 | Linda | +----+-------+ 1 row in set (0.00 sec)需要注意的是,這里的等待時間不能過長,要及時操作
Lock wait timeout exceeded; try restarting transaction 鎖等待超時過度
不然就會報錯,timeout exceeded 超時 過度的
索引
- 什么是索引?index 位置 ,索引
索引是再數據庫表的字段上添加的,是為了提高查詢效率存在的一種機制
一張表的一個字段可以添加一個索引,當然和標記約束一樣,多個字段也可以聯合添加索引,索引相當于一本書的目錄,是為了縮小查找范圍所存在的一種機制
現實生活中,我們查字典的時候,就可以有兩種方式 : 第一種一頁一頁的查找,也就是全局掃描;第二種是先找到一個大概的范圍,之后再進行局域性搜索
Mysql中也是一樣,比如之前直接SELECT 的時候,那就是整張表一個一個查詢,幾千條記錄還好,查詢時間還是比較少,如果是百萬記錄,那么一條一條匹配就太慢了,這個時候就需要目錄了
SELECT * FROM temp WHERE name = 'Linda';這里執行的時候,會到name字段上進行掃描,因為這里條件就是name 沒有添加索引,那么這里就會一條一條搜索整個字段,效率低可以給每一個字段都添加索引,當然,如果數據較少,就不需要索引了- 索引需要排序嗎?
字典的目錄是需要排序的,按照字母順序,只有排序了才可以區間查找;在Mysql這個索引也是需要排序的,這個排序和TreeSet數據結構相同,TreeSet和TreeMap的底層都是自平衡二叉樹,在MYSQL中索引是一個b-Tree結構 ----- 遵循左小右大的原則,中序遍歷取數據
上面的引擎內容之中,也提到文件組織分別是 結構文件.frm;數據文件和索引文件,MYISAM是分別組織,可以壓縮,InnoDB索引和數據在邏輯空間tablespace中,而MEMORY把這兩個放在內存中,非常高效但是非常不安全
索引的實現原理
- 首先上面也提到過,添加主鍵約束或者UNIQUE約束的字段會自動添加索引對象
- 在任何數據庫當中,任何一張表的任何一條記錄 在硬盤存儲上都有一個硬盤的物理存儲編號
就類似于地址,這里是物理的地址;每一行數據都是一個記錄
- 索引是一個單獨的的對象,只是不同的引擎存儲在不同的位置,但是不管儲存在哪里,索引在mysql中都是以一個樹的形式存在 ----- 自平衡二叉樹 B-Tree
這里就可以看出來索引就是一個對象,這個對象為一顆b+樹,查找時就比較,如果比根節點大就在右子樹查找,比根節點小就在左子樹查找 【縮小范圍,快速定位】
比如查找到之后,因為節點放置著每一行記錄的物理地址,馬上就可以轉化SQL語句了
SELECT * FROM temp WHERE id = 55; id 是PRIMARY KEY 這里id就有一個索引對象,那么這里就是通過上面的那種b+樹的結構來遍歷查找 找到后得到物理地址,變化為 SELECT * FROM temp WHERE 物理編號 = ……; 這樣直接就取出數據了所以索引實現的原理就是縮小查找范圍,避免全表掃描;表中的字段不會動,但是索引對象會排序
添加索引的情況
什么情況下會添加索引呢? 這里的主要條件就是
-
數據量十分龐大 — 相對的概念
-
該字段經常出現在WHERE后面,也就是說經常被當作查詢的條件,經常被掃描
-
該字段很少進行DML操作,因為一旦修改之后就要進行平衡,會重新排序形成b+樹
但是不要隨意添加索引,因為索引需要維護,太多索引的話可能會影響系統的性能。建議通過主鍵查詢或者UNIQUE修飾字段進行查詢,效率較高
如何添加索引和刪除索引
- 創建索引的語法格式為
這里的索引名稱可以為tablename_colname_index 【按照下劃線命名法】
mysql> CREATE INDEX fruits_name_index ON fruits(fruits_name); Query OK, 0 rows affected (0.07 sec) Records: 0 Duplicates: 0 Warnings: 0可以看到這里下面有幾個欄目 : records , duplicates ,warnings
- 刪除索引的語法格式將CREATE 改為DROP,同時范圍限制不用具體到字段,只要到表就可,因為名稱指定了索引
DROP INDEX 索引名稱 ON table_name;
mysql> DROP INDEX fruits_name_index ON fruits; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0查看是否使用了索引
使用explain命令可以查看
語法格式為
EXPLAIN SELECT ……這里可以分別看一下主鍵查找和普通字段查找的區別
mysql> EXPLAIN SELECT * FROM fruits WHERE id = 1002; +----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | fruits | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL | +----+-------------+--------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)mysql> SHOW WARNINGS; +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select '1002' AS `id`,'蘋果' AS `fruits_name`,'2.8' AS `fruits_price`,'山東' AS `fruits_origin`,'紅富士' AS `fruits_remark`,'NULL' AS `fruits_bonus` from `cfengbase`.`fruits` where true | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)mysql> EXPLAIN SELECT * FROM fruits WHERE fruits_price = 2.8; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | fruits | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.01 sec)mysql> SHOW WARNINGS; +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select `cfengbase`.`fruits`.`id` AS `id`,`cfengbase`.`fruits`.`fruits_name` AS `fruits_name`,`cfengbase`.`fruits`.`fruits_price` AS `fruits_price`,`cfengbase`.`fruits`.`fruits_origin` AS `fruits_origin`,`cfengbase`.`fruits`.`fruits_remark` AS `fruits_remark`,`cfengbase`.`fruits`.`fruits_bonus` AS `fruits_bonus` from `cfengbase`.`fruits` where (`cfengbase`.`fruits`.`fruits_price` = 2.8) | +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)如何看呢,EXPLAIN語句表格中的數據
rows代表的就是掃描記錄條數,fruits表一共有3條,上面的主鍵查詢的rows為1,下面的普通字段查詢rows為1 --索引查詢就直接找到地址取出
type也可以看出是否索引,上面的主鍵有索引,type為const限制,下面的普通字段type為ALL代表全局搜索
filtered代表的過濾的程度,索引查詢時100,普通的查詢為1/all
上面的key還有possible_keys也可以看出來字段是否是含有鍵值和索引,mysql索引的底層結構十分復雜,之后會進行分析
現在是先進行大致的分析,后面會在數據庫原理中繼續深入了解底層
索引失效
不是添加了索引,索引就可以一直使用
- 比如SELECT * FROM emp WHERE ename LIKE ‘%T’; 這個SQL語句只能全表掃描,為什么?
因為這里是模糊查詢,并且是以%開始的,底層B+樹不能正確匹配%
所以模糊查詢時要盡量避免以%開始,匹配先匹配第一個字符,所以不是%開始可以使用索引 ---- 這時優化的策略
可以來查看一下
//普通情況下的查詢 mysql> EXPLAIN SELECT * FROM emp WHERE empno = 7934; +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | emp | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec) //%開始的模糊查詢 mysql> EXPLAIN SELECT * FROM emp WHERE empno LIKE '%4'; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 11.11 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)-
失效的第二種情況
使用OR的時可能會失效,如果使用or那么要求兩邊的字段都要有索引,才會進行索引,如果其中一邊有一個字段沒有索引,那么另一個字段的索引也就沒有用,所以不建議使用OR ----- 優化的策略
這里可以看下這種情況的失效
empno為主鍵字段,ename為普通字段 mysql> EXPLAIN SELECT * FROM emp WHERE empno = 7934; +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | emp | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)mysql> EXPLAIN SELECT * FROM emp WHERE empno = 7934 OR empno = 7902; +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | emp | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 2 | 100.00 | Using where | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.01 sec)mysql> EXPLAIN SELECT * FROM emp WHERE empno = 7934 OR ename = 'FORD'; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | emp | NULL | ALL | PRIMARY | NULL | NULL | NULL | 14 | 16.43 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)可以發現普通一個字段為const,兩個字段為range,下面的OR所以失效了,type為ALL
那么如何優化呢? — 可以采用union的方式,union前面的索引查詢和下面的查詢,效率更高
- 失效的第三種情況,就是復合索引的時候,沒有使用左側的列進行查找
復合索引 : 多個字段聯合添加索引
CREATE INDEX 索引名稱 ON table_name(col1,col2……); mysql> CREATE INDEX fruits_nameprice_index ON fruits(fruits_name,fruits_price); Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0mysql> EXPLAIN SELECT * FROM fruits WHERE fruits_name = '蘋果'; +----+-------------+--------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | fruits | NULL | ref | fruits_nameprice_index | fruits_nameprice_index | 137 | const | 1 | 100.00 | NULL | +----+-------------+--------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)mysql> EXPLAIN SELECT * FROM fruits WHERE fruits_price = 2.8; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | fruits | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)這里就是因為樹中是先匹配的前面的,所以沒有使用左字段就沒有用,就像模糊查詢使用%開始
- 在WHERE中索引列參與了運算索引也會失效
這里就是如果索引字段參與了運算就 會失效
mysql> DROP INDEX fruits_nameprice_index ON fruits; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0mysql> CREATE INDEX fruits_price_index ON fruits(fruits_price); Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0mysql> EXPLAIN SELECT * FROM fruits WHERE fruits_price = 2.8; +----+-------------+--------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | fruits | NULL | ref | fruits_price_index | fruits_price_index | 8 | const | 1 | 100.00 | NULL | +----+-------------+--------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)mysql> EXPLAIN SELECT * FROM fruits WHERE fruits_price+1 = 3.8; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | fruits | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)這里可以清楚看到上面的搜索使用了索引,下面+1操作就是全表查詢
- 失效的第五種情況 在WHERE中,索引列使用了函數,也會失效
因為這里就是對源字段進行了類似的修改查詢,那么就和原來的索引樹對應不上了,所以只能走普通的查詢
mysql> SELECT * FROM fruits WHERE ROUND(fruits_price,0) = 3; +------+-------------+--------------+---------------+---------------+--------------+ | id | fruits_name | fruits_price | fruits_origin | fruits_remark | fruits_bonus | +------+-------------+--------------+---------------+---------------+--------------+ | 1002 | 蘋果 | 2.8 | 山東 | 紅富士 | NULL | +------+-------------+--------------+---------------+---------------+--------------+ 1 row in set (0.01 sec)mysql> EXPLAIN SELECT * FROM fruits WHERE ROUND(fruits_price,0) = 3; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | fruits | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Using where | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)這里可以看到使用了ROUND函數之后這里就是查詢了所有的數據
當然所索引失效還有其他的情況,比如類型轉化之類的
總結一下
索引失效有很多種情況
第一種就是模糊查詢%開始和復合索引沒有左字段,都是因為匹配不了就失效了
第二種就是使用了OR,OR的兩邊要都是索引字段才會進行索引
第三種就是對索引字段進行了操作查詢 ,比如類型轉換,函數,數學運算,原來的索引對象不能匹配,所以就失效了
索引的分類
索引是各種數據庫進行優化的重要手段,優化的時候優先考慮的手段就是索引,索引在數據庫種分了很多種類
- 單一索引 : 一個字段上添加索引
- 復合索引: 多個字段上添加索引
- 唯一索引 : UNIQUE 約束的字段上添加索引
- 主鍵索引 : 主鍵上添加索引
根據索引的結構,所以唯一性比較弱的字段上添加索引用處不大,因為唯一性比較弱,那么數據可能會大量重復,這個時候索引用處不大
越唯一效率越高
視圖view
- 什么是視圖呢?
視圖就是站在不同的角度去看待同一份數據
創建視圖 CREATE VIEW view_name AS SELECT ……
這里的view后面只能是DQL語句,也即是data query language 就是SELECT 語句;也就是DQL語句才能以view的形式創建;同時后面不僅可以是單表,也可以是表連接得到的臨時
其實之前接觸的FROM子查詢的時候,當時所提到的臨時表就類似一個視圖
CREATE VIEW emp_view AS SELECT * FROM emp;這里可以看一下這個的使用
mysql> CREATE VIEW emp_view AS SELECT * FROM emp LIMIT 5; Query OK, 0 rows affected (0.01 sec)- 刪除視圖 — 就是類似于DROP VIEW view_name;
用視圖可以做什么呢? 【視圖也可以看成一張特殊的表】
- 視圖的作用: 可以面向視圖對象進行增刪查改,但是會導致原表被操作 視圖指向了原表的數據
這里就區別于之前的表復制 CREATE TABLE table_name AS SELECT ……
表復制創建一張全新的表,對新表的操作不會影響舊表的數據;但是對視圖的操作會影響到原表的數據
所以這里就可以按照普通的對表的操作對視圖進行操作
mysql> CREATE VIEW temp_view AS SELECT * FROM temp LIMIT 3; Query OK, 0 rows affected (0.01 sec)mysql> SELECT * FROM temp_view; +----+-------+ | id | name | +----+-------+ | 1 | Linda | | 4 | Hu | | 5 | Wang | +----+-------+ 3 rows in set (0.00 sec)mysql> INSERT INTO temp_view (name) VALUES ('LIUzhen'); ERROR 1471 (HY000): The target table temp_view of the INSERT is not insertable-into mysql> DELETE FROM temp_view WHERE id = 5; ERROR 1288 (HY000): The target table temp_view of the DELETE is not updatable這里報錯說明不可操作
- 面向視圖就是可以方便獲取各種的臨時的表,并且可以直接在視圖中進行操作,這樣就可以達到效果;比如在龐大的數據中可以建立一個小視圖,或者表連接的時候的視圖直接修改,則可以影響原來表的數據
視圖的作用
視圖的作用就是簡化SQL語句,比如要對表連接的部分進行持續修改,如果原來的方式就會使用FROM子查詢,可是問題是,這個語句可能特別特別長,那么就像字段起別名一樣,如果多次操作,并且是要確實修改原表的數據,那就不能用復制表,而是使用視圖。
這樣就可以簡化開發了,可以將復雜的SQL語句直接以視圖對象的形式創建,利于后期的維護,修改只需要修改一個位置就可以,只需要修改視圖所映射的SQL語句
面向視圖開發的時候,就是像使用表一樣,視圖不是存儲在內存中,而是存儲在硬盤中,不會消失的。視圖對應的語句只能是DQL語句,但是視圖建立之后可以進行增刪改查 CRUD create retrieve(檢索) update delete 事務的acid
DBA命令
還有的DCL命令比如GRANT和REVOKE后面再分享,這里分享兩個主要的命令
刪庫跑路,一旦失手刪除數據庫難道只能cry,所以平時使用數據庫要記得備份數據
導出數據庫到文件
我們在操作之后可以記得隨時將數據庫導出到文件之中
mysqldump basename >路徑+xx.sql -u user -p password這里演示將數據庫cfengtest導出到D盤Download中
C:\Users\OMEY-PC>mysqldump cfengtest>D:\Download\cfengtest.sql -u root -p Enter password: ******** //這里就用的root,因為cfeng用戶沒有權限這樣就算刪除數據庫了,可以使用SOURCE命令將數據庫給導入就可以獲取到數據了,創建數據庫,之后USE ,use之后再source就可以重新開始操作了
數據庫設計范式
數據庫設計范式就是數據庫表的設計依據,怎么進行數據庫表的設計
數據庫范式共有3個,之前其實提到過的,設計表的時候,按照范式進行,避免表中數據的冗余,避免空間的浪費
數據庫設計第一范式 PK,atomicity
任何一張表必須有主鍵,每一個字段的原子性不能再分
第一個挺好理解,使用workbench的時候也就提示讓第一個字段設計為主鍵
//下面的表就不合理 學生編號 姓名 聯系方式 --------------------------------------------------- 1001 張三 zhagnsan@liu.cn,1787823978 1002 李素 lisu@liu.cn,235589768這里的問題是沒有主鍵 ,第二個就是聯系方式字段不具有原子性,可以分為郵箱和qq學生編號(PK) 姓名 郵箱 QQ --------------------------------------------------- 1001 張三 zhagnsan@liu.cn 1787823978 1002 李素 lisu@liu.cn 235589768數據庫第二范式 : 非PK字段完全依賴PK
建立在第一范式的基礎上,就是要求所有的非主鍵字段完全依賴主鍵,不要產生部分依賴
//下面的表也不合理 no name classno classname ----------------------------------------------- 1 Lucy 100 英才一班 2 JACK 100 英才一班 3 Wang 100 英才一班 4 Liu 100 英才一班 5 Linda 100 英才一班 6 Zhang 101 英才二班 7 Sun 101 英才二班 8 Ge 101 英才二班 9 Hu 101 英才二班 10 Hong 101 英才二班首先是沒有主鍵,這里可以對no 和 classno設計復合主鍵;這里是多對一的關系;這樣設計之后 name依賴的是no,classname依賴的是classno;都是部分依賴主鍵,所以不滿足數據庫第二范式,需要修改 直接變為兩張表,FK就可以 如果是對對多的關系,那么就建立3張表來解決問題多對多,三張表;關系表,兩個外鍵
假設上面的學生和班級不是多對一,而是多對多,那么表的設計就是
t_student --------------- no (PK) namet_class ----------------------- classno(PK) classnamet_association 關系表 ------------------------- id(自然PK) 學生編號no(FK t_student no) 教師編號(FK)普通的多對一就兩張表,讓其中簡單的字段連接就可以外鍵
數據庫第三范式 : 直接依賴PK,不傳遞依賴
建立在第二范式的基礎上,要求所有的非主鍵字段直接依賴主鍵,而不是傳遞依賴
這里可以用二中的數據
//下面的表也不合理 no(PK) name classno classname ----------------------------------------------- 1 Lucy 100 英才一班 2 JACK 100 英才一班 3 Wang 100 英才一班 4 Liu 100 英才一班 5 Linda 100 英才一班 6 Zhang 101 英才二班 7 Sun 101 英才二班 8 Ge 101 英才二班 9 Hu 101 英才二班 10 Hong 101 英才二班//這里是單一主鍵,一對多關系,滿足第一范式和第二范式 但是不滿足第三范式,以為classname依賴classno,classno依賴no(PK)怎么設計一對多 拆分成兩張表 --- 一張表是no為主鍵,一張表是classno為主鍵 也就是上面講外鍵的時候分析的數據一對多,兩張表,多的表加外鍵
- 總結一下就是
一對一設計 : 一張表可能字段太多,太龐大,需要拆分表,那么select可能不好使用,那么這個時候就建議拆分成兩張表 ,這個時候拆分的時候第一部分的主鍵字段拿來當外鍵字段并加唯一性約束
一對一: 外鍵唯一
多對一 : 兩張表,多的表加外鍵
多對多: 三張表,關系表兩個外鍵
MySQL設計原則
上面說的范式只是一個大概的參考,實際開發中不一定要照搬,實際和理論是有偏差的,最終的目的是滿足客戶的要求
冗余的表雖然耗費內存,但是優點就是查詢的效率高一些,冗余換速度,因為可以減少表的連接次數,并且對于開發人員來說,SQL語句的編寫會降低
MySQL搜索題
之前已經做過第一個題目了,就一個FROM的子查詢和連接查詢就可以解決問題
Q.哪些人的薪水在部門平均薪水之上
這里還是用FROM的子查詢和連接查詢,查詢條件就是deptno相等并且大于平均薪資
mysql> SELECT e.ename,e.sal,e.deptno,t.avgsal-> FROM-> (SELECT deptno,AVG(sal) avgsal FROM emp GROUP BY deptno) t-> JOIN-> emp e-> ON-> (e.deptno = t.deptno) AND (e.sal > t.avgsal); +-------+---------+--------+-------------+ | ename | sal | deptno | avgsal | +-------+---------+--------+-------------+ | ALLEN | 1600.00 | 30 | 1566.666667 | | JONES | 2975.00 | 20 | 2175.000000 | | BLANK | 2850.00 | 30 | 1566.666667 | | SCOTT | 3000.00 | 20 | 2175.000000 | | KING | 5000.00 | 10 | 2916.666667 | | FORD | 3000.00 | 20 | 2175.000000 | +-------+---------+--------+-------------+ 6 rows in set (0.00 sec)Q.取得公司中所有人的薪水等級平均值
這里注意臨時表要起別名
mysql> SELECT-> AVG(grade)-> FROM-> (SELECT g.grade FROM emp e JOIN salgrade g ON e.sal BETWEEN g.losal AND g.hisal); ERROR 1248 (42000): Every derived table must have its own aliasEvery derived table must have its own alias 每一個衍生表必須有它自己的別名
所以要給臨時表起別名
mysql> SELECT AVG(t.grade) FROM (SELECT g.grade FROM emp e JOIN salgrade g ON e.sal BETWEEN g.losal AND g.hisal) t; +--------------+ | AVG(t.grade) | +--------------+ | 2.8571 | +--------------+ 1 row in set (0.01 sec)之后的會補上~已經12000字了
總結
以上是生活随笔為你收集整理的MySQL PK,FK,视图,索引,引擎总结【12000字概览MySQL】用于查漏补缺的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【H5即时通讯系统PHP源码】支持嵌入+
- 下一篇: **ISO9001好处和定义**