unittest框架
目錄
- 初識資料庫
- 基本sql語句
- 儲存引擎
- 建立表的完整語法
- 基本資料型別
- 約束條件
- 表與表之間建關係
- 前期表準備
- 多表操作
- 子查詢
- Navicat軟體
- 提示
- 練習題
- pymysql模組
- sql注入
- 檢視(瞭解)
- 觸發器(瞭解)
- 事務
- 儲存過程
- 知識擴充套件
- Mysql的四種隔離級別
- 測試Mysql的隔離級別
初識資料庫
資料庫演變過程
#隨意存到一個檔案中的資料格式也是千差萬別的,取決於我們的自己
# 小李,|分割
jason|123|NB
# 小王,-分割
egon-123-DBJ
# 小紅,~分割
tank~123~hecha
#所以使用資料庫統一儲存的格式
軟體開發目錄規範
#軟體開發目錄規範,限制了儲存資料的具體位置
bin #啟動檔案
conf #配置檔案
core #專案主程式碼
db #存放資料
lib #公共方法
readme #使用說明
#假設上訴是一個單機遊戲,那麼每個人的記錄只會在自己的計算機儲存,註冊登入的帳號也只能在自己的計算機有效
#如何將單機程式設計聯網
將資料庫儲存部分統一起來,所有人在一個地方操作
資料庫本質
#本質就是一款基於網路通訊的應用程式,那其實每個人都可以開發一款資料庫軟體,因為它僅僅是一個基於網路通訊的應用程式
#關係型資料庫
MySql、oracle、db2、access、sql server
#什麼是關係型
1 資料之間有關係或約束
2 儲存資料的表現形式通常是以表格儲存,每個欄位還會有儲存型別限制
name password hobby
jason 123 學習
egon 123 女教練
tank 123 吃生蠔
#非關係型資料庫(快取資料庫)
redis、mongodb、memcahe
#什麼是非關係型
儲存資料通常都是以k,v鍵值對的形式
MySql
#任何基於網路通訊的應用程式底層用的都是socket協議
#服務端
基於scoket通訊
收發資訊
SQL語句
#客戶端
基於scoket通訊
收發訊息
SQL語句
#MySql不單單支援自己的客戶端app還支援其他程式語言來充當客戶端操作
#如何解決語言的障礙?
1 讓伺服器相容所有的語言(精通萬國語言)
2 採用統一的語言(SQL語句)
重要概念介紹
庫比喻成資料夾
表比喻成檔案
記錄比喻成檔案內一行行的資料
表頭就是表格的第一行欄位
欄位就是每一縱的名稱
MySql安裝
#在IT界一般不會輕易使用最新版本的軟體,因為新版本可能會出現各種問題,使用舊版本更穩定
#MySql有很多版本(5.6,5.7,8.0....)目前企業用的比較多還是5.6左右
MySql服務端與客戶端
#服務端
mysqld.exe
#客戶端
mysql.exe
#注意,在前期配置mysql的時候,cmd儘量使用管理員執行,普通使用者有一些命令是無法執行的
啟動
#cmd(管理員)先切換到mysqld所在的bin目錄下,輸入mysqld即可
#重新開啟一個cmd視窗
#第一次一管理員身份進入是沒有密碼的,直接回車即可
#完整命令 mysql -h 127.0.0.1 -P 3306 -uroot -p
#簡寫命令 mysql -uroot -p
常見的軟體預設埠號
MySQL 3306
redis 6379
mongodb 27017
django 8000
flask 5000
vue 8080
SQL語句初識
#mysql中的sql語句是以分號作為結束的標誌
#基本命令
show databases; #檢視所有的庫名
#當你發現輸入的命令不對,可以用\c取消
#客戶端退出
exit
quit
#當你在連線服務端的時候,發現只輸入mysql也能連線資料庫,但只是遊客身份
環境變數配置及系統服務製作
#cmd檢視當前具體程序
tasklist
tasklist |findstr mysqld #查詢mysqld程序號
#殺死具體程序
taskkill /F /PID 程序的pid號
#檢視當前計算機的服務
services.msc
#每次啟動mysqld需要先切換到檔案路徑下才能操作太過繁瑣,將mysqld所在的檔案新增到系統環境變數中
#還需要手動啟動mysql服務端也不好,把它做成服務(開機自啟)
mysql --install
mysqld --remove #移除mysql系統服務
設定密碼
#無需進入mysql,cmd終端輸入即可
mysqladmin -uroot -p原密碼 password 新密碼
如:mysqladmin -uroot -p123 password 123456
破解密碼
'''
可以將mysql獲取使用者名稱和密碼效驗的功能看成是一個裝飾器,裝飾在客戶端請求訪問的功能上,
我們將該裝飾器移除,那麼mysql服務端就不會校檢使用者名稱和密碼了
'''
#先關閉當前mysql服務端
mysqld --skip-grant-tables
#直接以無密碼的方式連線
mysql -uroot -p 直接回車
#修改密碼
update mysql.user set password=password(123456) where user='root' and host='localhost';
#重新整理資料到硬碟
flush privileges;
#關閉當前服務端,然後以正常效驗授權表的顯示啟動
統一編碼
#mysql的預設配置檔案,程式啟動會先載入配置檔案中的配置才會真正的啟動
my-default.ini #ini結尾的一般都是配置檔案
#在D:\Mysql\mysql-5.6.48-winx64(程式根目錄)自己建立一個my.ini的配置檔案
# 修改配置檔案後一定要重啟服務才能生效
# 統一編碼的配置,無需掌握,直接拷貝即可
# 偷懶可以將管理員的使用者名稱和密碼也新增到配置檔案中
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
[client]
default-character-set=utf8
[mysql]
user="root"
password=123456
default-character-set=utf8
基本sql語句
DML:
資料操作語言,它是對錶記錄的操作(增,刪,改)
DCL:
資料控制語言
*一個專案建立一個使用者!一個專案對應的資料庫只有一個~
*這個使用者只能對這個資料庫有許可權,其他資料庫你就操作不了了!
DQL:
資料查詢語言,查詢不會修改資料庫記錄!
DDL:
資料定義語言。這些語句定義了不同的資料段、
資料庫、表、列、索引等資料庫物件的定義。常用的語句關鍵字主要包括create、drop、alter
等。
針對庫的增刪查改(資料夾)
#增
create database db1;
create database db1 charset='gbk'; #指定gbk編碼
#刪
drop database db1;
#查
show databases; #查所有
show create database db1; #查單個
#改
alter database db1 charset='utf8' #改編碼格式
針對表的增刪查改(檔案)
#在操作表(檔案)的時候,需要指定所在的庫(資料夾)
#檢視當前所在哭的名字
select database();
#切換庫
use db1;
#增
create table t1(id int,name char(4));
#刪
drop table t1;
#查
show tables; #檢視當前庫下的所有表名
show create table t1; #檢視單個表
describe t1; #查看錶結構,簡寫 desc t1;
#改
alter table t1 modify name char(16); #改欄位name的型別char為16長度
#也可以用絕對路徑的形式操作不同的庫
create table db2.t1(id int); #在db2庫下的建立t1表
針對資料的增刪改查(一行行的資料)
#一定要先有庫,有表才能操作記錄
#增
insert into t1 values(1,'jsson'); #插入一條ID為1,name為jsoin的資料
insert into t1 values(1,'jsson'),(2,'egon'),(3,'tank'); #插三條
#刪
delete from t1 where id>1; #刪除id大於1的資料
delete from t1 where name='jason'; #刪除name=jason的資料
delete from t1; #清空表
#查
select * from t1; #檢視t1表的所有資料,當資料量特別大的時候不建議使用
#改
update t1 set name='DSB' where id>1; #把id大於1的name更新為DSB
儲存引擎
日常生活中檔案格式有很多種,並且針對不同的檔案格式會有對應不同的儲存方式和處理機制(txt,pdf,word,mp4......),針對不同的資料應該有不同的處理機制來儲存,儲存引擎就是不同的處理機制
MySql主要的儲存引擎
#innodb
是mysql5.5版本之後預設的儲存引擎,儲存資料更加安全
#myisam
是mysql5.5版本之前預設的儲存引擎,速度比innodb更快,但是我們更加註重的是資料的安全
#memory
記憶體引擎(資料全部放在記憶體中)斷電資料丟失
#blackhole
無論存什麼,都立刻消失(黑洞)
#檢視所有的儲存引擎
show engines;
# 不同的儲存引擎在儲存表的時候 異同點
create table t1(id int) engine=innodb;
create table t2(id int) engine=myisam;
create table t3(id int) engine=blackhole;
create table t4(id int) engine=memory;
建立表的完整語法
#語法
create table t1(
欄位名1 型別(寬度) 約束條件,
欄位名2 型別(寬度) 約束條件,
欄位名3 型別(寬度) 約束條件,
)
#注意
1 在同一張表中欄位名不能重複
2 寬度和約束條件是可選的(可寫可不寫),而欄位名和子彈類是必填的
約束條件還支援多個,欄位名1 型別(寬度) 約束條件1 約束條件2,
3 結尾不能有逗號
create table t6(id int,name char,); #報錯
#寬度
一般情況下指的是對儲存資料的限制
create table t7(name char); 預設寬度是1
insert into t7 values('jason');
insert into t7 values(null); 關鍵字NULL
針對不同版本的會出現不同的效果,5.6版本,預設沒有開啟嚴格模式,規定只能存一個字元,你給了多個字元,會自動幫你擷取
5.7版本及以上或者開啟了嚴格模式,那麼規定只能存幾個,一旦超出範圍立即報錯, Data too long for ....
#那麼嚴格模式到的開不開呢
mysql5.7之後的版本預設都是開啟嚴格模式的,
#使用資料庫的準則
能儘量少的讓資料庫幹活就儘量少,不要給資料庫在家額外的壓力
#約束提交 null not null不能插入null
create table t8(id int,name char not null); #name欄位不能為null
#寬度和約束條件到底是什麼關係
寬度是用來限制資料的儲存
約束條件是在寬度的基礎上增加額外的約束
基本資料型別
整形
#分類,看下圖
INT #大整數值
TINYINT #小整數值
SMALLINT #大整數值
MEDUIMINT #大整數值
BIGINT #極大整數值
#作用
儲存年齡、等級、id、號碼等等
#以TINYINT為例
是否有符號,預設情況下是帶符號的
超出會怎麼樣,超出ianzhi只存最大可接受值
create table t9(id tinyint);
insert into t9 values(-129),(256); #結果是-128,127 #只接受最大值
#約束條件之unsigned無符號
create table t10(id tinyint unsigned);
create table t11(id int);
#int預設也是帶有符號的
#整形預設情況下都是都有符號的
#針對整型括號的寬度到底是幹嘛的
create table t12(id int(8));
insert into t12 values(123456789);
特例:
只有整形括號裡面的數字不是表示限制位數,如果沒有8位,那麼用空格填充至8位,
如果超出了8位,那麼有幾位就存幾位,但是還是要遵循最大範圍
#總結
針對整數字段,括號內無序指定寬度,以為他預設的寬度已經足夠顯示所有的資料了
嚴格模式
#mysql5.7之後的版本預設都是開啟嚴格模式的
針對不同版本的會出現不同的效果,5.6版本,預設沒有開啟嚴格模式,規定只能存一個字元,你給了多個字元,會自動幫你擷取
5.7版本及以上或者開啟了嚴格模式,那麼規定只能存幾個,一旦超出範圍立即報錯, Data too long for ....
#檢視嚴格模式
show variables like "%mode";
模糊匹配/查詢
關鍵字 like
%:匹配任意多個字元
_:匹配任意單個字元
#修改嚴格模式
set session #只在當前視窗有效
set global #全域性有效
set global spl_mode = 'STRICT_TRANS_TABLES'; #修改完後,重新進入服務端即可
浮點型
#分類
FLOAT
DOUBLE
DECIMAL
#作用
身高、體重、薪資
#儲存限制
float(255,30) #總共255位,小數部分佔30位
double(255,30) # 總共255位 小數部分佔30位
decimal(65,30) # 總共65位 小數部分佔30位
# 精確度驗證
create table t15(id float(255,30));
create table t16(id double(255,30));
create table t17(id decimal(65,30));
insert into t15 values(1.111111111111111111111111111111);
insert into t16 values(1.111111111111111111111111111111);
insert into t17 values(1.111111111111111111111111111111);
float < double < decimal
# 要結合實際應用場景 三者都能使用
字元型別
#分類
char #定長,char(4)資料超過4個字元直接報錯,不夠4個字元空格補全
varchar #變長,varchar(4)資料超過4個字元直接報錯,不夠有幾個存幾個
# 介紹一個小方法 char_length統計欄位長度
create table t18(name char(4));
create table t19(name varchar(4));
insert into t18 values('a');
insert into t19 values('a');
select char_length(name) from t18;
select char_length(name) from t19;
"""
首先可以肯定的是 char硬碟上存的絕對是真正的資料 帶有空格的
但是在顯示的時候MySQL會自動將多餘的空格剔除
"""
# 再次修改sql_mode,讓MySQL不要做自動剔除操作
set global sql_mode = 'STRICT_TRANS_TABLES,PAD_CHAR_TO_FULL_LENGTH';
char與varchar對比
#char
缺點:浪費空間
優點:存取都很簡單
直接按照固定的字元存取資料即可
jason egon alex wusir tank
存按照五個字元存 取也直接按照五個字元取
#varchar
優點:節省空間
缺點:存取較為麻煩
1bytes+jason 1bytes+egon 1bytes+alex 1bytes+tank
存的時候需要製作報頭
取的時候也需要先讀取報頭 之後才能讀取真實資料
#以前基本上都是用的char,其實現在用varchar的也挺多
時間型別
#分類
date #年月日 2020-5-4
datetime #年月日時分秒 2020-5-4 11:11:11
time #時分秒11:11:11
Year #年2020
#建立表和插入資料
create table student(
id int,
name varchar(16),
born_year year,
birth date,
study_time time,
reg_time datetime
);
insert into student values(1,'egon','1880','1880-11-11','11:11:11','2020-11-11 11:11:11');
列舉與集合型別
#分類
列舉(enum) #多選一
集合(set) #多選多
#集體使用
create table user(
id int,
name char(16),
gender enum('male','female','others')
);
insert into user values(1,'jason','male'); 正常
insert into user values(2,'egon','xxxxooo'); 報錯
# 列舉欄位 後期在存資料的時候只能從列舉裡面選擇一個儲存
create table teacher(
id int,
name char(16),
gender enum('male','female','others'),
hobby set('read','DBJ','hecha')
);
insert into teacher values(1,'jason','male','read'); 正常
insert into teacher values(2,'egon','female','DBJ,hecha'); 正常
insert into teacher values(3,'tank','others','生蠔'); 報錯
# 集合可以只寫一個 但是不能寫沒有列舉的
總結(重點記憶)
欄位型別
嚴格模式
約束條件
not null
zerofill
unsigned
約束條件
default預設值
# 補充知識點 插入資料的時候可以指定欄位
create table t1(
id int,
name char(16)
);
insert into t1(name,id) values('jason',1);
create table t2(
id int,
name char(16),
gender enum('male','female','others') default 'male'
);
insert into t2(id,name) values(1,'jason');
insert into t2 values(2,'egon','female');
unique唯一
# 單列唯一
create table t3(
id int unique,
name char(16)
);
insert into t3 values(1,'jason'),(1,'egon');
insert into t3 values(1,'jason'),(2,'egon');
# 聯合唯一
"""
ip和port
單個都可以重複 但是載入一起必須是唯一的
"""
create table t4(
id int,
ip char(16),
port int,
unique(ip,port)
);
insert into t4 values(1,'127.0.0.1',8080);
insert into t4 values(2,'127.0.0.1',8081);
insert into t4 values(3,'127.0.0.2',8080);
insert into t4 values(4,'127.0.0.1',8080); 報錯
primary key唯一
1.單單從約束效果上來看primary key等價於not null + unique
非空且唯一!!!
create table t5(id int primary key);
insert into t5 values(null); 報錯
insert into t5 values(1),(1); 報錯
insert into t5 values(1),(2);
2.它除了有約束效果之外 它還是Innodb儲存引擎組織資料的依據
Innodb儲存引擎在建立表的時候必須要有primary key
因為它類似於書的目錄 能夠幫助提示查詢效率並且也是建表的依據
# 1 一張表中有且只有一個主鍵 如果你沒有設定主鍵 那麼會從上往下搜尋直到遇到一個非空且唯一的欄位將它自動升級為主鍵
create table t6(
id int,
name char(16),
age int not null unique,
addr char(32) not null unique
);
# 2 如果表中沒有主鍵也沒有其他任何的非空且唯一欄位 那麼Innodb會採用自己內部提供的一個隱藏欄位作為主鍵,隱藏意味著你無法使用到它 就無法提示查詢速度
# 3 一張表中通常都應該有一個主鍵欄位 並且通常將id/uid/sid欄位作為主鍵
# 單個欄位主鍵
create table t5(
id int primary key
name char(16)
);
# 聯合主鍵(多個欄位聯合起來作為表的主鍵 本質還是一個主鍵)
create table t7(
ip char(16),
port int,
primary key(ip,port)
);
"""
也意味著 以後我們在建立表的時候id欄位一定要加primary key
"""
auto_increment自增
# 當編號特別多的時候 人為的去維護太麻煩
create table t8(
id int primary key auto_increment,
name char(16)
);
insert into t8(name) values('jason'),('egon'),('kevin');
# 注意auto_increment通常都是加在主鍵上的 不能給普通欄位加
create table t9(
id int primary key auto_increment,
name char(16),
cid int auto_increment
);
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
結論
以後在建立表的id(資料的唯一標識id、uid、sid)欄位的時候
id int primary key auto_increment
補充
delete from t1 刪除表中資料後 主鍵的自增不會停止
truncate t1 清空表資料並且重置主鍵
表與表之間建關係
#定義一張員工表,表中有很多欄位
id name gender dep_name dep_desc
1 該表的組織結構不是很清晰(可忽視)
2 浪費硬碟空間(可忽視)
3 資料的擴充套件性極差(無法忽視的)
# 如何優化?
上述問題就類似於你將所有的程式碼都寫在了一個py檔案中
將員工表拆分,員工表和部門表
外來鍵
外來鍵就是用來幫助我們建立表與表之間關係的
foreign key
表關係
表與表之間最多隻有四種關係
一對多關係
在MySQL的關係中沒有多對一一說
一對多 多對一 都叫一對多!!!
多對多關係
一對一關係
沒有關係
一對多關係
#判斷表與表之間關係的時候 前期不熟悉的情況下 一定要按照我給你的建議,換位思考,分別站在兩張表的角度考慮
#員工表與部門表為例
先站在員工表
思考一個員工能否對應多個部門(一條員工資料能否對應多條部門資料)
不能!!!
(不能直接得出結論 一定要兩張表都考慮完全)
再站在部門表
思考一個部門能否對應多個員工(一個部門資料能否對應多條員工資料)
能!!!
得出結論
員工表與部門表示單向的一對多
所以表關係就是一對多
#foreign key
1 一對多表關係,外來鍵欄位建在多的一方
2 在建立表的時候,一定要先建被關聯表
3 在錄入資料的時候,也必須先錄入被關聯表
# SQL語句建立表關係
create table dep(
id int primary key auto_increment,
dep_name char(16),
dep_desc char(32)
);
create table emp(
id int primary key auto_increment,
name char(16),
gender enum('male','female','others') default 'male',
dep_id int,
foreign key(dep_id) references dep(id)
);
insert into dep(dep_name,dep_desc) values('sb教學部','教書育人'),('外交部','多人外交'),('nb技術部','技術能力有限部門');
insert into emp(name,dep_id) values('jason',2),('egon',1),('tank',1),('kevin',3);
# 修改dep表裡面的id欄位
update dep set id=200 where id=2; 不行
# 刪除dep表裡面的資料
delete from dep; 不行
# 1 先刪除教學部對應的員工資料 之後再刪除部門
操作太過繁瑣
# 2 真正做到資料之間有關係
更新就同步更新
刪除就同步刪除
"""
級聯更新 >>> 同步更新
級聯刪除 >>> 同步刪除
"""
create table dep(
id int primary key auto_increment,
dep_name char(16),
dep_desc char(32)
);
create table emp(
id int primary key auto_increment,
name char(16),
gender enum('male','female','others') default 'male',
dep_id int,
foreign key(dep_id) references dep(id)
on update cascade # 同步更新
on delete cascade # 同步刪除
);
insert into dep(dep_name,dep_desc) values('sb教學部','教書育人'),('外交部','多人外交'),('nb技術部','技術能力有限部門');
insert into emp(name,dep_id) values('jason',2),('egon',1),('tank',1),('kevin',3);
多對多關係
#圖書表和作者表
create table book(
id int primary key auto_increment,
title varchar(32),
price int,
author_id int,
foreign key(author_id) references author(id)
on update cascade # 同步更新
on delete cascade # 同步刪除
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int,
book_id int,
foreign key(book_id) references book(id)
on update cascade # 同步更新
on delete cascade # 同步刪除
);
"""
按照上述的方式建立 一個都別想成功!!!
其實我們只是想記錄書籍和作者的關係
針對多對多欄位表關係 不能在兩張原有的表中建立外來鍵
需要你單獨再開設一張 專門用來儲存兩張表資料之間的關係
"""
create table book(
id int primary key auto_increment,
title varchar(32),
price int
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int
);
create table book2author(
id int primary key auto_increment,
author_id int,
book_id int,
foreign key(author_id) references author(id)
on update cascade # 同步更新
on delete cascade, # 同步刪除
foreign key(book_id) references book(id)
on update cascade # 同步更新
on delete cascade # 同步刪除
);
一對一關係
#id name age addr phone hobby email........
#如果一個表的欄位特別多 每次查詢又不是所有的欄位都能用得到,將表一分為二
使用者表
使用者表
id name age
使用者詳情表
id addr phone hobby email........
站在使用者表
一個使用者能否對應多個使用者詳情 不能!!!
站在詳情表
一個詳情能否屬於多個使用者 不能!!!
結論:單向的一對多都不成立 那麼這個時候兩者之間的表關係
就是一對一
或者沒有關係(好判斷)
客戶表和學生表
在你們報名之前你們是客戶端
報名之後是學生(期間有一些客戶不會報名)
#一對一 外來鍵欄位建在任意一方都可以 但是推薦你建在查詢頻率比較高的表中
create table authordetail(
id int primary key auto_increment,
phone int,
addr varchar(64)
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int,
authordetail_id int unique,
foreign key(authordetail_id) references authordetail(id)
on update cascade # 同步更新
on delete cascade # 同步刪除
)
修改表(瞭解)
# MySQL對大小寫是不敏感的
#1 修改表名
alter table 表名 rename 新表名;
#2 增加欄位
alter table 表名 add 欄位名 欄位型別(寬度) 約束條件;
alter table 表名 add 欄位名 欄位型別(寬度) 約束條件 first;
alter table 表名 add 欄位名 欄位型別(寬度) 約束條件 after 欄位名;
#3 刪除欄位
alter table 表名 drop 欄位名;
#4 修改欄位
alter table 表名 modify 欄位名 欄位型別(寬度) 約束條件;
alter table 表名 change 舊欄位名 新欄位名 欄位型別(寬度) 約束條件;
複製表(瞭解)
#我們sql語句查詢的結果其實也是一張虛擬表
create table 表名 select * from 舊錶; 不能複製主鍵 外來鍵 ...
create table new_dep2 select * from dep where id>3;
前期表準備
create table emp(
id int not null unique auto_increment,
name varchar(20) not null,
sex enum('male','female') not null default 'male', #大部分是男的
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int, #一個部門一個屋子
depart_id int
);
#插入記錄,三個部門:教學,銷售,運營
insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values
('jason','male',18,'20170301','張江第一帥形象代言',7300.33,401,1), #以下是教學部
('tom','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tony','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jack','female',18,'20110211','teacher',9000,401,1),
('jenny','male',18,'19000301','teacher',30000,401,1),
('sank','male',48,'20101111','teacher',10000,401,1),
('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是銷售部門
('呵呵','female',38,'20101101','sale',2000.35,402,2),
('西西','female',18,'20110312','sale',1000.37,402,2),
('樂樂','female',18,'20160513','sale',3000.29,402,2),
('拉拉','female',28,'20170127','sale',4000.33,402,2),
('僧龍','male',28,'20160311','operation',10000.13,403,3), #以下是運營部門
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬銀','female',18,'20130311','operation',19000,403,3),
('程咬銅','male',18,'20150411','operation',18000,403,3),
('程咬鐵','female',18,'20140512','operation',17000,403,3);
# 當表字段特別多 展示的時候錯亂 可以使用\G分行展示
select * from emp\G;
# 個別同學的電腦在插入中文的時候還是會出現亂碼或者空白的現象,你可以將字元編碼統一設定成GBK
幾個重要的關鍵字的執行順序
# 書寫順序
select id,name from emp where id > 3;
# 執行順序
from
where
select
#雖然執行順序和書寫順序不一致,你在寫sql語句的時候可能不知道怎麼寫,你就按照書寫順序的方式寫sql
select * 先用*號佔位
之後去補全後面的sql語句
最後將*號替換後你想要的具體欄位
where篩選條件
# 作用:是對整體資料的一個篩選操作
#1.查詢id大於等於3小於等於6的資料
select id,name,age from emp where id>=3 and id<=6;
select id,name from emp where id between 3 and 6; 兩者等價
#2.查詢薪資是20000或者18000或者17000的資料
select * from emp where salary=20000 or salary=18000 or salary=17000;
select * from emp where salary in (20000,18000,17000);
#3.查詢員工姓名中包含字母o的員工的姓名和薪資
"""
模糊查詢
like
% 匹配任意多個字元
_ 匹配任意單個字元
"""
select name,salary from emp where name like '%o%';
# 4.查詢員工姓名是由四個字元組成的 姓名和薪資 char_length()
select name,salary from emp where name like '____';
select name,salary from emp where char_length(name) = 4;
# 5.查詢id小於3或者id大於6的資料
select * from emp where id not between 3 and 6;
# 6.查詢薪資不在20000,18000,17000範圍的資料
select * from emp where salary not in (20000,18000,17000);
# 7.查詢崗位描述為空的員工姓名和崗位名 針對null不用等號 用is
select name,post from emp where post_comment = NULL;
select name,post from emp where post_comment is NULL;
group by分組
# 分組實際應用場景,分組應用場景非常的多
男女比例
部門平均薪資
部門禿頭率
國家之間資料統計
# 1 按照部門分組
select * from emp group by post;
"""
分組之後 最小可操作單位應該是組 還不再是組內的單個數據
上述命令在你沒有設定嚴格模式的時候是可正常執行的 返回的是分組之後 每個組的第一條資料 但是這不符合分組的規範:分組之後不應該考慮單個數據 而應該以組為操作單位(分組之後 沒辦法直接獲取組內單個數據)
如果設定了嚴格模式 那麼上述命令會直接報錯
"""
set global sql_mode = 'strict_trans_tables,only_full_group_by';
設定嚴格模式之後,分組,預設只能拿到分組的依據
select post from emp group by post;
按照什麼分組就只能拿到分組 其他欄位不能直接獲取 需要藉助於一些方法(聚合函式)
#什麼時候需要分組啊???
關鍵字
每個 平均 最高 最低
聚合函式
max
min
sum
count
avg
# 1.獲取每個部門的最高薪資
select post,max(salary) from emp group by post;
select post as '部門',max(salary) as '最高薪資' from emp group by post;
select post '部門',max(salary) '最高薪資' from emp group by post;
# as可以給欄位起別名,也可以直接省略不寫,但是不推薦,因為省略的話語意不明確,容易錯亂
# 2.獲取每個部門的最低薪資
select post,min(salary) from emp group by post;
# 3.獲取每個部門的平均薪資
select post,avg(salary) from emp group by post;
# 4.獲取每個部門的工資總和
select post,sum(salary) from emp group by post;
# 5.獲取每個部門的人數
select post,count(id) from emp group by post; # 常用 符合邏輯
select post,count(salary) from emp group by post;
select post,count(age) from emp group by post;
select post,count(post_comment) from emp group by post; null不行
# 6.查詢分組之後的部門名稱和每個部門下所有的員工姓名
# group_concat不單單可以支援你獲取分組之後的其他欄位值 還支援拼接操作
select post,group_concat(name) from emp group by post;
select post,group_concat(name,'_DSB') from emp group by post;
select post,group_concat(name,':',salary) from emp group by post;
# concat不分組的時候用
select concat('NAME:',name),concat('SAL:',salary) from emp;
# 補充 as語法不單單可以給欄位起別名 還可以給表臨時起別名
select emp.id,emp.name from emp;
select emp.id,emp.name from emp as t1; 報錯
select t1.id,t1.name from emp as t1;
# 查詢每個人的年薪 12薪
select name,salary*12 from emp;
分組注意事項
# 關鍵字where和group by同時出現的時候group by必須在where的後面
where先對整體資料進行過濾之後再分組操作
where篩選條件不能使用聚合函式
select id,name,age from emp where max(salary) > 3000;
select max(salary) from emp; # 不分組 預設整體就是一組
# 統計各部門年齡在30歲以上的員工平均薪資
1 先求所有年齡大於30歲的員工
select * from emp where age>30;
2 再對結果進行分組
select * from emp where age>30 group by post;
select post,avg(salary) from emp where age>30 group by post;
having分組之後的篩選條件
#having的語法根where是一致的,只不過having是在分組之後進行的過濾操作,即having是可以直接使用聚合函式的
# 統計各部門年齡在30歲以上的員工平均工資並且保留平均薪資大於10000的部門
select post,avg(salary) from emp
where age>30
group by post
having avg(salary) > 10000
;
distinct去重
#一定要注意 必須是完全一樣的資料才可以去重!!!
#一定不要將逐漸忽視了,有逐漸存在的情況下,是不可能去重的
[
{'id':1,'name':'jason','age':18},
{'id':2,'name':'jason','age':18},
{'id':3,'name':'egon','age':18}
]
ORM 物件關係對映 讓不懂SQL語句的人也能夠非常牛逼的操作資料庫
表 類
一條條的資料 物件
欄位對應的值 物件的屬性
你再寫類 就意味著在建立表
用類生成物件 就意味著再建立資料
物件點屬性 就是在獲取資料欄位對應的值
目的就是減輕python程式設計師的壓力 只需要會python面向物件的知識點就可以操作MySQL
select distinct id,age from emp;
select distinct age from emp;
order by排序
select * from emp order by salary;
select * from emp order by salary asc;
select * from emp order by salary desc;
#order by預設是升序 asc 該asc可以省略不寫
#也可以修改為降序 desc
select * from emp order by age desc,salary asc;
# 先按照age降序排 如果碰到age相同 則再按照salary升序排
# 統計各部門年齡在10歲以上的員工平均工資並且保留平均薪資大於1000的部門,然後對平均工資降序排序
select post,avg(salary) from emp
where age>10
group by post
having avg(salary) > 1000
order by avg(salary) desc
;
limit限制展示條數
select * from emp;
"""針對資料過多的情況 我們通常都是做分頁處理"""
select * from emp limit 3; # 只展示三條資料
select * from emp limit 0,5; #第一個引數是起始位置,第二個引數是展示條數
select * from emp limit 5,5;
正則
select * from emp where name regexp '^j.*(n|y)$';
多表操作
前期表準備
#建表
create table dep(
id int,
name varchar(20)
);
create table emp(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);
#插入資料
insert into dep values
(200,'技術'),
(201,'人力資源'),
(202,'銷售'),
(203,'運營');
insert into emp(name,sex,age,dep_id) values
('jason','male',18,200),
('egon','female',48,201),
('kevin','male',18,201),
('nick','male',28,202),
('owen','male',18,203),
('jerry','female',18,204);
表查詢
select * from dep,emp; # 結果 笛卡爾積
"""
瞭解即可 不知道也沒關係
"""
select * from emp,dep where emp.dep_id = dep.id;
"""
MySQL也知道 你在後面查詢資料過程中 肯定會經常用到拼表操作
所以特地給你開設了對應的方法
inner join 內連線
left join 左連線
right join 右連線
union 全連線
"""
# inner join 內連線
select * from emp inner join dep on emp.dep_id = dep.id;
# 只拼接兩張表中公有的資料部分
# left join 左連線
select * from emp left join dep on emp.dep_id = dep.id;
# 左表所有的資料都展示出來 沒有對應的項就用NULL
# right join 右連線
select * from emp right join dep on emp.dep_id = dep.id;
# 右表所有的資料都展示出來 沒有對應的項就用NULL
# union 全連線 左右兩表所有的資料都展示出來
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;
子查詢
#表的查詢結果可以作為其他表的查詢條件,也可以通過起別名的方式把它作為一個張虛擬表根其他表關聯
#多表查詢就兩種方式
先拼接表再查詢
子查詢 一步一步來
Navicat軟體
一開始學習python的時候,下載python直譯器然後直接在終端書寫
pycharm能夠更加方便快捷的幫助你書寫python程式碼
excel word pdf
我們在終端操作MySQL,也沒有自動提示也無法儲存等等,不方便開發
Navicat內部封裝了所有的操作資料庫的命令
使用者在使用它的時候只需要滑鼠點點即可完成操作,無需書寫sql語句
安裝
#直接百度搜索,有破解版的也有非破解
#非破解的有試用期,你如果不嫌麻煩,你就用使用,到期之後重新裝再使用 或者破解一下也很簡單
https://www.cr173.com/soft/126934.html
#下載完成後是一個壓縮包 直接解壓 然後點選安裝 有提醒直接點選next即可
#navicat能夠充當多個數據庫的客戶端
#navicat圖形化介面有時候反應速度較慢 你可以選擇重新整理或者關閉當前視窗再次開啟即可
#當你有一些需求該軟體無法滿足的時候 你就自己動手寫sql
提示
1 MySQL是不區分大小寫的
驗證碼忽略大小寫,內部統一轉大寫或者小寫比較即可
upper
lower
2 MySQL建議所有的關鍵字寫大寫
3 MySQL中的註釋 有兩種
--
#
4 在navicat中如何快速的註釋和解註釋
ctrl + ? 加註釋
ctrl + ? 基於上述操作再來一次就是解開註釋
如果你的navicat版本不一致還有可能是
ctrl + shift + ?解開註釋
練習題
-- 1、查詢所有的課程的名稱以及對應的任課老師姓名
-- SELECT
-- course.cname,
-- teacher.tname
-- FROM
-- course
-- INNER JOIN teacher ON course.teacher_id = teacher.tid;
-- 4、查詢平均成績大於八十分的同學的姓名和平均成績
-- SELECT
-- student.sname,
-- t1.avg_num
-- FROM
-- student
-- INNER JOIN (
-- SELECT
-- score.student_id,
-- avg( num ) AS avg_num
-- FROM
-- score
-- INNER JOIN student ON score.student_id = student.sid
-- GROUP BY
-- score.student_id
-- HAVING
-- AVG( num ) > 80
-- ) AS t1 ON student.sid = t1.student_id;
-- 7、 查詢沒有報李平老師課的學生姓名
# 分步操作
# 1 先找到李平老師教授的課程id
# 2 再找所有報了李平老師課程的學生id
# 3 之後去學生表裡面取反 就可以獲取到沒有報李平老師課程的學生姓名
-- SELECT
-- student.sname
-- FROM
-- student
-- WHERE
-- sid NOT IN (
-- SELECT DISTINCT
-- score.student_id
-- FROM
-- score
-- WHERE
-- score.course_id IN ( SELECT course.cid FROM teacher INNER JOIN course ON teacher.tid = course.teacher_id WHERE teacher.tname = '李平老師' )
-- );
-- 8、 查詢沒有同時選修物理課程和體育課程的學生姓名
-- (只要選了一門的 選了兩門和沒有選的都不要)
# 1 先查物理和體育課程的id
# 2 再去獲取所有選了物理和體育的學生資料
# 3 按照學生分組 利用聚合函式count篩選出只選了一門的學生id
# 4 依舊id獲取學生姓名
-- SELECT
-- student.sname
-- FROM
-- student
-- WHERE
-- student.sid IN (
-- SELECT
-- score.student_id
-- FROM
-- score
-- WHERE
-- score.course_id IN ( SELECT course.cid FROM course WHERE course.cname IN ( '物理', '體育' ) )
-- GROUP BY
-- score.student_id
-- HAVING
-- COUNT( score.course_id ) = 1
-- );
-- 9、 查詢掛科超過兩門(包括兩門)的學生姓名和班級
# 1 先篩選出所有分數小於60的資料
# 2 按照學生分組 對資料進行計數獲取大於等於2的資料
SELECT
class.caption,
student.sname
FROM
class
INNER JOIN student ON class.cid = student.class_id
WHERE
student.sid IN (
SELECT
score.student_id
FROM
score
WHERE
score.num < 60 GROUP BY score.student_id HAVING COUNT( score.course_id ) >= 2
);
pymysql模組
#支援python程式碼操作資料庫MySQL
pip3 install pymysql
sql注入
# 利用一些語法的特性,書寫一些特點的語句實現固定的語法
MySQL利用的是MySQL的註釋語法
select * from user where name='jason' -- jhsadklsajdkla' and password=''
select * from user where name='xxx' or 1=1 -- sakjdkljakldjasl' and password=''
# 日常生活中很多軟體在註冊的時候都不能含有特殊符號,因為怕你構造出特定的語句入侵資料庫,不安全
# 敏感的資料不要自己做拼接 交給execute幫你拼接即可
# 結合資料庫完成一個使用者的登入功能?
import pymysql
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = '123456',
database = 'day48',
charset = 'utf8' # 編碼千萬不要加-
)
# 連結資料庫
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
username = input('>>>:')
password = input('>>>:')
sql = "select * from user where name=%s and password=%s"
# 不要手動拼接資料 先用%s佔位 之後將需要拼接的資料直接交給execute方法即可
print(sql)
rows = cursor.execute(sql,(username,password)) # 自動識別sql裡面的%s用後面元組裡面的資料替換
if rows:
print('登入成功')
print(cursor.fetchall())
else:
print('使用者名稱密碼錯誤')
檢視(瞭解)
什麼是檢視
檢視就是通過查詢得到一張虛擬表,然後儲存下來,下次可以直接使用
其實檢視也是表
為什麼要用檢視
如果要頻繁的操作一張虛擬表(拼表組成的),你就可以製作成檢視 後續直接操作
如何操作
# 固定語法
create view 表名 as 虛擬表的查詢sql語句
# 具體操作
create view teacher2course as
select * from teacher INNER JOIN course
on teacher.tid = course.teacher_id;
注意
1 建立檢視在硬碟上只會有表結構 沒有表資料(資料還是來自於之前的表)
2 檢視一般只用來查詢 裡面的資料不要繼續修改 可能會影響真正的表
檢視到底使用頻率高不高呢?
總結
不高,當你建立了很多檢視之後 會造成表的不好維護
檢視瞭解即可 基本不用!!!
觸發器(瞭解)
在滿足對錶資料進行增、刪、改的情況下,自動觸發的功能
使用觸發器可以幫助我們實現監控、日誌...
觸發器可以在六種情況下自動觸發 增前 增後 刪前刪後 改前改後
基本語法結構
create trigger 觸發器的名字 before/after insert/update/delete on 表名
for each row
begin
sql語句
end
# 具體使用 針對觸發器的名字 我們通常需要做到見名知意
# 針對增
create trigger tri_before_insert_t1 before insert on t1
for each row
begin
sql語句
end
create trigger tri_after_insert_t1 after insert on t1
for each row
begin
sql語句
end
"""針對刪除和修改 書寫格式一致"""
ps:修改MySQL預設的語句結束符 只作用於當前視窗
delimiter $$ 將預設的結束符號由;改為$$
delimiter ;
# 案例
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
);
"""
當cmd表中的記錄succes欄位是no那麼就觸發觸發器的執行去errlog表中插入資料
NEW指代的就是一條條資料物件
"""
delimiter $$
create trigger tri_after_insert_cmd after insert on cmd
for each row
begin
if NEW.success = 'no' then
insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
end if;
end $$
delimiter ;
# 朝cmd表插入資料
INSERT INTO cmd (
USER,
priv,
cmd,
sub_time,
success
)
VALUES
('jason','0755','ls -l /etc',NOW(),'yes'),
('jason','0755','cat /etc/passwd',NOW(),'no'),
('jason','0755','useradd xxx',NOW(),'no'),
('jason','0755','ps aux',NOW(),'yes');
# 刪除觸發器
drop trigger tri_after_insert_cmd;
事務
什麼是事務
開啟一個事務可以包含多條sql語句 這些sql語句要麼同時成功
要麼一個都別想成功 稱之為事務的原子性
事務的作用
保證了對資料操作的安全性
還錢的例子:
egon用銀行卡給我的支付寶轉賬1000
1 將egon銀行卡賬戶的資料減1000塊
2 將jason支付寶賬戶的資料加1000塊
你在操作多條資料的時候可能會出現某幾條操作不成功的情況
事務的四大特性
ACID
A:原子性
一個事務是一個不可分割的單位,事務中包含的諸多操作
要麼同時成功要麼同時失敗
C:一致性
事務必須是使資料庫從一個一致性的狀態變到另外一個一致性的狀態
一致性跟原子性是密切相關的
I:隔離性
一個事務的執行不能被其他事務干擾
(即一個事務內部的操作及使用到的資料對併發的其他事務是隔離的,併發執行的事務之間也是互相不干擾的)
D:永續性
也叫"永久性"
一個事務一旦提交成功執行成功 那麼它對資料庫中資料的修改應該是永久的
接下來的其他操作或者故障不應該對其有任何的影響
如何使用事務?
# 事務相關的關鍵字
# 1 開啟事務
start transaction;
# 2 回滾(回到事務執行之前的狀態)
rollback;
# 3 確認(確認之後就無法回滾了)
commit;
"""模擬轉賬功能"""
create table user(
id int primary key auto_increment,
name char(16),
balance int
);
insert into user(name,balance) values
('jason',1000),
('egon',1000),
('tank',1000);
# 1 先開啟事務
start transaction;
# 2 多條sql語句
update user set balance=900 where name='jason';
update user set balance=1010 where name='egon';
update user set balance=1090 where name='tank';
#總結
當你想讓多條sql語句保持一致性 要麼同時成功要麼同時失敗
你就應該考慮使用事務
儲存過程
儲存過程就類似於python中的自定義函式
它的內部包含了一系列可以執行的sql語句,儲存過程存放於MySQL服務端中,你可以直接通過呼叫儲存過程觸發內部sql語句的執行
基本使用
create procedure 儲存過程的名字(形參1,形參2,...)
begin
sql程式碼
end
# 呼叫
call 儲存過程的名字();
三種開發模式
第一種
應用程式:程式設計師寫程式碼開發
MySQL:提前編寫好儲存過程,供應用程式呼叫
好處:開發效率提升了 執行效率也上去了
缺點:考慮到認為元素、跨部門溝通的問題 後續的儲存過程的擴充套件性差
第二種
應用程式:程式設計師寫程式碼開發之外 設計到資料庫操作也自己動手寫
優點:擴充套件性很高
缺點:
開發效率降低
編寫sql語句太過繁瑣 而且後續還需要考慮sql優化的問題
第三種
應用程式:程式設計師寫程式碼開發之外 設計到資料庫操作也自己動手寫
優點:擴充套件性很高
缺點:
開發效率降低
編寫sql語句太過繁瑣 而且後續還需要考慮sql優化的問題
第一種基本不用。一般都是第三種,出現效率問題再動手寫sql
儲存過程具體演示
delimiter $$
create procedure p1(
in m int, # 只進不出 m不能返回出去
in n int,
out res int # 該形參可以返回出去
)
begin
select tname from teacher where tid>m and tid<n;
set res=666; # 將res變數修改 用來標識當前的儲存過程程式碼確實執行了
end $$
delimiter ;
# 針對形參res 不能直接傳資料 應該傳一個變數名
# 定義變數
set @ret = 10;
# 檢視變數對應的值
select @ret;
在pymysql模組中如何呼叫儲存過程呢?
import pymysql
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
passwd = '123456',
db = 'day48',
charset = 'utf8',
autocommit = True
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# 呼叫儲存過程
cursor.callproc('p1',(1,5,10))
"""
@_p1_0=1
@_p1_1=5
@_p1_2=10
"""
# print(cursor.fetchall())
cursor.execute('select @_p1_2;')
print(cursor.fetchall())
知識擴充套件
函式
跟儲存過程是有區別的,儲存過程是自定義函式,函式就類似於是內建函式
('jason','0755','ls -l /etc',NOW(),'yes')
CREATE TABLE blog (
id INT PRIMARY KEY auto_increment,
NAME CHAR (32),
sub_time datetime
);
INSERT INTO blog (NAME, sub_time)
VALUES
('第1篇','2015-03-01 11:31:21'),
('第2篇','2015-03-11 16:31:21'),
('第3篇','2016-07-01 10:21:31'),
('第4篇','2016-07-22 09:23:21'),
('第5篇','2016-07-23 10:11:11'),
('第6篇','2016-07-25 11:21:31'),
('第7篇','2017-03-01 15:33:21'),
('第8篇','2017-03-01 17:32:21'),
('第9篇','2017-03-01 18:31:21');
select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
流程控制(瞭解)
# if判斷
delimiter //
CREATE PROCEDURE proc_if ()
BEGIN
declare i int default 0;
if i = 1 THEN
SELECT 1;
ELSEIF i = 2 THEN
SELECT 2;
ELSE
SELECT 7;
END IF;
END //
delimiter ;
# while迴圈
delimiter //
CREATE PROCEDURE proc_while ()
BEGIN
DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
SELECT
num ;
SET num = num + 1 ;
END WHILE ;
索引
ps:資料都是存在與硬碟上的,查詢資料不可避免的需要進行IO操作
索引:就是一種資料結構,類似於書的目錄。意味著以後在查詢資料的應該先找目錄再找資料,而不是一頁一頁的翻書,從而提升查詢速度降低IO操作
索引在MySQL中也叫“鍵”,是儲存引擎用於快速查詢記錄的一種資料結構
- primary key
- unique key
- index key
注意foreign key不是用來加速查詢用的,不在我們的而研究範圍之內
上面的三種key,前面兩種除了可以增加查詢速度之外各自還具有約束條件,而最後一種index key沒有任何的約束條件,只是用來幫助你快速查詢資料
本質
通過不斷的縮小想要的資料範圍篩選出最終的結果,同時將隨機事件(一頁一頁的翻)
變成順序事件(先找目錄、找資料)
也就是說有了索引機制,我們可以總是用一種固定的方式查詢資料
一張表中可以有多個索引(多個目錄)
索引雖然能夠幫助你加快查詢速度但是也有缺點
"""
1 當表中有大量資料存在的前提下 建立索引速度會很慢
2 在索引建立完畢之後 對錶的查詢效能會大幅度的提升 但是寫的效能也會大幅度的降低
"""
索引不要隨意的建立!!!
b+樹
"""
只有葉子節點存放的是真實的資料 其他節點存放的是虛擬資料 僅僅是用來指路的
樹的層級越高查詢資料所需要經歷的步驟就越多(樹有幾層查詢資料就需要幾步)
一個磁碟塊儲存是有限制的
為什麼建議你將id欄位作為索引
佔得空間少 一個磁碟塊能夠儲存的資料多
那麼久降低了樹的高度 從而減少查詢次數
"""
聚集索引(primary key)
"""
聚集索引指的就是主鍵
Innodb 只有兩個檔案 直接將主鍵存放在了idb表中
MyIsam 三個檔案 單獨將索引存在一個檔案
"""
輔助索引(unique,index)
查詢資料的時候不可能一直使用到主鍵,也有可能會用到name,password等其他欄位
那麼這個時候你是沒有辦法利用聚集索引。這個時候你就可以根據情況給其他欄位設定輔助索引(也是一個b+樹)
"""
葉子節點存放的是資料對應的主鍵值
先按照輔助索引拿到資料的主鍵值
之後還是需要去主鍵的聚集索引裡面查詢資料
"""
覆蓋索引
在輔助索引的葉子節點就已經拿到了需要的資料
# 給name設定輔助索引
select name from user where name='jason';
# 非覆蓋索引
select age from user where name='jason';
測試索引是否有效的程式碼
感興趣就自己試一試 不感興趣直接忽略
**準備**
```mysql
#1. 準備表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);
#2. 建立儲存過程,實現批量插入記錄
delimiter $$ #宣告儲存過程的結束符號為$$
create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
set i=i+1;
end while;
END$$ #$$結束
delimiter ; #重新宣告分號為結束符號
#3. 檢視儲存過程
show create procedure auto_insert1\G
#4. 呼叫儲存過程
call auto_insert1();
```
``` mysql
# 表沒有任何索引的情況下
select * from s1 where id=30000;
# 避免列印帶來的時間損耗
select count(id) from s1 where id = 30000;
select count(id) from s1 where id = 1;
# 給id做一個主鍵
alter table s1 add primary key(id); # 速度很慢
select count(id) from s1 where id = 1; # 速度相較於未建索引之前兩者差著數量級
select count(id) from s1 where name = 'jason' # 速度仍然很慢
"""
範圍問題
"""
# 並不是加了索引,以後查詢的時候按照這個欄位速度就一定快
select count(id) from s1 where id > 1; # 速度相較於id = 1慢了很多
select count(id) from s1 where id >1 and id < 3;
select count(id) from s1 where id > 1 and id < 10000;
select count(id) from s1 where id != 3;
alter table s1 drop primary key; # 刪除主鍵 單獨再來研究name欄位
select count(id) from s1 where name = 'jason'; # 又慢了
create index idx_name on s1(name); # 給s1表的name欄位建立索引
select count(id) from s1 where name = 'jason' # 仍然很慢!!!
"""
再來看b+樹的原理,資料需要區分度比較高,而我們這張表全是jason,根本無法區分
那這個樹其實就建成了“一根棍子”
"""
select count(id) from s1 where name = 'xxx';
# 這個會很快,我就是一根棍,第一個不匹配直接不需要再往下走了
select count(id) from s1 where name like 'xxx';
select count(id) from s1 where name like 'xxx%';
select count(id) from s1 where name like '%xxx'; # 慢 最左匹配特性
# 區分度低的欄位不能建索引
drop index idx_name on s1;
# 給id欄位建普通的索引
create index idx_id on s1(id);
select count(id) from s1 where id = 3; # 快了
select count(id) from s1 where id*12 = 3; # 慢了 索引的欄位一定不要參與計算
drop index idx_id on s1;
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';
# 針對上面這種連續多個and的操作,mysql會從左到右先找區分度比較高的索引欄位,先將整體範圍降下來再去比較其他條件
create index idx_name on s1(name);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 並沒有加速
drop index idx_name on s1;
# 給name,gender這種區分度不高的欄位加上索引並不難加快查詢速度
create index idx_id on s1(id);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 快了 先通過id已經講資料快速鎖定成了一條了
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 慢了 基於id查出來的資料仍然很多,然後還要去比較其他欄位
drop index idx_id on s1
create index idx_email on s1(email);
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 快 通過email欄位一劍封喉
```
#### 聯合索引
```mysql
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';
# 如果上述四個欄位區分度都很高,那給誰建都能加速查詢
# 給email加然而不用email欄位
select count(id) from s1 where name='jason' and gender = 'male' and id > 3;
# 給name加然而不用name欄位
select count(id) from s1 where gender = 'male' and id > 3;
# 給gender加然而不用gender欄位
select count(id) from s1 where id > 3;
# 帶來的問題是所有的欄位都建了索引然而都沒有用到,還需要花費四次建立的時間
create index idx_all on s1(email,name,gender,id); # 最左匹配原則,區分度高的往左放
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 速度變快
```
總結:上面這些操作,你感興趣可以敲一敲,不感興趣你就可以不用敲了,權當看個樂呵。理論掌握了就行了
慢查詢日誌
設定一個時間檢測所有超出該時間的sql語句,然後針對性的進行優化!
Mysql的四種隔離級別
SQL標準定義了4類隔離級別,包括了一些具體規則,用來限定事務內外的哪些改變是可見的,哪些是不可見的。低級別的隔離級一般支援更高的併發處理,並擁有更低的系統開銷。
Read Uncommitted(讀取未提交內容)
在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。本隔離級別很少用於實際應用,因為它的效能也不比其他級別好多少。讀取未提交的資料,也被稱之為髒讀(Dirty Read)。
Read Committed(讀取提交內容)
這是大多數資料庫系統的預設隔離級別(但不是MySQL預設的)。它滿足了隔離的簡單定義:一個事務只能看見已經提交事務所做的改變。這種隔離級別 也支援所謂的不可重複讀(Nonrepeatable Read),因為同一事務的其他例項在該例項處理其間可能會有新的commit,所以同一select可能返回不同結果。
Repeatable Read(可重讀)
這是MySQL的預設事務隔離級別,它確保同一事務的多個例項在併發讀取資料時,會看到同樣的資料行。不過理論上,這會導致另一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指當用戶讀取某一範圍的資料行時,另一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的資料行時,會發現有新的“幻影” 行。InnoDB和Falcon儲存引擎通過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。
Serializable(可序列化)
這是最高的隔離級別,它通過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每個讀的資料行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭。
出現問題
這四種隔離級別採取不同的鎖型別來實現,若讀取的是同一個資料的話,就容易發生問題。例如:
髒讀(Drity Read):某個事務已更新一份資料,另一個事務在此時讀取了同一份資料,由於某些原因,前一個RollBack了操作,則後一個事務所讀取的資料就會是不正確的。
不可重複讀(Non-repeatable read):在一個事務的兩次查詢之中資料不一致,這可能是兩次查詢過程中間插入了一個事務更新的原有的資料。
幻讀(Phantom Read):在一個事務的兩次查詢中資料筆數不一致,例如有一個事務查詢了幾列(Row)資料,而另一個事務卻在此時插入了新的幾列資料,先前的事務在接下來的查詢中,就會發現有幾列資料是它先前所沒有的。
在MySQL中,實現了這四種隔離級別,分別有可能產生問題如下所示:
測試Mysql的隔離級別
下面,將利用MySQL的客戶端程式,我們分別來測試一下這幾種隔離級別。
測試資料庫為demo,表為test;表結構:
兩個命令列客戶端分別為A,B;不斷改變A的隔離級別,在B端修改資料。
(一)、將A的隔離級別設定為read uncommitted(未提交讀)
set session transaction isolation level read uncommitted;
檢視隔離級別是否設定成功
select @@transaction_isolation (mysql版本 8.0 以後)
select @@tx_isolation (mysql版本 8.0 之前)
檢視mysql版本
> status
A:啟動事務,此時資料為初始狀態
start transaction;
B:啟動事務,更新資料,但不提交
start transaction;
A:再次讀取資料,發現數據已經被修改了,這就是所謂的“髒讀”
B:回滾事務
rollback;
A:再次讀資料,發現數據變回初始狀態
經過上面的實驗可以得出結論,事務B更新了一條記錄,但是沒有提交,此時事務A可以查詢出未提交記錄。造成髒讀現象。未提交讀是最低的隔離級別。
(二)、將客戶端A的事務隔離級別設定為read committed(已提交讀)
set session transaction isolation level read committed;
A:啟動事務,此時資料為初始狀態
B:啟動事務,更新資料,但不提交
A:再次讀資料,發現數據未被修改
B:提交事務
A:再次讀取資料,發現數據已發生變化,說明B提交的修改被事務中的A讀到了,這就是所謂的“不可重複讀”
經過上面的實驗可以得出結論,已提交讀隔離級別解決了髒讀的問題,但是出現了不可重複讀的問題,即事務A在兩次查詢的資料不一致,因為在兩次查詢之間事務B更新了一條資料。已提交讀只允許讀取已提交的記錄,但不要求可重複讀。
(三)、將A的隔離級別設定為repeatable read(可重複讀)
A:啟動事務,此時資料為初始狀態
B:啟動事務,更新資料,但不提交
A:再次讀取資料,發現數據未被修改
B:提交事務
A:再次讀取資料,發現數據依然未發生變化,這說明這次可以重複讀了
B:插入一條新的資料,並提交
A:再次讀取資料,發現數據依然未發生變化,雖然可以重複讀了,但是卻發現讀的不是最新資料,這就是所謂的“幻讀”
A:提交本次事務,再次讀取資料,發現讀取正常了
由以上的實驗可以得出結論,可重複讀隔離級別只允許讀取已提交記錄,而且在一個事務兩次讀取一個記錄期間,其他事務部的更新該記錄。但該事務不要求與其他事務可序列化。例如,當一個事務可以找到由一個已提交事務更新的記錄,但是可能產生幻讀問題(注意是可能,因為資料庫對隔離級別的實現有所差別)。像以上的實驗,就沒有出現數據幻讀的問題
(四)、*將A的隔離級別設定為*可序列化(Serializable)
A:啟動事務,此時資料為初始狀態
B:發現B此時進入了等待狀態,原因是因為A的事務尚未提交,只能等待(此時,B可能會發生等待超時)
A:提交事務
B:發現插入成功
serializable完全鎖定欄位,若一個事務來查詢同一份資料就必須等待,直到前一個事務完成並解除鎖定為止。是完整的隔離級別,會鎖定對應的資料表格,因而會有效率的問題。