數據庫基礎
1.什麼是數據庫
用來存儲數據。數據庫可在硬盤及內存中存儲數據html
數據庫與文件存儲數據的區別java
數據庫本質也是經過文件來存儲數據,數據庫的概念就是系統的管理存儲數據的文件
數據庫介紹 本質就是存儲數據的C/S架構的socket套接字軟件 數據庫服務器端:存放數據的主機集羣 數據庫客戶端:能夠鏈接數據庫的任意客戶端 數據庫管理員:DBA
2.數據庫基本概念
庫:多個表構建一個數據庫
表:多條數據構建一張表
記錄:存放一條條數據,本質就是文件中的一條條數據記錄
字段,字段,字段。。。==》一條數據 python
3.數據庫分類
關係型數據庫(MySQL 、Oracle 、 SQL server 、access、db2)
1.有表的概念
2.以表中一條條的紀錄存儲數據
非關係型數據庫 (mongodb、redis、memcache)
1.有表的概念
2.以key-value鍵值對方式存儲數據
mysql
4.數據庫安裝、啓動與鏈接
安裝 server端h和client端
如何啓動server? 若是經過client鏈接serverweb
前提:配置環境變量
一、前往數據庫安裝路徑,bin文件夾下,cmd執行MySQL啓動服務器端
二、前往數據庫安裝路徑,bin文件夾下,cmd執行MySQL啓動客戶端連接服務器端
面試
將MySQL服務器添加到系統服務,在系統服務中啓動MySQL,命令:mysqld --install
進入系統 win+r=> services.msc=>找到MySQL服務手動啓動或關閉
鏈接數據庫:mysql -hlocalhost -P3306 -uroot -p
經過最高權限進入數據庫,要採用root用戶進入,連入本地數據庫: mysql -uroot -p正則表達式
查看MySQL版本:select version();
查看當前用戶:select user();
查看MySQL下(當前用戶能夠操做的)數據庫:show databases;redis
5.密碼操做:
修改密碼: mysqladmin -uroot -p舊密碼 password「新密碼」 忘記舊密碼 1.繞過受權表啓動服務(安全認證的服務中止):mysqld --skip-grant-tables; 2.以任意密碼登錄root用戶:mysql -uroot -p任意 3.更新密碼: updata mysql.user set password=passwore("新密碼")where user=root and host="localhost" 4.刷新權限:flush privileges;
6.統一字符編碼
查看數據庫配置信息 1.在mysql安裝目錄下:建立my.ini (my.cnf) (命令:type nul>文件名.文件後綴) 2.設置配置信息並保存 [mysqld] #port=7777 註釋 character-set-server=utf8 collation-server=utf8_general_ci [client] default-character-set=utf8 3.重啓服務
庫的基本操做
# 前提: 鏈接上數據庫 1.增,建立數據庫 採用默認編碼集: create database db1; # db1爲數據庫名 自定義編碼集: create database db1 charset="gbk"; 2.查,查看數據庫所有信息 縱觀全部數據庫: show databases; 詳細信息: show create database db1; 3.改,修改數據庫編碼集 alter database db1 charset="utf8"; 4.刪, 移除數據庫 drop database db1;
表的基本操做
#前提: 在具體的某個庫下建立表 進入指定數據庫: use db1 肯定當前使用的數據庫: select database(); 1.增, 建立表(字段1 類型, ..., 字段n 類型) create table t1(name char, age int); 2.查,查看錶信息 縱觀全部數據庫: show tables; 詳細信息: show create table t1; 表字段結構信息: desc t1; 3.改 修改字段屬性: alter table t1 modify name char(20); 修改字段名: alter table t1 change name usr char(16); 修改表名: alter table t1 rename t2; 4.刪, 移除表 drop table t1;
字段的基本操做
# 前提: 知道具體操做的是哪張表 1.增, 添加字段 insert into t1 (usr, age) values ("aa", 18),("bb", 8); 2.查 select * from t1; 3.改 update t1 set age=28 where usr="aa"; 4.刪 delete from t1 where age>8;
引擎與數據類型
前提: 引擎是建表是規定, 提供給表使用的, 不是數據庫 mysql> show engines; # 展現全部引擎 # 重點: # innodb(默認): 支持事務, 行級鎖, 外鍵 # myisam: 查詢效率要優於innodb, 當不須要支持事務, 行級鎖, 外鍵, 能夠經過設置myisam來優化數據庫 mysql> use db1; mysql> create table t1(id int)engine=innodb; mysql> create table t2(id int)engine=myisam; mysql> create table t3(id int)engine=blackhole; mysql> create table t4(id int)engine=memory; insert into t1 values(1); insert into t2 values(1); insert into t3 values(1); insert into t4 values(1); select * from t1; ...
建立表完整語法
''' create table 表名( 字段名1 類型[(寬度) 約束條件], 字段名2 類型[(寬度) 約束條件], 字段名3 類型[(寬度) 約束條件] )engine=innodb charset=utf8; ''' # []可選參數 # create table db1.t1(name char(3) not null); # 數據插入時,name不能爲空(null), 且最長只能存放三個字符 # 總結: 寬度和約束條件爲可選參數, 用來限制存放數據的規則
數據庫的模式
# sql_mode: 反映數據庫的全局變量 # 數據庫模式限制的是客戶端對服務器操做數據的方式(是否嚴格) # 兩種模式 no_engine_substitution:非安全性,默認 strict_trans_tables:安全模式 # 查看當前數據庫模式: show variables like "%sql_mode%"; # %匹配0~n個任意字符 => 模糊查詢 # 設置爲安全模式 set global sql_mode="strict_trans_tables"; # 重啓鏈接(客戶端) quit # 應用 create table t1(name char(2)); insert into t1 values ("ab") # 正常 insert into t1 values ("zero") # 錯誤 Data too long for column 'name' at row 1
數據類型
# mysql數據庫支持存放哪些數據 # 整型* | 浮點型 | 字符型* | 時間類型 | 枚舉類型 | 集合類型
整型
'''類型 tinyint:1字節 -128~127 * smallint:2字節 mediumint:3字節 int:4字節 -2147483648~2147483647 * bigint:8字節 ''' '''約束 * unsigned:無符號 zerofill:0填充 ''' # 不一樣類型所佔字節數不同, 決定所佔空間及存放數據的大小限制 # eg: create table t8(x tinyint); insert into t8 values(200); # 非安全模式存入,值只能到最大值127 select x from t8; '''寬度 1.不能決定整型存放數據的寬度, 超過寬度能夠存放, 最終由數據類型所佔字節決定 2.若是沒有超過寬度,且有zerofill限制, 會用0填充前置位的不足位 3.沒有必要規定整型的寬度, 默認設置的寬度就爲該整型能存放數據的最大寬度 * ''' # eg:1 create table t9(x int(5)); insert into t9 values(123456); select (x) from t9; # 結果: 123456 insert into t9 values(2147483648); select (x) from t9; # 結果: 2147483647 insert into t9 values(10); select (x) from t9; # 結果: 10 # eg:2 create table t10(x int(5) unsigned zerofill); # 區域0~4294967295 insert into t10 values(10); select x from t10; # 結果: 00010 insert into t10 values(12345678900); select x from t10; # 結果: 4294967295
浮點型
'''類型 float:4字節,3.4E–38~3.4E+38 * double:8字節,1.7E–308~1.7E+308 decimal:M,D大值基礎上+2 ''' '''寬度: 限制存儲寬度 (M, D) => M爲總位數,D爲小數位 float(255, 30):精度最低,最經常使用 double(255, 30):精度高,佔位多 decimal(65, 30):字符串存,全精度 ''' # eg:1 create table t11 (age float(256, 30)); # Display width out of range for column 'age' (max = 255) create table t11 (age float(255, 31)); # Too big scale 31 specified for column 'age'. Maximum is 30. # eg:2 create table t12 (x float(255, 30)); create table t13 (x double(255, 30)); create table t14 (x decimal(65, 30)); insert into t12 values(1.11111111111111111111); insert into t13 values(1.11111111111111111111); insert into t14 values(1.11111111111111111111); select * from t12; # 1.111111164093017600000000000000 => 小數據,精度要求不高, 均採用float來存儲 * select * from t13; # 1.111111111111111200000000000000 select * from t14; # 1.111111111111111111110000000000 alter table t14 modify x decimal(10, 5); # 1.11111 => 限制了數據的存儲寬度
字符型
'''類型 char:定長 varchar:不定長 ''' '''寬度 限制存儲寬度 char(4):以4個字符存儲定長存儲數據 varchar(4):數據長度決定字符長度,爲可變長度存儲數據 ''' # eg: create table t15 (x char(4), y varchar(4)); insert into t15 values("zero", 'owen'); # '' | "" 都可以表示字符 select x,y from t15; # 正常 insert into t15 values("yanghuhu", 'lxxVSegon'); # 非安全模式數據丟失,能夠存放, 安全模式報錯 select x,y from t15; # 能夠正常顯示丟失後(不完整)的數據 insert into t15 values('a', 'b'); # 驗證數據所在字符長度 # 前提: 安全模式下以空白填充字符 set global sql_mode="strict_trans_tables,PAD_CHAR_TO_FULL_LENGTH"; # 重啓鏈接 select char_length(x), char_length(y) from t15; # a佔4 b佔1 '''重點: 存儲數據的方式 ** => 數據庫優化 char: 必定按規定的寬度存放數據, 以規定寬度讀取數據, 一般更佔空間 varchar: 首先根據數據長度計算所需寬度, 並在數據開始以數據頭方式將寬度信息保存起來, 是一個計算耗時過程, 取先讀取寬度信息,以寬度信息爲依準讀取數據, 一般節省空間 ''' 8: zero egon lxx yanghuhu 8: 4zero4egon3lxx8yanghuhu 注: varchar的數據頭佔1~2字節 規定char|varchar寬度均爲4,用來存放4個字符的數據, char存取更高效,char佔4字符,varchar佔5字符,char更省空間 總結: 數據長度相近的數據提倡用char來存放數據, 數據須要高速存取,以空間換時間, 採用char
時間類型
'''類型 year:yyyy(1901/2155) date:yyyy-MM-dd(1000-01-01/9999-12-31) time:HH:mm:ss datetime:yyyy-MM-dd HH:mm:ss(1000-01-01 00:00:00/9999-12-31 23:59:59) timestamp:yyyy-MM-dd HH:mm:ss(1970-01-01 00:00:00/2038-01-19 ??) ''' # eg: 1 create table t16(my_year year, my_date date, my_time time); insert into t16 values(); # 三個時間類型的默認值均是null insert into t16 values(2156, null, null); # 在時間範圍外,不容許插入該數據 insert into t16 values(1, '2000-01-01 12:00:00', null); # 2001 2000-01-01 null insert into t16 values(2019, '2019-01-08', "15-19-30"); # time報格式錯誤 => 按照時間規定格式存放數據 alter table t16 change my_year myYear year(2); # 時間的寬度修改後仍是採用默認寬度 => 不須要關係寬度 # eg:2 create table t17(my_datetime datetime, my_timestamp timestamp); insert into t17 values(null, null); # 能夠爲空, 不能爲null,賦值null採用默認值current_timestamp insert into t17 values('4000-01-01 12:00:00', '2000-01-01 12:00:00'); # 在各自範圍內能夠插入對應格式的時間數據 # datetime VS timestamp datetime:時間範圍,不依賴當前時區,8字節,能夠爲null timestamp:時間範圍,依賴當前時區,4字節,有默認值CURRENT_TIMESTAMP
枚舉與集合
'''類型 enum:單選 set:多選 ''' create table t19( sex enum('male','female','wasai') not null default 'wasai', # 枚舉 hobbies set('play','read','music') # 集合 ); insert into t19 values (null, null); # sex不能設置null insert into t19 values (); # wasai null insert into t19 (hobbies) values ('play,read'), ('music,play'); # sex採用默認值, 對hobbies字段添加兩條記錄 insert into t19 (sex,hobbies) values ('male,female', 'play'); # sex字段只能單選
約束條件
""" primary key:主鍵,惟一標識,表都會擁有,不設置爲默認找第一個 不空,惟一 字段,未標識則建立隱藏字段 foreing key:外鍵 unique key:惟一性數據, 該條字段的值須要保證惟一,不能重複 auto_increment:自增,只能加給key字段輔助修飾 not null:不爲空 default:默認值 unsigned:無符號 zerofill:0填充 """ 注: 1.鍵是用來說的io提供存取效率 2.聯合惟一 create table web ( ip char(16), port int, unique(ip,port) ); 3.聯合主鍵 create table web ( ip char(16), port int, primary key(ip,port) ); # eg:1 # 單列惟一 create table t20 ( id int unique ); # 聯合惟一 create table web ( ip char(16), port int, unique(ip,port) ); # 若是聯合兩個字段,兩個字段全相同才相同,不然爲不一樣 insert into web values ('10.10.10.10', 3306), ('10.10.10.10', 3306); # 注: # 1.表默認都有主鍵, 且只能擁有一個主鍵字段(單列主鍵 | 聯合主鍵) # 2.沒有設置主鍵的表, 數據庫系統會自上而下將第一個規定爲unique not null字段自動提高爲primary key主鍵 # 3.若是整個表都沒有unique not null字段且沒有primary key字段, 系統會默認建立一個隱藏字段做爲主鍵 # 4.一般必須手動指定表的主鍵, 通常用id字段, 且id字段通常類型爲int, 由於int類型能夠auto_increment # eg:2 create table t21(id int auto_increment); # 自增約束必須添加給key的字段 # eg:3 create table t21(id int primary key auto_increment); # 自增要結合key,不賦值插入,數據會自動自增, 且自增的結果一直被記錄保留 # eg:4 # 聯合主鍵 create table t22( ip char(16), port int, primary key(ip,port) ); # 若是聯合兩個字段,兩個字段全相同才相同,不然爲不一樣 insert into web values ('10.10.10.10', 3306), ('10.10.10.10', 3306);
表關係
1.表的詳細操做
1.修改表名 alter table 舊錶名 rename 新表名; 2.修改表的引擎與字符編碼 alter table 表名 engine="引擎名" charset="編碼名"; 3.複製表 * # 結構 create table 新表名 like 舊錶名;#只複製表的結構,包括約束 eg: create table nt like tt; # 將tt的表結構複製到新表nt中, 約束條件一併複製 # 結構+數據 create table 新表名 select * from 舊錶名; 注: 會複製表結構+數據, 但不會複製約束條件 eg: create table nt1 select * from tt where 1=2; # 將tt的表結構複製到新表nt1中, 鍵(相關)的約束條件不會複製 4.清空表 truncate 表名; 注:表被重置,自增字段重置
表中字段的詳細操做
create table t2( id int primary key auto_increment, x int, y int ); insert into t2(x, y) values(10, 20), (100, 200), (1000, 2000); 1.修改字段信息 alter table 表名 modify 字段名 類型[(寬度) 約束]; alter table t2 modify x bigint default 0; # 模式不一樣, 涉及精度問題 2.修改字段名及信息 alter table 表名 change 舊字段名 新字段名 類型[(寬度) 約束]; alter table t2 change y c char(10) not null; # 模式不一樣, 涉及類型轉換問題 3.添加字段名 # 末尾添加 alter table 表名 add 字段名 類型[(寬度) 約束], ..., add 字段名 類型[(寬度) 約束]; alter table t2 add age int, add gender enum("male", "female", "wasai") default "wasai"; # 首尾添加 alter table 表名 add 字段名 類型[(寬度) 約束] first; # 指定位添加:指定字段後 alter table 表名 add 字段名 類型[(寬度) 約束] after 舊字段名; alter table t2 add y int after x; 4.刪除字段名 alter table 表名 drop 字段名; alter table t2 drop y; 5.移動字段名 alter table t1 modify 字段名 類型[(寬度) 約束] first; alter table t1 modify 字段名 類型[(寬度) 約束] after 指定字段名; ??? 若是想刪除自增約束,如何操做? auto_increment 需跟在key後,好比自增的是id,表t1: alter table t1 change id id int;
特殊表 (mysql.user) => 用戶管理
# 操做前提:登陸root用戶 mysql -uroot -p 1.重要字段 Host | User | Password 2.新建用戶 create user 用戶名@主機名 identified by '密碼'; # 正確 create user zero@localhost identified by 'zero'; 注:insert into mysql.user(Host,User,Password) values("主機名","用戶名",password("密碼")); # 錯誤 3.設置用戶權限* grant 權限們 on 數據庫名.表名 to 用戶名@主機名 [with grant option]; grant create on db1.* to zero@localhost with grant option; # 權限有select,delete,update,insert,drop..., all表明全部權限 # 數據庫名,表名能夠用*替換,表明全部 # 設置權限時若是沒有當前用戶,會自動建立用戶,提倡使用 *重點*: grant all on db1.* to owen@localhost identified by 'owen'; # (建立用戶)設置權限 4.撤銷權限 revoke 權限名 on 數據庫名.表名 from 用戶名@主機名; revoke delete on db1.* from owen@localhost; 5.修改密碼 set password for 用戶名@主機名 = password('新密碼'); set password for owen@localhost = password('123'); 6.刪除用戶 drop user 用戶名@主機名; '''
表關係
社會中存儲須要能夠構建成表的數據, 它們造成的表,每每之間存儲某種或某些社會關係, mysql數據庫創建表結構就是社會中產生的各類數據, 分門別類管理 但mysql創建的(代碼層次的)表之間, 一樣須要處理表與表之間的關係 造成了 多對一 | 多對多 | 一對一 三種關係 ''' 外鍵直接影響數據庫效率,但會提升數據的完整性(安全),通常首先效率,由於安全能夠經過其餘方式來處理 '''
多對一
案例:員工employees表 | 部門department表 建表規則: 先創建主表,再創建從表,在從表中設置主表的惟一字段(一般爲主鍵)做爲外鍵 建表語法: create table 主表( id int primary key auto_increment, ... ); create table dep( id int primary key auto_increment, name varchar(16), work varchar(16) ); create table 從表( id int primary key auto_increment, ... 主表_id int, # 只是在從表中起了一個名字, 該名字和主表主鍵對應,全部起了個見名知義的名字 foreign key(主表_id) references 主表(惟一字段名id) on update cascade on delete cascade ); create table emp( id int primary key auto_increment, name varchar(16), salary float, dep_id int, foreign key(dep_id) references dep(id) on update cascade # 設置級聯 on delete cascade ); 插入記錄規則: 先插入主表數據,再插入從表數據 insert into dep values(1, '市場部', '銷售'), (2, '教學部', '授課'); insert into emp(name, salary, dep_id) values('egon', 3.0, 2),('yanghuhu', 2.0, 2),('sanjiang', 10.0, 1),('owen', 88888.0, 2),('liujie', 8.0, 1); 更新刪除數據: 兩表間相互影響,先從依賴數據入手,再進行更新刪除操做 eg:1 刪除主表dep中一個部門 delete from dep where id=1; => 從表emp中屬於該部門的員工都被刪除了 更新從表emp中一個員工的部門 update emp set dep_id=3 where name='egon'; <= 部門必須存在 insert into dep values(3, '管理部', '吃飯睡覺打豆豆, 明確團隊方針');
多對多
案例:做者author表 | 書book表 建表規則: 新建第三張表,經過兩個外鍵造成多對多關係 建表語法: create table 表1( id int primary key auto_increment, ... ); create table book( id int primary key auto_increment, name varchar(16), price int ); create table 表2( id int primary key auto_increment, ... ); create table author( id int primary key auto_increment, name varchar(16) ); create table 關係表( id int primary key auto_increment, 表1_id int, 表2_id int, foreign key(表1_id) references 表1(id) on update cascade on delete cascade, foreign key(表2_id) references 表2(id) on update cascade on delete cascade ); create table book_author( id int primary key auto_increment, book_id int, author_id int, foreign key(book_id) references book(id) on update cascade on delete cascade, foreign key(author_id) references author(id) on update cascade on delete cascade );
一對一
爲了提升效率,強行將海量數據中相關信息抽離成一個對應表來與依賴表造成一對一算法
案例:丈夫husband表 | 妻子wife表 建表規則: 未存放外鍵的表被依賴,稱之爲左表;存放外鍵的表示依賴表,稱之爲右表;先操做左邊再操做右表 建表語法: create table 左表( id int primary key auto_increment, ... ); create table husband( id int primary key auto_increment, name varchar(16) ); create table 右表( id int primary key auto_increment, ... 左表_id int unique, # 一對一的外鍵須要惟一性 foreign key(左表_id) references 左表(id) on update cascade on delete cascade ); create table wife( id int primary key auto_increment, name varchar(16), husband_id int unique, # 一對一的外鍵須要惟一性 foreign key(husband_id) references husband(id) on update cascade on delete cascade );
練習


1.建立一個stu表,字段有:自增主鍵id,不爲空姓名,默認值性別(枚舉類型),無限制身高 mysql> create table stu(id int primary key auto_increment, -> name varchar(20) not null, -> gender enum('male','female','wasai') not null default 'wasai', ->height float); 2.爲stu表依次插入如下三條數據 i)插入一條包含id,name,gender,height四個信息的數據 insert into stu values(1,"xiaoming","male",170); ii)插入一條name,gender,height三個信息的數據 insert into stu(name,gender,height) values("ming","male",170); iii)插入一條只有name信息的數據 insert into stu(name) values("lal"); 3.實現新表new_stu對已有表stu的字段、約束及數據的拷貝 create table new_stu like stu; insert into new_stu select * from stu; # 拷貝 4.建立一張有姓名、年齡的teacher表,在最後添加工資字段,在姓名後添加id主鍵字段 create table teacher(name varchar(20),age int); alter table teacher add salary float; alter table teacher add id int primary key after name; 5.思考:將5中id字段移到到表的最前方,造成最終字段順序爲id、姓名、年齡、工資 alter table teacher drop id; alter table teacher add id int primary key first; alter table teacher modify id int first; 6.完成 公民表 與 國家表 的 多對一 表關係的建立 mysql> create table country(id int primary key, -> name char); mysql> create table people(id int primary key, -> name char, -> age int, -> country_id int, -> foreign key(country_id) references country(id) -> on update cascade -> on delete cascade -> ); 7.完成 學生表 與 課程表 的 多對多 表關係的建立 create table stu1( id int primary key auto_increment, name varchar(16), age int ); create table course1( id int primary key auto_increment, name varchar(16) ); create table s_c( id int primary key auto_increment, stu1_id int, course1_id int, foreign key(stu1_id) references stu1(id) on update cascade on delete cascade, foreign key(course1_id) references course1(id) on update cascade on delete cascade ); 8.完成 做者表 與 做者簡介表 的 一對一 表關係的建立(思考爲何要這樣設計) create table author( id int primary key auto_increment, name varchar(16) ); create table jj( id int primary key auto_increment, name varchar(16), author_id int unique, foreign key(author_id ) references author(id) on update cascade on delete cascade );
查詢語句
sql_mode配置
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
增語法
1.全部數據按順序插入 insert [into] 表名 values (值1, ..., 值n)[, ..., (值1, ..., 值n)]; 2.指定字段匹配插入,能夠任意順序 insert [into] 表名(字段2, 字段1, ..., 字段n) values (值2, 值1, ..., 值n)[, ..., (值2, 值1, ..., 值n)]; 3.插入查詢結果 insert [into] 表1(字段1, ..., 字段n) select 字段1, ..., 字段n from 表2 [條件];# 表2與表1字段相同或要插入至表1的字段與表1相同時,表1後字段能夠*替 # eg:1 create table t1( id int auto_increment, x int, y int, primary key(id) ); insert t1 values(1,2,3),(2,3,4); # 按順序插入 insert into t1(y,x) values (5,4); # 按規定字段順序 create table nt1 like t1;複製表即完整結構 insert into nt1 select * from t1; # 建立新表並按照字段順序指定插入複製數據
刪語法
1.會記錄自增信息,操做會被日誌記錄,效率低 delete from [庫名].表名 [條件]; delete from t1; # 沒有條件的狀況下是清空全部數據。但會記錄自增的約束 insert into t1(x,y)values(6,66) 2.清空表,會重置自增信息 truncate table 表名; truncate table nt1; insert into nt1(x, y) values(6, 66);
改語法
update 表名 set 字段1=值1[, ..., 字段n=值n] [條件] updata tt1 set x=666;# 無條件,全改 update tt1 set x=777, z=555 where z<888; # 只修改知足條件的行 沒有知足條件的不改變
查詢語法
select [distinct] 字段1 [as 別名], ..., 字段n [as 別名] from [庫名.]表名 [ where 約束條件 group by 分組依據 =》查詢結果只能爲聚合結果或分組字段 having 篩選,過濾條件=》對聚合結果的篩選 order by 排序的字段=》asc|desc limit 限制顯示的條數 =>n| i,n ]; 注: 1.查表中全部字段用*表示 2.條件的書寫規則嚴格按照語法順序書寫,能夠缺省,但不能夠錯序 3.約束條件的流程:from -> where -> group by -> having -> distinct -> order by -> limit 4.字段能夠起別名 5.字段能夠直接作運算 select age + 1 'new_age' from emp; 6.分組後的條件都可以使用聚合函數 """ 3. def from(): return "查詢的文件" def where(file): return "條件篩選後的結果" def group_by(res): return "分組後的結果" def having(res): return "再次過濾後的結果" def distinct(res): return "去重後的結果" def order_by(res): return "排序後的結果" def limit(res): return "限制條數後的結果" def select(from=from, where=null, ..., limit=null): file = from() res = where(file) if where else file res = group_by(res) if group_by else res ... res = limit(res) if limit else res return res select(where=where, group_by=group_by) """
單表依賴數據
CREATE TABLE 'emp' ( `id` int(0) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, `gender` enum('男','女','未知') NULL DEFAULT '未知', `age` int(0) NULL DEFAULT 0, `salary` float NULL DEFAULT 0, `area` varchar(20) NULL DEFAULT '中國', `port` varchar(20) DEFAULT '未知', `dep` varchar(20), PRIMARY KEY (`id`) ); INSERT INTO `emp` VALUES (1, 'yangsir', '男', 42, 10.5, '上海', '浦東', '教職部'), (2, 'engo', '男', 38, 9.4, '山東', '濟南', '教學部'), (3, 'jerry', '女', 30, 3.0, '江蘇', '張家港', '教學部'), (4, 'tank', '女', 28, 2.4, '廣州', '廣東', '教學部'), (5, 'jiboy', '男', 28, 2.4, '江蘇', '蘇州', '教學部'), (6, 'zero', '男', 28, 8.8, '中國', '黃浦', '諮詢部'), (7, 'owen', '男', 28, 8.8, '安徽', '宣城', '教學部'), (9, 'ying', '女', 36, 1.2, '安徽', '蕪湖', '諮詢部'), (10, 'kevin', '男', 36, 5.8, '山東', '濟南', '教學部'), (11, 'monkey', '女', 28, 1.2, '山東', '青島', '教職部'), (12, 'san', '男', 30, 9.0, '上海', '浦東', '諮詢部'), (13, 'san1', '男', 30, 6.0, '上海', '浦東', '諮詢部'), (14, 'san2', '男', 30, 6.0, '上海', '浦西', '教學部');
經常使用內部函數
concat(字段1,...,字段n):完成字段的拼接 concat_ws(x, 字段1,...,字段n):完成字段的拼接,x爲鏈接符 lower():小寫 upper():大寫 ceil():向上取整 floor():向下取整 round():四捨五入
簡單查詢
去重前提:所查全部字段的綜合結果徹底一致/相同,才認爲是重複的,只保留重複中的一行數據 select distinct * from emp; # 去重,去的是所查結果徹底相同的 select contat(area,'-',port)[as 'xx'] from emp; # 上海-浦東 屬於起的別名xx 列 select contat_ws("-",name,area,port)[as 'xx'] from emp; #以"-"字符拼接後面的全部字段 select upper(name) 'name', gender, age from emp; # 能夠指定多個字段 select name, ceil(salary), floor(salary), round(salary) from emp where name='kevin'; # 數學函數
where 條件
1.比較運算符 = | < | > | <= | >= | != select * from emp where area!="上海"; 2.區間運算符 between 10 and 20:10~20 in(10, 20, 30):10或20或30 select * from emp where id between 3 and5; # [3,5]閉合的區間 select * from emp where id in (2,4,6,8);# 分離的區間 2,4,6,8,10,12,14都會被顯示 3.邏輯運算符 and | or | not select * from emp where area="山東" and port="濟南"; 4.類似運算符 like '_owen%':模糊匹配字符串owen,_表示一個字符,%表示任意字符 # 匹配的字段爲en,想獲得的結果爲owen select * from emp where name like '__en%'; # 在en前能夠出現2個任意字符, 以後能夠出現0或多個任意字符 需求: 查找姓名有數字的員工信息
正則匹配
對like的擴展,like完成模糊匹配,但功能侷限,能夠模糊個數,但不能模糊類型。正則能夠完成類型及個數的模糊匹配 語法:字段 regexp '正則表達式' 注:只支持部分正則語法 select * from emp where name regexp '.*[0-9]+.*';
group by 分組
注意:分了組,一個組就是一個總體, 你不能直接拿到組裏的具體的某一條信息(安全模式下會報錯), 可是你能夠用好比max的方法取出每一個組信息裏的最大值sql
分組:根據字段相同值造成不一樣的類別,不明確分組其實整個表就爲一個默認大組 緣由:把以值共性獲得的類別做爲考慮單位,再也不關係單條記錄,並且一組記錄 結果:只能考慮組內多條數據的聚合 結果 (聚合函數結果),分組的字段一樣是聚合結果,如:組內的最大最小值 sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 聚合函數: max():最大值 min():最小值 avg():平均值 sum():和 count():記數 group_concat():組內字段拼接,用來查看組內其餘字段 eg:1 每一個部門的平均薪資 select dep, avg(salary) '平均薪資' from emp group by dep; eg:2 每一個部門都有哪些人 select dep, group_concat(name) from emp group by dep; 需求: 各性別中附屬於教學部的最高薪資 select max(salary) '最高薪資', gender from emp where dep='教學部' group by gender; 思考: 想知道需求中員工的姓名 => 子查詢 思考: 想知道需求中員工的姓名 => 子查詢 上方結果: 男的最高薪資對應的人名, 女的最高薪資對應的人名 # select group_concat(name), max(salary) '最高薪資', gender from emp where dep='教學部' group by gender; 錯誤 select max(salary)"最高薪資",gender,name from emp where dep = "教學部" group by gender; ''' ''' 1. 14條數據部門有3個, 而且每一個部分有多條記錄, 能夠做爲分組依據, 同理, 性別也能夠 # select * from emp group by dep; # 非分組安全模式下, 能夠查詢非聚合結果, 顯示的是第一條記錄, 沒有意義, 分組安全模式下不能查詢非聚合結果的字段 select dep from emp group by dep; 2. 若是就像以姓名進行分組, 能夠, 但沒多大意義, 緣由name值基本上都不相同, 以組考慮會致使組內大多隻要一條記錄(自成一組), 組的利用就不是很強烈, 此類分組是無心義的 select name from emp group by name; # 能夠分組, 意義不大 考慮的三個問題: 以什麼分組(相同數據較多的字段) 分組後的考慮單位(組並不是組內的每一條記錄) 能夠查詢的結果(當前分組的字段及聚合函數造成的聚合結果)
解決分組中思考題的過程
# res = select max(salary) '最高薪資', gender from emp where dep='教學部' group by gender; # select name from emp where (salary 跟 res做比較) # 一個查詢依賴於另外一個查詢的結果 => 一個查詢的結果做爲另一個查詢的條件 => 子查詢
子查詢
子查詢:將一條查詢結果做爲另一條查詢的條件 語法:一條select語句用()包裹獲得的結果做爲另外一條select語句的條件 # 僞sql: select * from emp where salary =|in (select salary from emp where 條件) 單行子查詢: 子查詢語句的結果爲一行數據,能夠結合 = | < | > | <= | >= | != 運算符來完成父查詢 select salary from emp where salary > 10; # => 做爲子查詢 # 查詢姓名,性別.地區,基於薪資大於10的結果的查詢結果 eg: 1 select name, gender, area from emp where salary = (select salary from emp where salary > 10); 多行子查詢: 子查詢語句的結果爲多行數據,能夠結合 in | all | any 運算符來完成父查詢 in:任意單一值,只能考慮子查詢中的一個結果 all:所有值,將子查詢結果做爲一個總體考慮 any:任意多個值:子查詢的每個結果均可以做爲參考依據 eg: 2 # 子查詢的結果 (9.4, 3) select name from emp where salary in (select max(salary) '最高薪資' from emp where dep='教學部' group by gender); # 遍歷14條數據, 14條數據的salary在(9.4, 3)區域中,就能夠完成匹配, 結果爲兩條(9.4和3那兩條) select * from emp where salary < all(select max(salary) '最高薪資' from emp where dep='教學部' group by gender); # 遍歷14條數據, salary要小於(9.4, 3)中的每個, 反映就是小於3, 結果爲薪資1.2,2.4的那四條數據 select * from emp where salary > any(select max(salary) '最高薪資' from emp where dep='教學部' group by gender); # 遍歷14條數據, salary大於9.4或大於3的數據均知足條件, 結果就是刨除小於等於3的那幾條數據
having 篩選
why:完成在分組以後的篩選 注意:having條件是實現聚合結果層面上的篩選 => 拿聚會結果完成判斷 需求: 1.各部門的平均薪資 select dep, avg(salary) '平均薪資' from emp group by dep; 2.平均薪資大於6w的部門(部門與部門的平均薪資) 解決: 以dep進行分組, 以avg(salary)做爲判斷條件(篩選) select dep, avg(salary) '平均薪資' from emp group by dep having avg(salary) > 6; # 總結: having經過聚合函數結果完成篩選 select max(salary) from emp having max(salary) > 9.4; # 雖然沒有明確書寫group by, 但在having中使用了聚合函數,因此該查詢就將整個表當作一個默認大表來考慮,因此查詢的字段只能爲聚合函數的結果
order by 排序
why:完成排序 注意:可使用聚合函數,哪怕沒有明確group by 升序 | 降序:asc | desc eg:order by age desc => 按照年齡降序 select * from emp order by age desc; 需求: 將部門按照工資降序方式排序 select dep, avg(salary) from emp group by dep order by avg(salary) desc;
limit 限制
why:限制最終結果的顯示數據行數 注意:limit只與數字結合使用 應用: limit 1:只能顯示一行數據 limit 6,5:從第6+1行開始顯示5條數據(索引從0開始) select * from emp limit 1; select * from emp limit 6,5; 需求: 得到薪資最高的人的一條信息 select * from emp order by salary desc limit 1;
練習


1、查詢教學部山東人的平均薪資 select avg(salary)"平均薪資",area from emp where dep="教學部" group by area having area="山東"; select avg(salary)"平均薪資",dep from emp where area="山東" group by dep having dep="教學部"; select avg(salary)"平均薪資",area,dep from emp where dep="教學部" and area="山東"; 2、查詢姓名中包含英文字母n而且居住在上海的人的全部信息 select area,name from emp where area="上海" and name regexp ".*n.*"; 3、查詢姓名中包含英文字母n但不包含數字的人的全部信息 select * from emp where name regexp ".*n.*"and name not regexp ".*[0-9]+.*"; 4、查看各部的平均年齡並升序排序。升序時,asc可省 pythonx select avg(age),dep from emp group by dep order by avg(age) asc; 5、思考:按照年齡升序的基礎上再姓名降序查看全部人的信息 select * from emp order by age asc,name desc; 六、查詢各部門中年紀最大的人的姓名與居住地(戶籍+區域) select name,dep,concat_ws("-",area,port)"居住地" from emp where(age,dep) in (select max(age),dep from emp group by dep); select name, concat(area, '-', port), dep from emp where (age,dep) in (select max(age),dep from emp group by dep); 7、查詢不一樣年齡層次平均薪資大於5w組中工資最高者的姓名與薪資 select age,name,avg(salary)"平均薪資",max(salary)"最高薪資" from emp group by age having avg(salary)>5 ; # 非安全模式下可行,但name不是聚合,不可直接查,安全模式下報錯 select age,name,salary from emp where (salary,age) in(select max(salary),age from emp group by age having avg(salary)>5);
多表操做
多表依賴數據
create table dep( id int primary key auto_increment, name varchar(16), work varchar(16) ); create table emp( id int primary key auto_increment, name varchar(16), salary float, dep_id int ); insert into dep values(1, '市場部', '銷售'), (2, '教學部', '授課'), (3, '管理部', '開車'); insert into emp(name, salary, dep_id) values('egon', 3.0, 2),('yanghuhu', 2.0, 2),('sanjiang', 10.0, 1),('owen', 88888.0, 2),('liujie', 8.0, 1),('yingjie', 1.2, 0);
笛卡爾積(交叉鏈接)
# 需求: # 查看每位員工的部門的全部信息 select * from emp; select * from dep; # 子查詢, 最終結果只能顯示單表的信息, 但需求是同時顯示兩張表的信息 => 先將兩張表合成一張表 select * from emp where dep_id in (select id from dep); # Empty set (0.06 sec) 默認查詢結果:笛卡爾積 select * from emp, dep; # Empty set (0.07 sec) 數學概念:A{a, b}*B{0, 1, 2} => O{{a, 1}, {a, 2}, {a, 3}, {b, 1}, {b, 2}, {b, 3}} 交叉查詢:select * from emp,dep;| select * from emp course join dep; # 作了篩選, 結果<=完整數據, 非笛卡爾積 select * from emp, dep where db2.emp.dep_id = db2.dep.id; # 同sql語句上表現是從兩張表拿數據 # 注意: 同時查詢兩張表造成新的表,能夠稱之爲虛擬表, 原表與表之間可能存在重複字段, 同時使用時須要明確所屬表,必要時還需明確所屬數據庫
合理對應關係的虛擬表
select * from emp, dep where emp.dep_id = dep.id; 注:虛擬表中可能出現重複字段,須要在sql語句中明確實體表名
多表鏈接(*****)
===>虛擬的單表
用專業的語法來關聯多表
內鏈接
內鏈接:結果爲兩張表有對應關係的數據(emp有dep無\emp無dep有的數據均不會被顯示) 語法:左表 inner join 右表 on 兩表有關聯的字段的條件 eg: select * from emp inner join dep on emp.dep_id = dep.id;
左鏈接
左鏈接:在內鏈接的基礎上還保留左表的記錄 語法:左表 left join 右表 on 兩表有關聯的字段的條件 eg: select * from emp left join dep on emp.dep_id = dep.id;
右鏈接
右鏈接:在內鏈接的基礎上還保留右表的記錄 語法:左表 right join 右表 on 兩表有關聯的字段的條件 eg: select * from emp right join dep on emp.dep_id = dep.id;
全鏈接
#注意:mysql不支持全外鏈接 full JOIN #強調:mysql可使用此種方式間接實現全外鏈接 全鏈接:在內鏈接的基礎上分別保留這左表右表的記錄 語法:mysql沒有full join on語法,但能夠經過去重達到效果 eg: select * from emp left join dep on emp.dep_id = dep.id union # ??? select * from emp right join dep on emp.dep_id = dep.id;
練習
1.查詢每一位員工對應的工做職責 # 每一位員工 => 左表爲emp表, 那麼左表的全部數據均須要被保留, 全部採用左鏈接 => 左表爲dep表, 那麼右表的全部數據均須要被保留, 全部採用右鏈接 # select emp.name, dep.work from emp left join dep on emp.dep_id = dep.id; select emp.name, dep.work from dep right join emp on emp.dep_id = dep.id; 2.查詢每個部門下的員工們及員工職責 # select max(dep.name), max(dep.work), group_concat(emp.name) from emp right join dep on emp.dep_id = dep.id group by dep_id; # 分析過程 # 每個部門 => dep的信息要被所有保留, 須要分組 # 員工職責 => dep.work, 因爲分組不能直接被查詢 => 須要用聚合函數處理 # 員工們 => emp.name作拼接 => group_concat(emp.name) # 分組的字段 => 部門 => emp.dep_id => emp.dep_id能夠直接被查詢,但沒有顯示意義 => dep.name用來顯示 => dep.name須要用聚合函數處理 select max(dep.name), max(dep.work), group_concat(emp.name) from dep left join emp on emp.dep_id = dep.id group by emp.dep_id; # 注: on在where條件關鍵詞之左
navicat
Navicat Premium是一款專業的數據管理工具, 支持鏈接MsSql、MySQL、SQLite、Oracle、PostgreSQL等類型的數據庫, 可導入/導出鏈接。編輯數據庫各項數據。 同步傳輸數據或數據庫結構, 在數據庫或模式中查找數據。 1. 安裝navicat 2.鏈接數據庫,並建庫 3.建立表、設置字段、插入數據 4.新建查詢
python使用mysql
# 模塊pymysql # 安裝並導入pymysql: pip3 insatll pymysql # 經過pymysql操做數據庫分四步: 1.創建鏈接 conn = pymysql.connect(host="localhost", port=3306, db='db2', user='root', password='root') 2.設置字典類型遊標 cursor = conn.cursor(pymysql.cursors.DictCursor) line=cursor.execute(sql,args)args=>防注入,其可爲元組|列表|字典 3.執行sql語句並使用執行結果 # 書寫sql語句 sql = 'select * from emp' # 執行sql語句, 有返回值, 返回值爲獲得的記錄行數 line = cursor.execute(sql) print(line) ''' # 使用執行的結果: date=cursor.fetchone()當前遊標日後獲取一行記錄 cursor.fetchall()當前遊標日後全部的記錄 scroll(num, mode="relative|absolute") relative: 遊標從當前位置日後移動num行 ablolute: 遊標從頭日後移動num行, 通常能夠結合line來使用能定位到任意位置 ''' tag = cursor.fetchone() # 第一條 print(tag) print(tag['salary']) tag = cursor.fetchone() # 第二條 print(tag) cursor.scroll(1, mode='relative') # 偏移第三條 # cursor.scroll(line - 1, mode='absolute') # 指針絕對, 遊標永遠從頭開始偏移 tags = cursor.fetchall() # 第四條到最後 print(tags) 4.斷開鏈接 cursor.close() conn.close()
pymysql處理了sql注入
# 什麼是sql注入: # 經過書寫sql包含(註釋相關的)特殊字符, 讓原有的sql執行順序發生改變, 從而改變執行獲得的sql # 目的: # 繞過原有的sql安全認證, 達到對數據庫攻擊的目的 # 沒有處理sql注入的寫法 sql = 'select * from user where usr="%s" and pwd="%s"' % (usr, pwd) res = cursor.execute(sql) import pymysql conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='db2') cursor = conn.cursor(pymysql.cursors.DictCursor) # 登陸 # 獲得用戶輸入的帳戶密碼 usr = input("請輸入用戶名: ") #abc pwd = input("請輸入密碼: ") #123 # sql注入 # 1.知道用戶名: abc" -- hehe | ooo # select * from user where usr="abc" -- hehe" and pwd="ooo" # 2.不知道用戶名 aaa" or 1=1 -- hehe | 000 # select * from user where usr="aaa" or 1=1 -- hehe" and pwd="000" # 處理方式 # 對輸入的帳戶密碼作徹底處理 => 不可能造成達到sql注入的特殊語法 => 正則 # 和數據庫的帳戶密碼進行配對 # sql = 'select * from user where usr="%s" and pwd="%s"' % (usr, pwd) # select * from user where usr="abc" and pwd="123" # res = cursor.execute(sql) # pymysql已經處理了sql注入 sql = 'select * from user where usr=%s and pwd=%s' res = cursor.execute(sql, (usr, pwd)) # print(res) if res: print('login success') else: print('login failed') cursor.close() conn.close()
增刪改
import pymysql conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='db2') cursor = conn.cursor(pymysql.cursors.DictCursor) # 增 # 增sql語句 sql1 = 'insert into user(usr, pwd) values (%s, %s)'# cursor執行sql語句,在內存中完成了對數據的插入, 但不能將數據存放到硬盤.會將id完成自增 # 在內存中一次插入一條 cursor.execute(sql1, ("opq", "123")) # 在內存中一次插入多條 cursor.executemany(sql1, [("aaa", "000"), ("bbb", "111")]) # 將內存中的數據提交到硬盤中, 完成真實意義上的數據存儲 conn.commit() # 刪 sql2 = 'delete from user where usr=%s' cursor.execute(sql2, ("aaa")) conn.commit() # 改 sql3 = 'update user set pwd=%s where usr=%s' res = cursor.execute(sql3, ("222", "bbb")) conn.commit()
練習
一、準備數據
create table book( id int primary key auto_increment, name varchar(16), price int ); create table author( id int primary key auto_increment, name varchar(16) ); create table book_author( id int primary key auto_increment, book_id int, author_id int ); insert into book values(1, '葵花寶典', 88), (2, '九陰真經', 78), (3, '辟邪劍譜', 99); insert into author values(1, '張全蛋'), (2, '李誕'), (3, '寡人'); insert into book_author(id, book_id, author_id) values(1,1,1),(2,1,2),(3,1,3),(4,2,1),(5,2,3),(6,3,2),(7,3,2);
id | name | pirce |
---|---|---|
1 | 葵花寶典 | 88 |
2 | 九陰真經 | 78 |
3 | 辟邪劍譜 | 99 |
id | name |
---|---|
1 | 張全蛋 |
2 | 李誕 |
3 | 寡人 |
id | book_id | author_id |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 1 | 3 |
4 | 2 | 1 |
5 | 2 | 3 |
6 | 3 | 2 |
7 | 3 | 2 |
二、查看每本書的做者們
select book.name,group_concat(distinct author.name) from book left join book_author on book.id=book_author.book_id inner join author on author.id=book_author.author_id group by book.name; 1.去重 select distinct book_id,author_id from book_author; 2.連表 book inner join author inner join (select distinct book_id,author_id from book_author)as ba on book_id=ba.book_id and author.id=ba.author_id; 3.分組 group by ba.book_id; 4.查詢字段 select book.name,group_concat(author.name) 5.結果 select max(book.name),group_concat(author.name)from book inner join author inner join (select distinct book_id,author_id from book_author; )as ba on book_id=ba.book_id and author.id=ba.author_id group by ba.book_id;
視圖
三、查看每位做者都有哪些做品
select author.name,group_concat(distinct book.name) from book right join book_author on book.id=book_author.book_id inner join author on author.id=book_author.author_id group by author.name;
四、假設每本書各賣出10本,每位做者的收益是多少
按做者分組--》看查啥字段--》=好--》看查出的字段是幾條數據--》數據關係--》再按做者分組,求和 select author.name author_name,sum(book.price*10) money from author,book,book_author c where author.id = c.author_id and book.id = c.book_id group by author.name;
五、一個歷史操做殘留的數據庫表user,有自增主鍵id,帳號usr及密碼pw兩個字段,有4條數據,但數據id值分別爲2,3,5,7(第1條數據等被刪除操做了),現需求要保證數據主內容不變,重構數據索引從1開始,4條數據id分別修改成1,2,3,4
# 備份數據 create table new_user select * from user; # 清空表 truncate user; # 重構數據 insert into user(usr,pwd)select usr,pwd from new_user; # 刪除備份 drop table new_user;
六、經過pymysql完成對錶emp的徹底拷貝(結果+數據),獲得新表new_emp
import pymysql conn=pymysql.connect( host="localhost", port=3306, user="root", password="root", db="db1" ) cursor = conn.cursor(pymysql.cursors.DictCursor) sql1 = "create table new_emp like emp " sql2 ="insert into new_emp select * from emp" cursor.execute(sql) cursor.execute(sql) conn.commit() cursor.close() conn.close()
七、實現註冊功能
帳號密碼由控制檯錄入
數據庫已有該用戶則提示用戶登陸
數據庫沒有該用戶則完成註冊
usr=input("輸入帳號") pwd=input("輸入密碼") # 先查
emmm 我知道我沒寫完,就醬。。何時想起來有空了我會再捋一遍的!!!
數據庫高級
一.視圖
what: 由一張表或多張表的查詢結果造成的一張虛擬表 -- 有實體的表結構 | 沒有實體屬於本身的數據, 數據來源於真實表 | (沒有鍵, 存在於真實表) why: 多張表的某一查詢結果須要不斷的重複使用(複用性) | 一張表能夠經過不一樣需求(權限)來造成多張子表
視圖的另外一個做用是,能夠不一樣的視圖來展現開放不一樣數據的訪問
例如,同一張工資表,老闆能夠查看所有,部門主管能夠查看該部門全部人,員工只能看本身的一條記錄
建立視圖
CREATE [OR REPLACE] VIEW view_name [(column_list字段名們)] AS select_statement查詢結果
加上OR REPLACE 時若是已經存在相同視圖則替換原有視圖
column_list指定哪些字段要出如今視圖中
注意:因爲是一張虛擬表,視圖中的數據實際上來源於其餘其餘表,因此在視圖中的數據不會出如今硬盤上
使用視圖
視圖是一張虛擬表 因此使用方式與普通表沒有任何區別
查看視圖
1.desc view_name; //查看數據結構 2.show create view view_name;//查看 建立語句
修改視圖
alter view_name select_statement
刪除視圖
drop view view_name
案例1: 簡化多表sql語句
#準備數據 create database db02 charset utf8; use db02 create table student( s_id int(3), name varchar(20), math float, chinese float ); insert into student values(1,'tom',80,70),(2,'jack',80,80),(3,'rose',60,75); create table stu_info( s_id int(3), class varchar(50), addr varchar(100) ); insert into stu_info values(1,'二班','安徽'),(2,'二班','湖南'),(3,'三班','黑龍江'); #建立視圖包含 編號 學生的姓名 和班級 create view stu_v (編號,姓名,班級) as select student.s_id,student.name ,stu_info.class from student,stu_info where student.s_id=stu_info.s_id; # 查看視圖中的數據 select *from stu_v;
案例2: 隔離數據
# 建立工資表 create table salarys( id int primary key, name char(10), salary double, dept char(10) ); insert into salarys values (1,"劉強東",900000,"市場"), (2,"馬雲",800090,"市場"), (3,"李彥宏",989090,"財務"), (4,"馬化騰",87879999,"財務"); # 建立市場部視圖 create view dept_sc as select *from salarys where dept = "市場"; # 查看市場部視圖 select *from dept_sc;
注意 對視圖數據的insert update delete 會同步到原表中,但因爲視圖多是部分字段,不少時候會失敗
總結:mysql能夠分擔程序中的部分邏輯,但這樣一來後續的維護會變得更麻煩
若是須要改表結構,那意味着視圖也須要相應的修改,沒有直接在程序中修改sql來的方便
二.觸發器
what: 在一個表數據發生變化時(增刪改), 觸發(對另外一表)某項功能(執行sql語句塊) why: 須要完成的操做基於對某一種表的增刪改操做
建立觸發器
delimiter && CREATE TRIGGER t_name t_time t_event ON table_name FOR EACH ROW begin stmts..... end && delimiter ;
結論: 觸發器內不能出現查詢語句(Not allowed to return a result set from a trigger), 不對對自身表完成增刪改查
支持的時間點(t_time):時間發生前和發生先後 before|after
支持的事件(t_event): update insert delete
在觸發器中能夠訪問到將被修改的那一行數據 根據事件不一樣 能訪問也不一樣 update 可用OLD訪問舊數據 NEW訪問新數據 insert 可用NEW訪問新數據 delete 可用OLD訪問舊數據
能夠將NEW和OLD看作一個對象其中封裝了這列數據的全部字段
案例:有cmd表和錯誤日誌表,需求:在cmd執行失敗時自動將信息存儲到錯誤日誌表中
#準備數據 CREATE TABLE cmd ( id INT PRIMARY KEY auto_increment, USER CHAR (32), priv CHAR (10), cmd CHAR (64), sub_time datetime, #提交時間 success enum ('yes', 'no') #0表明執行失敗 ); #錯誤日誌表 CREATE TABLE errlog ( id INT PRIMARY KEY auto_increment, err_cmd CHAR (64), err_time datetime ); # 建立觸發器 delimiter // create trigger trigger1 after insert on cmd for each row begin if new.success = "no" then insert into errlog values(null,new.cmd,new.sub_time); end if; end// delimiter ; #往表cmd中插入記錄,觸發觸發器,根據IF的條件決定是否插入錯誤日誌 INSERT INTO cmd ( USER, priv, cmd, sub_time, success ) VALUES ('egon','0755','ls -l /etc',NOW(),'yes'), ('egon','0755','cat /etc/passwd',NOW(),'no'), ('egon','0755','useradd xxx',NOW(),'no'), ('egon','0755','ps aux',NOW(),'yes'); # 查看錯誤日誌表中的記錄是否有自動插入 select *from errlog;
delimiter
用於修改默認的行結束符 ,因爲在觸發器中有多條sql語句他們須要使用分號來結束,可是觸發器是一個總體,因此咱們須要先更換默認的結束符,在觸發器編寫完後在將結束符設置回分號
注意:
外鍵不能觸發事件 主表刪除了某個主鍵 從表也會相應刪除 可是並不會執行觸發器。觸發器中不能使用事務,相同時間點的相同事件的觸發器 不能同時存在
刪除觸發器
語法: drop trigger trigger_name; 案例: drop trigger trigger1;
一樣的這種需求咱們徹底能夠在python中來完成! mysql最想完成的事情是講全部能處理的邏輯所有放到mysql中,那樣一來應用程序開發者的活兒就變少了,相應的數據庫管理員的工資就高了,惋惜大多中小公司都沒有專門的DBA;
疑惑:修改行結束符後,觸發器內的sql語句任然是以分號結束,爲何? 實際上在mysql中輸入分號回車,mysql會當即將語句發送給服務器端,修改行結束符僅僅是告訴mysql客戶端,語句沒有寫完,不要當即發送!
三.事務
what: 一組sql操做, 要麼所有成功, 要麼所有失敗 why: 一組操做須要同時成功,不然就斷定爲失敗, 要將其構建成事務
例如轉帳操做,
1.從原有帳戶減去轉帳金額
2.給目標帳戶加上轉帳金額
若中間忽然斷電了或系統崩潰了,錢就不知去向了!
使用事務
start transaction; --開啓事物,在這條語句以後的sql將處在同一事務,並不會當即修改數據庫
commit;--提交事務,讓這個事物中的sql當即執行數據的操做,
rollback;--回滾事務,取消這個事物,這個事物不會對數據庫中的數據產生任何影響
案例:轉帳過程當中發生異常
#準備數據 create table account( id int primary key auto_increment, name varchar(20), money double ); insert into account values(1,'趙大兒子',1000); insert into account values(2,'劉大牛',1000); insert into account values(3,'豬頭三',1000); insert into account values(4,'王進',1000); insert into account values(5,'黃卉',1000); # 趙大兒子劉大牛佳轉帳1000塊 # 未使用事務 update account set money = money - 1000 where id = 1; update account set moneys = money - 1000 where id = 1; # money打錯了致使執行失敗 # 在python中使用事務處理 sql = 'update account set money = money - 1000 where id = 1;' sql2 = 'update account set moneys = money + 1000 where id = 2;' # money打錯了致使執行失敗 try: cursor.execute(sql) cursor.execute(sql2) conn.commit() except: conn.rollback()
注意:事務的回滾的前提是能捕捉到異常,不然沒法決定什麼時候回滾,Python中很簡單就實現了,另外mysql中須要使用存儲過程才能捕獲異常!
事務的四個特性:
原子性:事務是一組不可分割的單位,要麼同時成功,要麼同時不成功
一致性:事物先後的數據完整性應該保持一致,(數據庫的完整性:若是數據庫在某一時間點下,全部的數據都符合全部的約束,則稱數據庫爲完整性的狀態);
隔離性:事物的隔離性是指多個用戶併發訪問數據時,一個用戶的事物不能被其它用戶的事務所幹擾,多個併發事務之間數據要相互隔離
持久性:持久性是指一個事物一旦被提交,它對數據的改變就是永久性的,接下來即便數據庫發生故障也不該該對其有任何影響
事務的用戶隔離級別:
數據庫使用者能夠控制數據庫工做在哪一個級別下,就可與防止不一樣的隔離性問題
read uncommitted --不作任何隔離,可能髒讀,幻讀
read committed----能夠防止髒讀,不能防止不可重複讀,和幻讀,
Repeatable read --能夠防止髒讀,不可重複讀,不能防止幻讀
Serializable--數據庫運行在串行化實現,全部問題都沒有,就是性能低
修改隔離級別:
select @@tx_isolation;--查詢當前級別
set[session|global] transaction isolation level .... ;修改級別
實例:
set global transaction isolation level Repeatable read ; 修改後從新鏈接服務器生效
四.存儲過程
1.什麼是存儲過程
存儲過程是一組任意的sql語句集合,存儲在mysql中,調用存儲過程時將會執行其包含的全部sql語句;與python中函數相似;
2.爲何使用存儲過程
回顧觸發器與視圖都是爲了簡化應用程序中sql語句的書寫,可是仍是須要編寫,而存儲過程當中能夠包含任何的sql語句,包括視圖,事務,流程控制等,這樣一來,應用程序能夠從sql語句中徹底解放,mysql能夠替代應用程序完成數據相關的的邏輯處理!
那咱們之後都是用存儲過程不就完了?
三種開發方式對比
1.應用程序僅負責業務邏輯編寫,全部與數據相關的邏輯都交給mysql來完成,經過存儲過程(推薦使用)
優勢:
應用程序與數據處理完解耦合,一堆複雜的sql被封裝成了一個簡單的存儲過程,考慮到網絡環境因素,效率高
應用程序開發者不須要編寫sql語句,開發效率高
缺點:
python語法與mysql語法區別巨大,學習成本高,而且各類數據庫的語法大不相同,因此移植性很是差
應用程序開發者與BDA的跨部門溝通成本高,形成總體效率低
2.應用程序不只編寫業務邏輯,還須要編寫全部的sql語句
優勢:擴展性高,對於應用程序開發者而言,擴展性和維護性相較於第一種都有所提升
缺點:sql語句過於複雜,致使開發效率低,且須要考慮sql'優化問題
3.應用程序僅負責業務邏輯,sql語句的編寫交給ORM框架,(經常使用解決方案)
優勢:應用程序開發者不須要編寫sql語句,開發效率高
缺點:執行效率低,因爲須要將對象的操做轉化爲sql語句,且須要經過網絡發送大量sql
建立存儲過程
create procedure pro_name(p_Type p_name data_type) begin sql語句......流程控制 end
p_type 參數類型
in 表示輸入參數
out 表示輸出參數
inout表示既能輸入又能輸出
p_name 參數名稱
data_type 參數類型 能夠是mysql支持的數據類型
案例:使用存儲過程完成對student表的查詢
delimiter // create procedure p1(in m int,in n int,out res int) begin select *from student where chinese > m and chinese < n; #select *from student where chineseXXX > m and chinese < n; 修改錯誤的列名以測試執行失敗 set res = 100; end// delimiter ; set @res = 0; #調用存儲過程 call p1(70,80,@res); #查看執行結果 select @res;
須要注意的是,存儲過程的out類參數必須是一個變量,不能是值;
在python中調用存儲過程
import pymysql #創建鏈接 conn = pymysql.connect( host="127.0.0.1", user="root", password="admin", database="db02" ) # 獲取遊標 cursor = conn.cursor(pymysql.cursors.DictCursor) # 調用用存儲過程 cursor.callproc("p1",(70,80,0)) #p1爲存儲過程名 會自動爲爲每一個值設置變量,名稱爲 @_p1_0,@_p1_1,@_p1_2 # 提取執行結果是否有結果取決於存儲過程當中的sql語句 print(cursor.fetchall()) # 獲取執行狀態 cursor.execute("select @_p1_2") print(cursor.fetchone())
此處pymysql會自動將參數都設置一個變量因此能夠直接傳入一個值,固然值若是做爲輸出參數的話,傳入什麼都無所謂!
刪除存儲過程
drop procedure 過程名稱;
修改存儲過程意義不大,不如刪除重寫!
查看存儲過程
#當前庫全部存儲過程名稱 select `name` from mysql.proc where db = 'db02' and `type` = 'PROCEDURE'; #查看建立語句 show create procedure p1;
存儲過程當中的事務應用
存儲過程當中支持任何的sql語句包括事務!
案例:模擬轉帳中發送異常,進行回滾
delimiter // create PROCEDURE p5( OUT p_return_code tinyint ) BEGIN DECLARE exit handler for sqlexception BEGIN # ERROR set p_return_code = 1; rollback; END; # exit 也能夠換成continue 表示發送異常時繼續執行 DECLARE exit handler for sqlwarning BEGIN # WARNING set p_return_code = 2; rollback; END; START TRANSACTION; update account set money = money - 1000 where id = 1; update account set moneys = money - 1000 where id = 1; # moneys字段致使異常 COMMIT; # SUCCESS set p_return_code = 0; #0表明執行成功 END // delimiter ; #在mysql中調用存儲過程 set @res=123; call p5(@res); select @res;
總結:拋開溝通成本,學習成本,存儲過程無疑是效率最高的處理方式,面試會問,一些公司也有一些現存的存儲過程,重點掌握!
五.函數
函數與python中的定義一致,再也不囉嗦!
內置函數
日期相關:
字符串相關:
數字相關:
其餘函數:
固然也包括以前學習的聚合函數
自定義函數
語法:
CREATE FUNCTION f_name(paramters) returns dataType; return value;
說明: paramters 只能是in 輸入參數 參數名 類型 必須有返回值 不能呢加begin 和end returns 後面是返回值的類型 這裏不加分號 return 後面是要返回的值
案例: 將兩數相加
create function addfuntion(a int,b int) returns int return a + b; #執行函數 select addfuntion(1,1);
注意:
函數只能返回一個值 函數通常不涉及數據的增刪改查 就是一個通用的功能 調用自定義的函數 與調用系統的一致 不須要call 使用select 可得到返回值 函數中不能使用sql語句 就像在java中不能識別sql語句同樣
六.數據備份與恢復
使用mysqldump程序進行備份
mysqldump -u -p db_name [table_name,,,] > fileName.sql
能夠選擇要備份哪些表 若是不指定表明 所有備份
#示例: #單庫備份 mysqldump -uroot -p123 db1 > db1.sql mysqldump -uroot -p123 db1 table1 table2 > db1-table1-table2.sql #多庫備份 mysqldump -uroot -p123 --databases db1 db2 mysql db3 > db1_db2_mysql_db3.sql #備份全部庫 mysqldump -uroot -p123 --all-databases > all.sql
使用 mysql 進行恢復
1.退出數據庫後
mysql -u -p < filename.sql;
2.不用退出數據庫
2.1 建立空數據庫
2.2選擇數據庫
2.3而後使用source filename; 來進行還原
use db1;
source /root/db1.sql
數據庫遷移
務必保證在相同版本之間遷移 # mysqldump -h 源IP -uroot -p123 --databases db1 | mysql -h 目標IP -uroot -p456
七.流程控制
if語句的使用
if 條件 then 語句; end if;
第二種 if elseif if 條件 then 語句1; elseif 條件 then 語句2; else 語句3; end if;
案例:編寫過程 實現 輸入一個整數type 範圍 1 - 2 輸出 type=1 or type=2 or type=other;
create procedure showType(in type int,out result char(20)) begin if type = 1 then set result = "type = 1"; elseif type = 2 then set result = "type = 2"; else set result = "type = other"; end if; end
CASE 語句
大致意思與Swtich同樣的 你給我一個值 我對它進行選擇 而後執行匹配上的語句 語法:
create procedure caseTest(in type int) begin CASE type when 1 then select "type = 1"; when 2 then select "type = 2"; else select "type = other"; end case; end
定義變量
declare 變量名 類型 default 值; 例如: declare i int default 0;
WHILE循環
循環輸出10次hello mysql create procedure showHello() begin declare i int default 0; while i < 10 do select "hello mysql"; set i = i + 1; end while; end
LOOP循環的
沒有條件 須要本身定義結束語句 語法:
輸出十次hello mysql; create procedure showloop() begin declare i int default 0; aloop: LOOP select "hello loop"; set i = i + 1; if i > 9 then leave aloop; end if; end LOOP aloop; end
REPEAT循環
#相似do while #輸出10次hello repeat create procedure showRepeat() begin declare i int default 0; repeat select "hello repeat"; set i = i + 1; until i > 9 end repeat; end #輸出0-100之間的奇數 create procedure showjishu() begin declare i int default 0; aloop: loop set i = i + 1; if i >= 101 then leave aloop; end if; if i % 2 = 0 then iterate aloop; end if; select i; end loop aloop; end
索引
在關係數據庫中,索引是一種單獨的、物理的對數據庫表中一列或多列的值進行排序的一種存儲結構; 也稱之爲key
索引的做用至關於圖書的目錄,能夠根據目錄中的頁碼快速找到所需的內容。
爲何須要索引
相比較下,對數據的讀操做會更加頻繁,比例在10:1左右,也就是說對數據庫的查詢操做是很是頻繁的
隨着時間的推移,表中的記錄會愈來愈多,此時若是查詢速度太慢的話對用戶體驗是很是不利的
索引是提高查詢效率最有效的手段!
簡單的說索引的就是用幫咱們加快查詢速度的
須要注意的是:在數據庫中插入數據會引起索引的重建
小白的誤區
既然索引如此神奇,那之後只要速度慢了就加索引,
這種想法是很是low的,
索引是否是越多越好,而且有了索引後還要考慮索引是否命中
加上索引後對數據的寫操做速度會下降
索引的實現原理
如何能實現加快查詢的效果呢?
來看一個例子:
初版的新華字典共800頁,那時沒有檢字表,每一個字的詳細信息,隨機的羅列在書中,一同窗買回來查了一次,在也沒用過,由於沒有任何的數據結構,查字只能一頁一頁日後翻,反了兩小時沒翻着,只能放棄了!
後來出版社發現了這個問題,他們將書中全部字按照拼音音節順序進行了排序,拼音首字母爲a的排在最前,首字母爲z的排在最後:
如此一來再再也不須要一頁一頁的去查字了,而是先查看索引,找出字的拼音首字母到索引中進行對照,例如:找搭
字其拼音首字母爲d,因此直接找到D對應的索引目錄,很快就能定位到要找的搭
字在79頁,查詢速度獲得數量級的提高!
須要注意的是,原來內容爲800頁如今由於多了索引數據,總體頁數必然增長了
數據庫中的索引,實現思路與字典是一致的,須要一個獨立的存儲結構,專門存儲索引數據
本質上索引是經過不斷的縮小查詢範圍來提升查詢效率
磁盤IO問題(瞭解)
數據庫的數據最終存儲到了硬盤上
機械硬盤因爲設計原理,致使查找數據時須要有一個尋道時間與平均延遲時間,常規硬盤尋道爲5ms,平均延遲按照每分鐘7200轉來計算,7200/60 = 120 ; 1000/120/2 = 4ms 總共爲9ms,那麼9毫秒對於cpu而言已經很是很是的長了,足夠作不少運算操做,目前最新的處理器每秒能處理數萬億次運算,拿一個很是垃圾的處理器來舉例子,假設處理器每秒處理5億次計算,每毫秒是50萬次運算,9ms能夠進行450萬次運算,數據庫中成千上萬的數據,每條數據9ms顯然慢到不行!
操做系統預讀取(瞭解)
考慮到磁盤IO是很是高昂的操做,計算機操做系統作了一些優化,當一次IO時,不只獲取當前磁盤地址的數據,並且把相鄰的數據也都讀取到內存緩衝區內,由於局部預讀性原理告訴咱們,當計算機訪問一個地址的數據的時候,與其相鄰的數據也會很快被訪問到。每一次IO讀取的數據咱們稱之爲一頁(page)。具體一頁有多大數據跟操做系統有關,通常爲4k或8k,也就是咱們讀取一頁內的數據時候,實際上才發生了一次IO,這個理論對於索引的數據結構設計很是有幫助。
索引數據結構剖析
在字典的例子中咱們知道了,索引是獨立於真實數據的一個存儲結構,這個結構究竟是什麼樣的?
索引最終的目的是要儘量下降io次數,減小查找的次數,以最少的io找到須要的數據,此時B+樹閃亮登場
光有數據結構還不行,還須要有對應的算法作支持,就是二分查找法
有了B+數據結構後查找數據的方式就再也不是逐個的對比了,而是經過二分查找法來查找(流程演示)
另外,其實大多數文件系統都是使用B+是來完成的!
應該儘量的將數據量小的字段做爲索引
經過分析能夠發如今上面的樹中,查找一個任何一個數據都是3次IO操做, 可是這個3次並非固定的,它取決於數結構的高度,目前是三層,若是要存儲新的數據比99還大的數據時,發現葉子節點已經不夠了必須在上面加一個子節點,因爲樹根只能有一個,因此整個數的高度會增長,一旦高度增長則 查找是IO次數也會增長,因此:
應該儘量的將數據量小的字段做爲索引,這樣一個葉子節點能存儲的數據就更多,從而下降樹的高度;
例如:name
和id
,應當將id設置爲索引而不是name
最左匹配原則*
當b+樹的數據項是複合的數據結構,好比(name,age,sex)的時候(多字段聯合索引),b+樹會按照從左到右的順序來創建搜索樹,好比當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來肯定下一步的所搜方向,若是name相同再依次比較age和sex,最後獲得檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪一個節點,由於創建搜索樹的時候name就是第一個比較因子,必需要先根據name來搜索才能知道下一步去哪裏查詢。好比當(張三,F)這樣的數據來檢索時,b+樹能夠用name來指定搜索方向,但下一個字段age的缺失,因此只能把名字等於張三的數據都找到,而後再匹配性別是F的數據了, 這個是很是重要的性質,即索引的最左匹配特性。
彙集索引*
mysql官方文檔原文: 插入瞭解 或摺疊
MySQL爲表把它的數據詞典信息以.frm文件的形式存在數據庫目錄裏,這對全部MySQL存儲引擎都是真的。但 是每一個InnoDB表在表空間內的InnoDB內部數據詞典裏有它本身的條目。當MySQL移除表或數據庫,它不得不 刪除.frm文件和InnoDB數據詞典內的相應條目。這就是爲何你不能在數據庫之間簡單地移動.frm文件來移 動InnoDB表。
每一個InnoDB表有專門索引,被稱爲clustered index,對行的數據被存於其中。若是你對你的表定義一 個PRIMARY KEY, 主鍵的索引是集束索引。
若是你沒有爲表定義PRIMARY KEY,MySQL拾取第一個僅有NOT NULL列的UNIQUE索引做爲主鍵,並 且InnoDB把它看成集束索引來用。若是表中沒有這樣一個索引,InnoDB內部產生一個集束索引,其中 用InnoDB在這樣一個表內指定給行的行ID來排序行。行ID是一個6字節的域,它在新行被插入的時候簡單地增長。所以被行ID排序的行是物理地按照插入順序排的。
經過集束索引訪問一個行是較快的,由於行數據是在索引搜索引導的同一頁面。若是表是巨大的,當對比於傳 統解決方案,集束索引構架常常節約磁盤I/O。(在許多數據庫,數據傳統地被存在與索引記錄不一樣的頁)。
在InnoDB中,非集束索引裏的記錄(也稱爲第二索引)包含對行的主鍵值。InnoDB用這個 主鍵值來從集束索 引中搜索行。注意,若是主鍵是長的,第二索引使用更多空間。
簡單總結:
聚焦索引的特色:
葉子節點保存的就是完整的一行記錄,若是設置了主鍵,主鍵就做爲彙集索引,
若是沒有主鍵,則找第一個NOT NULL 且QUNIQUE的列做爲彙集索引,
若是也沒有這樣的列,innoDB會在表內自動產生一個彙集索引,它是自增的
輔助索引*
除了彙集索引以外的索引都稱之爲輔助索引或第二索引,包括 foreign key
與 unique
輔助索引的特色:
其葉子節點保存的是索引數據與所在行的主鍵值,InnoDB用這個 主鍵值來從彙集索引中搜查找數據
覆蓋索引
覆蓋索引指的是須要的數據僅在輔助索引中就能找到:
#假設stu表的name字段是一個輔助索引 select name from stu where name = "jack";
這樣的話則不須要在查找彙集索引數據已經找到
回表
若是要查找的數據在輔助索引中不存在,則須要回到彙集索引中查找,這種現象稱之爲回表
# name字段是一個輔助索引 而sex字段不是索引 select sex from stu where name = "jack";
須要從輔助索引中獲取主鍵的值,在拿着主鍵值到彙集索引中找到sex的值
查詢速度對比:
彙集索引 > 覆蓋索引 > 非覆蓋索引
正確使用索引
案例:首先準備一張表數據量在百萬級別
create table usr(id int,name char(10),gender char(3),email char(30)); #準備數據 delimiter // create procedure addData(in num int) begin declare i int default 0; while i < num do insert into usr values(i,"jack","m",concat("xxxx",i,"@qq.com")); set i = i + 1; end while; end // delimiter ; #執行查詢語句 觀察查詢時間 select count(*) from usr where id = 1; #1 row in set (3.85 sec) #時間在秒級別 比較慢 1. #添加主鍵 alter table usr add primary key(id); #再次查詢 select count(*) from usr where id = 1; #1 row in set (0.00 sec) #基本在毫秒級就能完成 提高很是大 2. #當條件爲範圍查詢時 select count(*) from usr where id > 1; #速度依然很慢 對於這種查詢沒有辦法能夠優化由於須要的數據就是那麼多 #縮小查詢範圍 速度立馬就快了 select count(*) from usr where id > 1 and id < 10; #當查詢語句中匹配字段沒有索引時 效率測試 select count(*) from usr where name = "jack"; #1 row in set (2.85 sec) # 速度慢 3. # 爲name字段添加索引 create index name_index on usr(name); # 再次查詢 select count(*) from usr where name = "jack"; #1 row in set (3.89 sec) # 速度反而下降了 爲何? #因爲name字段的區分度很是低 徹底沒法區分 ,由於值都相同 這樣一來B+樹會沒有任何的子節點,像一根竹竿每一都匹配至關於,有幾條記錄就有幾回io ,全部要注意 區分度低的字段不該該創建索引,不能加速查詢反而下降寫入效率, #同理 性別字段也不該該創建索引,email字段更加適合創建索引 # 修改查詢語句爲 select count(*) from usr where name = "aaaaaaaaa"; #1 row in set (0.00 sec) 速度很是快由於在 樹根位置就已經判斷出樹中沒有這個數據 所有跳過了 # 模糊匹配時 select count(*) from usr where name like "xxx"; #快 select count(*) from usr where name like "xxx%"; #快 select count(*) from usr where name like "%xxx"; #慢 #因爲索引是比較大小 會從左邊開始匹配 很明顯全部字符都能匹配% 因此全都匹配了一遍 4.索引字段不能參加運算 select count(*) from usr where id * 12 = 120; #速度很是慢緣由在於 mysql須要取出全部列的id 進行運算以後才能判斷是否成立 #解決方案 select count(*) from usr where id = 120/12; #速度提高了 由於在讀取數據時 條件就必定固定了 至關於 select count(*) from usr where id = 10; #速度天然快了 5.有多個匹配條件時 索引的執行順序 and 和 or #先看and #先刪除全部的索引 alter table usr drop primary key; drop index name_index on usr; #測試 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx2@qq.com"; #1 row in set (1.34 sec) 時間在秒級 #爲name字段添加索引 create index name_index on usr(name); #測試 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx2@qq.com"; #1 row in set (17.82 sec) 反而時間更長了 #爲gender字段添加索引 create index gender_index on usr(gender); #測試 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx2@qq.com"; #1 row in set (16.83 sec) gender字段任然不具有區分度 #爲id加上索引 alter table usr add primary key(id); #測試 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx1@qq.com"; #1 row in set (0.00 sec) id子彈區分度高 速度提高 #雖然三個字段都有索引 mysql並非從左往右傻傻的去查 而是找出一個區分度高的字段優先匹配 #改成範圍匹配 select count(*) from usr where name = "jack" and gender = "m" and id > 1 and email = "xxxx1@qq.com"; #速度變慢了 #刪除id索引 爲email創建索引 alter table usr drop primary key; create index email_index on usr(email); #測試 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx2@qq.com"; #1 row in set (0.00 sec) 速度很是快 #對於or條件 都是從左往右匹配 select count(*) from usr where name = "jackxxxx" or email = "xxxx0@qq.com"; #注意 必須or兩邊都有索引纔會使用索引 6.多字段聯合索引 爲何須要聯合索引 案例: select count(*) from usr where name = "jack" and gender = "m" and id > 3 and email = "xxxx2@qq.com"; 假設全部字段都是區分度很是高的字段,那麼除看id爲誰添加索引都可以提高速度,可是若是sql語句中沒有出現因此字段,那就沒法加速查詢,最簡單的辦法是爲每一個字段都加上索引,可是索引也是一種數據,會佔用內存空間,而且下降寫入效率 此處就可使用聯合索引, 聯合索引最重要的是順序 按照最左匹配原則 應該將區分度高的放在左邊 區分度低的放到右邊 #刪除其餘索引 drop index name_index on usr; drop index email_index on usr; #聯合索引 create index mul_index on usr(email,name,gender,id); # 查詢測試 select count(*) from usr where name = "xx" and id = 1 and email = "xx"; 只要語句中出現了最左側的索引(email) 不管在前在後都能提高效率 drop index mul_index on usr;
over