MySQL--DAY04
索引
定義
索引是在資料庫表的欄位上新增的,是為了提高查詢效率存在的一種機制。
一張表的一個欄位可以新增一個索引,當然,多個欄位聯合起來也可以新增索引。
索引相當於一本書的目錄,是為了縮小掃描範圍而存在的一種機制。
對於一本字典來說,查詢某個漢字有兩種方式:
- 第一種方式:一頁一頁挨著找,直到找到為止,這種查詢方式屬於全字典掃描。效率比較低。
- 第二種方式:先透過目錄(索引)去定位一個大概的位置,然後直接定位到這個位置,做局域性掃描,縮小掃描的範圍,快速的查詢。這種查詢方式屬於透過索引檢索,效率較高。
t_user
id(idIndex) name(nameIndex) email(emailIndex) address (emailAddressIndex)
----------------------------------------------------------------------------------
1 zhangsan...
2 lisi
3 wangwu
4 zhaoliu
5 hanmeimei
6 jack
select * from t_user where name = 'jack';
以上的這條SQL語句會去name欄位上掃描,為什麼?
因為查詢條件是:name='jack'
如果name欄位上沒有新增索引(目錄),或者說沒有給name欄位建立索引,
MySQL會進行全掃描,會將name欄位上的每一個值都比對一遍。效率比較低。
MySQL在查詢方面主要就是兩種方式:
第一種方式:全表掃描
第二種方式:根據索引檢索。
注意:
在實際中,漢語字典前面的目錄是排序的,按照a b c d e f....排序,
為什麼排序呢?因為只有排序了才會有區間查詢這一說!(縮小掃描範圍
其實就是掃描某個區間罷了!)
在mysql資料庫當中索引也是需要排序的,並且這個索引的排序和TreeSet
資料結構相同。TreeSet(TreeMap)底層是一個自平衡的二叉樹!在mysql
當中索引是一個B-Tree資料結構。
遵循左小右大原則存放。採用中序遍歷方式遍歷取資料。
原理
假設有一張使用者表:t_user
id(PK) name 每一行記錄在硬碟上都有物理儲存編號
----------------------------------------------------------------------------------
100 zhangsan 0x1111
120 lisi 0x2222
99 wangwu 0x8888
88 zhaoliu 0x9999
101 jack 0x6666
55 lucy 0x5555
130 tom 0x7777
提醒1:在任何資料庫當中主鍵上都會自動新增索引物件,id欄位上自動有索引,因為id是PK。另外在mysql當中,一個欄位上如果有unique約束的話,也會自動建立索引物件。
提醒2:在任何資料庫當中,任何一張表的任何一條記錄在硬碟儲存上都有一個硬碟的物理儲存編號。
提醒3:在mysql當中,索引是一個單獨的物件,不同的儲存引擎以不同的形式存在,在MyISAM儲存引擎中,索引儲存在一個.MYI檔案中。在InnoDB儲存引擎中索引儲存在一個邏輯名稱叫做tablespace的當中。在MEMORY儲存引擎當中索引被儲存在記憶體當中。不管索引儲存在哪裡,索引在mysql當中都是一個樹的形式存在。(自平衡二叉樹:B-Tree)
在mysql當中,主鍵上,以及unique欄位上都會自動新增索引的!!!!
什麼條件下,我們會考慮給欄位新增索引呢?
- 條件1:資料量龐大(到底有多麼龐大算龐大,這個需要測試,因為每一個硬體環境不同)
- 條件2:該欄位經常出現在where的後面,以條件的形式存在,也就是說這個欄位總是被掃描。
- 條件3:該欄位很少的DML(insert delete update)操作。(因為DML之後,索引需要重新排序。)
建議不要隨意新增索引,因為索引也是需要維護的,太多的話反而會降低系統的效能。
建議透過主鍵查詢,建議透過unique約束的欄位進行查詢,效率是比較高的。
建立和刪除索引
建立索引:
mysql> create index emp_ename_index on emp(ename);
給emp表的ename欄位新增索引,起名:emp_ename_index
刪除索引:
mysql> drop index emp_ename_index on emp;
將emp表上的emp_ename_index索引物件刪除。
檢視SQL語句是否使用索引
mysql> explain select * from emp where ename = 'KING';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
掃描14條記錄:說明沒有使用索引。type=ALL
mysql> create index emp_ename_index on emp(ename);
mysql> explain select * from emp where ename = 'KING';
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_ename_index | emp_ename_index | 33 | const | 1 | Using where |
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
索引失效
索引什麼時候失效?
失效的第1種情況
select * from emp where ename like '%T';
ename上即使新增了索引,也不會走索引,為什麼?
原因是因為模糊匹配當中以“%”開頭了!
儘量避免模糊查詢的時候以“%”開始。
這是一種最佳化的手段/策略。
mysql> explain select * from emp where ename like '%T';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第2種情況
使用or的時候會失效,如果使用or那麼要求or兩邊的條件欄位都要有索引,才會走索引,如果其中一邊有一個欄位沒有索引,那麼另一個欄位上的索引也會實現。所以這就是為什麼不建議使用or的原因。
mysql> explain select * from emp where ename = 'KING' or job = 'MANAGER';
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | emp_ename_index | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
失效的第3種情況
使用複合索引的時候,沒有使用左側的列查詢,索引失效
什麼是複合索引?
兩個欄位,或者更多的欄位聯合起來新增一個索引,叫做複合索引。
create index emp_job_sal_index on emp(job,sal);
mysql> explain select * from emp where job = 'MANAGER';
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_job_sal_index | emp_job_sal_index | 30 | const | 3 | Using where |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
mysql> explain select * from emp where sal = 800;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第4種情況
在where當中索引列參加了運算,索引失效。
mysql> create index emp_sal_index on emp(sal);
explain select * from emp where sal = 800;
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_sal_index | emp_sal_index | 9 | const | 1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
mysql> explain select * from emp where sal+1 = 800;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第5種情況
在where當中索引列使用了函式
explain select * from emp where lower(ename) = 'smith';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第...種情況
索引分類
- 單一索引:一個欄位上新增索引。
- 複合索引:兩個欄位或者更多的欄位上新增索引。
- 主鍵索引:主鍵上新增索引。
- 唯一性索引:具有unique約束的欄位上新增索引。
.....
注意:唯一性比較弱的欄位上新增索引用處不大。
檢視
什麼是檢視?
view:站在不同的角度去看待同一份資料。
建立和刪除檢視物件
表複製:
mysql> create table dept2 as select * from dept;
dept2表中的資料:
mysql> select * from dept2;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
建立檢視物件:
create view dept2_view as select * from dept2;
刪除檢視物件:
drop view dept2_view;
注意:只有DQL語句才能以view的形式建立。
create view view_name as // 這裡的語句必須是DQL語句;
檢視的作用
我們可以面向檢視物件進行增刪改查,對檢視物件的增刪改查,會導致原表被操作!
(檢視的特點:透過對檢視的操作,會影響到原表資料。)
//面向檢視查詢
select * from dept2_view;
// 面向檢視插入
insert into dept2_view(deptno,dname,loc) values(60,'SALES', 'BEIJING');
// 查詢原表資料
mysql> select * from dept2;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
| 60 | SALES | BEIJING |
+--------+------------+----------+
// 面向檢視刪除
mysql> delete from dept2_view;
// 查詢原表資料
mysql> select * from dept2;
Empty set (0.00 sec)
// 建立檢視物件
create view
emp_dept_view
as
select
e.ename,e.sal,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
// 查詢檢視物件
mysql> select * from emp_dept_view;
+--------+---------+------------+
| ename | sal | dname |
+--------+---------+------------+
| CLARK | 2450.00 | ACCOUNTING |
| KING | 5000.00 | ACCOUNTING |
| MILLER | 1300.00 | ACCOUNTING |
| SMITH | 800.00 | RESEARCH |
| JONES | 2975.00 | RESEARCH |
| SCOTT | 3000.00 | RESEARCH |
| ADAMS | 1100.00 | RESEARCH |
| FORD | 3000.00 | RESEARCH |
| ALLEN | 1600.00 | SALES |
| WARD | 1250.00 | SALES |
| MARTIN | 1250.00 | SALES |
| BLAKE | 2850.00 | SALES |
| TURNER | 1500.00 | SALES |
| JAMES | 950.00 | SALES |
+--------+---------+------------+
// 面向檢視更新
update emp_dept_view set sal = 1000 where dname = 'ACCOUNTING';
// 原表資料被更新
mysql> select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 1000.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 1000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1000.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
實際開發中的作用?
《方便,簡化開發,利於維護》
create view
emp_dept_view
as
select
e.ename,e.sal,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
假設有一條非常複雜的SQL語句,而這條SQL語句需要在不同的位置上反覆使用。
每一次使用這個sql語句的時候都需要重新編寫,很長,很麻煩,怎麼辦?
可以把這條複雜的SQL語句以檢視物件的形式新建。
在需要編寫這條SQL語句的位置直接使用檢視物件,可以大大簡化開發。
並且利於後期的維護,因為修改的時候也只需要修改一個位置就行,只需要修改檢視物件所對映的SQL語句。
我們以後面向檢視開發的時候,使用檢視的時候可以像使用table一樣。
可以對檢視進行增刪改查等操作。檢視不是在記憶體當中,檢視物件也是儲存在硬碟上的,不會消失。
再提醒一下:
檢視對應的語句只能是DQL語句。但是檢視物件建立完成之後,可以對檢視進行增刪改查等操作。
小插曲:
增刪改查,又叫做:CRUD。
CRUD是在公司中程式設計師之間溝通的術語。一般我們很少說增刪改查。
一般都說CRUD。
C:Create(增)
R:Retrive(查:檢索)
U:Update(改)
D:Delete(刪)
DBA常用命令?
重點掌握:資料的匯入和匯出(資料的備份)
其它命令瞭解一下即可。
資料匯出?
注意:在windows的dos命令視窗中:
mysqldump study>D:\study.sql -uroot -p123456
可以匯出指定的表嗎?
mysqldump study emp>D:\study.sql -uroot -p123456
資料匯入?
注意:需要先登入到mysql資料庫伺服器上。
然後建立資料庫:
create database study;
使用資料庫:
use study
然後初始化資料庫:source D:\study.sql
資料庫設計三正規化
什麼是資料庫設計三正規化?
資料庫表的設計依據。教你怎麼進行資料庫表的設計。
資料庫設計三正規化有幾類?
3個。
- 第一正規化:要求任何一張表必須有主鍵,每一個欄位原子性不可再分。
- 第二正規化:建立在第一正規化的基礎之上,要求所有非主鍵欄位完全依賴主鍵,不要產生部分依賴。
- 第三正規化:建立在第二正規化的基礎之上,要求所有非主鍵欄位直接依賴主鍵,不要產生傳遞依賴。
宣告:三正規化是面試官經常問的,所以一定要熟記在心!
設計資料庫表的時候,按照以上的正規化進行,可以避免表中資料的冗餘,空間的浪費。
第一正規化
最核心,最重要的正規化,所有表的設計都需要滿足。
必須有主鍵,並且每一個欄位都是原子性不可再分。
學生編號 學生姓名 聯絡方式
---------------------------------------------------
1001 張三 zs@gmail.com,1359999999
1002 李四 ls@gmail.com,13699999999
1001 王五 ww@163.net,13488888888
以上是學生表,滿足第一正規化嗎?
不滿足,第一:沒有主鍵。第二:聯絡方式可以分為郵箱地址和電話
學生編號(pk) 學生姓名 郵箱地址 聯絡電話
--------------------------------------------------------------------
1001 張三 zs@gmail.com 1359999999
1002 李四 ls@gmail.com 13699999999
1003 王五 ww@163.net 13488888888
第二正規化
建立在第一正規化的基礎之上,要求所有非主鍵欄位必須完全依賴主鍵,不要產生部分依賴。
學生編號 學生姓名 教師編號 教師姓名
----------------------------------------------------
1001 張三 001 王老師
1002 李四 002 趙老師
1003 王五 001 王老師
1001 張三 002 趙老師
這張表描述了學生和老師的關係:(1個學生可能有多個老師,1個老師有多個學生)
這是非常典型的:多對多關係!
分析以上的表是否滿足第一正規化?
不滿足第一正規化。
怎麼滿足第一正規化呢?修改
學生編號+教師編號(pk) 學生姓名 教師姓名
----------------------------------------------------
1001 001 張三 王老師
1002 002 李四 趙老師
1003 001 王五 王老師
1001 002 張三 趙老師
學生編號 教師編號,兩個欄位聯合做主鍵,複合主鍵(PK: 學生編號+教師編號)
經過修改之後,以上的表滿足了第一正規化。但是滿足第二正規化嗎?
不滿足,“張三”依賴1001,“王老師”依賴001,顯然產生了部分依賴。
產生部分依賴有什麼缺點?
資料冗餘了。空間浪費了。“張三”重複了,“王老師”重複了。
為了讓以上的表滿足第二正規化,你需要這樣設計:
使用三張表來表示多對多的關係!!!!
學生表
學生編號(pk) 學生名字
------------------------------------
1001 張三
1002 李四
1003 王五
教師表
教師編號(pk) 教師姓名
--------------------------------------
001 王老師
002 趙老師
學生教師關係表
id(pk) 學生編號(fk) 教師編號(fk)
-----------------------------------------------------------
1 1001 001
2 1002 002
3 1003 001
4 1001 002
背口訣:
多對多怎麼設計?
多對多,三張表,關係表兩個外來鍵!!!!!!!!!!!!!!!
第三正規化
第三正規化建立在第二正規化的基礎之上
要求所有非主鍵字典必須直接依賴主鍵,不要產生傳遞依賴。
學生編號(PK) 學生姓名 班級編號 班級名稱
---------------------------------------------------------
1001 張三 01 一年一班
1002 李四 02 一年二班
1003 王五 03 一年三班
1004 趙六 03 一年三班
以上表的設計是描述:班級和學生的關係。很顯然是1對多關係!
一個教室中有多個學生。
分析以上表是否滿足第一正規化?
滿足第一正規化,有主鍵。
分析以上表是否滿足第二正規化?
滿足第二正規化,因為主鍵不是複合主鍵,沒有產生部分依賴。主鍵是單一主鍵。
分析以上表是否滿足第三正規化?
第三正規化要求:不要產生傳遞依賴!
一年一班依賴01,01依賴1001,產生了傳遞依賴。
不符合第三正規化的要求。產生了資料的冗餘。
那麼應該怎麼設計一對多呢?
班級表:一
班級編號(pk) 班級名稱
----------------------------------------
01 一年一班
02 一年二班
03 一年三班
學生表:多
學生編號(PK) 學生姓名 班級編號(fk)
-------------------------------------------
1001 張三 01
1002 李四 02
1003 王五 03
1004 趙六 03
背口訣:
一對多,兩張表,多的表加外來鍵!!!!!!!!!!!!
總結
一對多:一對多,兩張表,多的表加外來鍵!!!!!!!!!!!!
多對多:多對多,三張表,關係表兩個外來鍵!!!!!!!!!!!!!!!
一對一:一對一放到一張表中不就行了嗎?為啥還要拆分表?
在實際的開發中,可能存在一張表欄位太多,太龐大。這個時候要拆分表。
一對一怎麼設計?
沒有拆分表之前:一張表
t_user
id login_name login_pwd real_name email address........
---------------------------------------------------------------------------
1 zhangsan 123 張三 zhangsan@xxx
2 lisi 123 李四 lisi@xxx
...
這種龐大的表建議拆分為兩張:
t_login 登入資訊表
id(pk) login_name login_pwd
---------------------------------
1 zhangsan 123
2 lisi 123
t_user 使用者詳細資訊表
id(pk) real_name email address........ login_id(fk+unique)
-----------------------------------------------------------------------------------------
100 張三 zhangsan@xxx 1
200 李四 lisi@xxx 2
口訣:一對一,外來鍵唯一!!!!!!!!!!
叮囑
資料庫設計三正規化是理論上的。
實踐和理論有的時候有偏差。
最終的目的都是為了滿足客戶的需求,有的時候會拿冗餘換執行速度。
因為在sql當中,表和表之間連線次數越多,效率越低。(笛卡爾積)
有的時候可能會存在冗餘,但是為了減少表的連線次數,這樣做也是合理的,並且對於開發人員來說,sql語句的編寫難度也會降低。
面試的時候把這句話說上:他就不會認為你是初級程式設計師了!