MySQL數據庫04 高級特性,事務,視圖,索引,存儲過程

MySQL的事務處理

概念

事務就是將一組SQL語句放在同一批次內去執行

如果一個SQL語句出錯,則該批次內的所有SQL都將被取消執行

銀行轉賬

A==> B 轉賬,

A的賬戶餘額 減去 1000 ,

B的賬戶餘額 加上 1000

該兩個步驟全部完成, 纔可以說, 轉賬業務完成

事務的特性

  • 原子性 不可分割的最小單位
  • 一致性(整體性) 遵循能量守恆定律
  • 隔離性
  • 持久性 (持續性/ 永久性) 一旦確認數據沒有問題, 提交到數據庫, 永久保存

案例實現

create table if not exists account(
    id varchar(5),
    name varchar(50),
    balance double
)
insert into account values('10086','老畢',30000);
insert into account values('10010','鳳姐',0);

轉賬業務

# 老畢 - 10000
update account set balance = balance - 10000 where id = 10086;
# 鳳姐 + 10000
update account set balance = balance + 10000 where id = 10010;

問題

如果 兩條語句 , 第一條正常執行, 第二條 出現異常, 轉賬業務只完成一半,業務是不能算作成功的, 應該把 已經減去的錢 , 加回去

mysql 事務的實現

1- 改變mysql 的事務提交方式 (默認是自動提交, 修改爲手動提交)
	set autocommit = 0; 關閉自動提交
	set autocommit = 1; 開啓自動提交
2- 開啓一個事務 標記事務的起始點
	start Transaction; 
3- commit 提交一個事務給數據庫 , 重新設定事務的起點
4- rollback 事務回滾, 數據回到本次事務的初始狀態
set autocommit = 0;
start Transaction;
# 老畢 - 10000
update account set balance = balance - 10000 where id = 10086;
# 鳳姐 + 10000
update account set balance = balance1 + 10000 where id = 10010;
rollback; # 事務起始點之後的所有操作, 都取消
# 如果沒有任何異常, 就可以提交 , 每一次提交都是一個新的起始點
commit;

隔離性

一個事務的執行不能被其它事務干擾。即一個事務內部的操作及使用的數據對其它併發事務是隔離的,併發執行的各個事務之間不能互相干擾。

四種隔離級別

第一類丟失更新:在沒有事務隔離的情況下,兩個事務都同時更新一行數據,但是第二個事務卻中途失敗退出, 導致對數據的兩個修改都失效了。
例如:
 張三的工資爲5000,事務A中獲取工資爲5000,事務B獲取工資爲5000,匯入100,並提交數據庫,工資變爲5100,
 隨後
 事務A發生異常,回滾了,恢復張三的工資爲5000,這樣就導致事務B的更新丟失了。

Read Uncommitted(讀取未提交內容)

髒讀:髒讀就是指當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然後使用了這個數據。

張三的工資爲5000,事務A中把他的工資改爲8000,但事務A尚未提交。
與此同時,
事務B正在讀取張三的工資,讀取到張三的工資爲8000。
隨後,
事務A發生異常,而回滾了事務。張三的工資又回滾爲5000。
最後,
事務B讀取到的張三工資爲8000的數據即爲髒數據,事務B做了一次髒讀。

Read Committed(讀取提交內容)

不可重複讀:是指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那麼,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,那麼第一個事務兩次讀到的的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱爲是不可重複讀。(主要修改)

不可重複讀的重點是修改,同樣的條件,你讀取過的數據,再次讀取出來發現值不一樣了 幻讀的重點在於新增或者刪除,同樣的條件,第 1 次和第 2 次讀出來的記錄數不一樣

在事務A中,讀取到張三的工資爲5000,操作沒有完成,事務還沒提交。
與此同時,
事務B把張三的工資改爲8000,並提交了事務。
隨後,
在事務A中,再次讀取張三的工資,此時工資變爲8000。在一個事務中前後兩次讀取的結果並不致,導致了不可重複讀。

Repeatable Read(可重讀)

幻讀:是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,以後就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。(針對insert)

目前工資爲5000的員工有10人,事務A讀取所有工資爲5000的人數爲10人。
此時,
事務B插入一條工資也爲5000的記錄。
這是,事務A再次讀取工資爲5000的員工,記錄爲11人。此時產生了幻讀

Serializable(可串行化)

傳播行爲

當多個事務相互發生關聯的時候, 事務如何嵌套使用

視圖

概念

概念 本質就是一個查詢語句, 是對查詢語句執行結果的引用,不存儲具體的數據

入門案例

子查詢 案例

查詢出 每個部門的編號,名稱,位置,部門人數,和平均工資

# 1- 先查詢出 部門人數,和平均工資
select deptno , count(1) ct ,avg(sal) asal
from emp 
group by deptno
# 2- 多表查詢
select d.deptno,d.dname,d.loc,nt.ct,nt.asal
from dept d join (select deptno , count(1) ct ,avg(sal) asal
    from emp 
    group by deptno) nt 
on d.deptno = nt.deptno

分析案例

使用子查詢, 語句較爲複雜, 可以進行一定程度上的優化

優化1

使用新表的方式優化 把子查詢的結果作爲一張實體表保存到數據庫

create table nt as 
select deptno , count(1) ct ,avg(sal) asal
from emp 
group by deptno

以上案例就可以簡寫爲

select d.deptno,d.dname,d.loc,nt.ct,nt.asal
from dept d join nt nt 
on d.deptno = nt.deptno

該優化採取的方式, 是把 子查詢的結果保存到數據庫中, 好處是子查詢的結果刻意直接作爲表使用, 弊端, 佔用數據庫存儲空間

優化2

採取視圖的方式優化

create view vnt as 
select deptno , count(1) ct ,avg(sal) asal
from emp 
group by deptno

視圖和新建實體表的區別

買東西, 
可以去超市商場買實際的商品 通過實體表
通過使用手機.電腦可以去網上商城,  下單所看到的商品  通過視圖

學校開設分校

主校區 和 分校區
主校區 有自己的數據庫 (所有資源)
分校區 也有自己的數據庫(需要查看主校區數據)

分校如何查看主校數據 
1- 通過網絡訪問主校區數據數據庫 (主校區給分校區提供視圖)
2- 把主校區相應數據 拷貝一份到本地 (新建表)
問題 如果主校區數據發生變化
	使用視圖 可以訪問到 變化之後的數據
	使用本地表 不能隨着 主校區數據的變化而變化

1- 視圖本質就是一條SQL 語句, 佔用空間非常小

​ 新建數據表, 是要保存到數據庫中的, 佔用較多空間

2- 視圖內容隨着 所查詢的表的內容的改變而改變

​ 新建數據表. 不會發生變化

優勢

方便操作,特別是查詢操作,減少複雜的SQL語句,增強可讀性;
更加安全,數據庫授權命令不能限定到特定行和特定列,但是通過合理創建視圖,可以把權限限定到行列級別;

語法

創建方式

create view 視圖名 as  查詢語句 
# 把查詢到的結果保存到視圖中, 該視圖指向該條語句的查詢結果

查看方式

視圖就是虛擬的表,因此,查看視圖的方法和查看錶的方法是一樣的:
show tables;
select * from 視圖名;
查看視圖字段詳情的方法有兩種,
一種是和查看錶詳情一樣使用desc 視圖名,
另外一種方法是show fields from 視圖名:

刪除視圖

刪除視圖是指刪除數據庫中已存在的視圖,刪除視圖時,只能刪除視圖的定義,不會刪除數據,也就是說不動基表:
DROP VIEW [IF EXISTS]   
view_name [, view_name] ...

案例

# 部門人數, 平均工資 視圖
create view vt as 
select deptno , count(1) ct ,avg(sal) asal
from emp 
group by deptno
# 查看
show tables;
desc vt;
# 刪除
drop view if exists vt ;

案例2

# 所有員工的姓名和工資以及部門編號 視圖
create view ns as select ename,sal from emp;
# 通過視圖 更改emp 表的數據
SELECT * FROM ns;
UPDATE ns SET sal = 900 WHERE ename = 'SMITH'
# emp 表也發生了變化

案例3

查詢20號部門員工的姓名和工資以及部門編號 
create view ns1 as  select empno,ename,sal ,deptno from emp where deptno = 20;

INSERT INTO ns1 VALUES(10086,'老畢',8000,20);
SELECT * FROM ns1;
INSERT INTO ns1 VALUES(10010,'老張',8000,30);
SELECT * FROM ns1;

用戶插入的數據 和 視圖中所顯示的數據不相關

可以在創建視圖的時候 添加 WITH CHECK OPTION

保證更新視圖是在該視圖的權限範圍之內
如果在創建視圖的時候制定了“WITH CHECK OPTION”,那麼更新數據時不能插入或更新不符合視圖限制條件的記錄。

重複演示

drop view if exists ns1 ;
create view ns1 as  select empno,ename,sal ,deptno from emp where deptno = 20 WITH CHECK OPTION;

INSERT INTO ns1 VALUES(10086,'老畢',8000,20);
SELECT * FROM ns1;
INSERT INTO ns1 VALUES(10010,'老張',8000,30);
SELECT * FROM ns1;

索引

概念

圖書館找一本書經歷 , 文學類 - 古典文學

在書中找一些具體內容, 每一本書 都有自己的目錄

作用

提高查詢速度
使用分組和排序子句進行數據檢索時,可以顯著減少分組和排序的時間
全文檢索字段進行搜索優化

分類

主鍵索引(PRIMARY KEY)
唯一索引(UNIQUE)
常規索引(INDEX)
全文索引(FULLTEXT)

常規索引

語法

index和key關鍵字都可設置常規索引

案例

CREATE TABLE `emp1` (
    `empno` INT(4) NOT NULL PRIMARY KEY,
    `ename` VARCHAR(10),  
    `job` VARCHAR(9),  
    `mgr` INT(4),  
    `hiredate` DATE,  
    `sal` FLOAT(7,2),  
    `comm` FLOAT(7,2),  
    `deptno` INT(2)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO EMP1 VALUES (7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20); 
INSERT INTO EMP1 VALUES (7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
INSERT INTO EMP1 VALUES (7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30); 
INSERT INTO EMP1 VALUES (7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20); 
INSERT INTO EMP1 VALUES (7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30); 
INSERT INTO EMP1 VALUES (7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30); 
INSERT INTO EMP1 VALUES (7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10); 
INSERT INTO EMP1 VALUES (7788,'SCOTT','ANALYST',7566,'1987-07-13',3000,NULL,20); 
INSERT INTO EMP1 VALUES (7839,'KING','PRESIDENT',NULL,'1981-11-07',5000,NULL,10); 
INSERT INTO EMP1 VALUES (7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30); 
INSERT INTO EMP1 VALUES (7876,'ADAMS','CLERK',7788,'1987-07-13',1100,NULL,20); 
INSERT INTO EMP1 VALUES (7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30); 
INSERT INTO EMP1 VALUES (7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20); 
INSERT INTO EMP1 VALUES (7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);
# 查詢工資爲 1250 的所有 員工
select * from emp1 where sal = 1250;
使用關鍵字 分析sql 語句執行的性能
EXPLAIN select * from emp1 where sal = 1250;

在這裏插入圖片描述

使用索引優化查詢之後, 對比顯示效果

給emp1 表的 sal 字段添加索引
alter table emp1 add index index_sal (sal);
使用關鍵字 分析sql 語句執行的性能
EXPLAIN select * from emp1 where sal = 1250;

在這裏插入圖片描述

注意

索引應加在查找條件的字段 
(給書籍添加索引,一般是類別索引,很少頁數索引, 原因很少人專門根據頁數查找書籍)
不宜添加太多常規索引,影響數據的插入、刪除和修改操作
(添加索引之後, 圖書館每新增一本書, 都需要把這本書掛在索引上, 需要更新索引)

索引的添加方式

alter table 表名 add index 索引名 (需要添加索引的字段, 可以寫多個);
在創建表的同時
create table 表名(
	字段 類型 屬性,
	index / key 索引名 (字段,可以寫多個)
)

全文索引

fulltext
只能用於MyISAM類型的數據表
只能用於 CHAR 、 VARCHAR、TEXT數據列類型
適合大型數據集

語法格式

CREATE TABLE  表名 (
       #省略一些SQL語句
       FULLTEXT (字段名) 
)ENGINE=MYISAM;
ALTER TABLE 表名 ADD FULLTEXT (字段名);

索引的查看

show index (keys) from 表名

索引的刪除

drop index 索引名 on 表名;
alter table 表名 drop index 索引名;

索引準則

1- 索引不是越多越好
2- 不要對經常變動的數據加索引
3- 小數據量的表建議不要加索引
4- 索引一般應加在查找條件的字段 (在WHERE、ORDER BY 子句中經常使用的字段
) 

sql查詢優化

1- 索引字段上進行運算會使索引失效。
儘量避免在WHERE子句中對字段進行函數或表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如: SELECT * FROM T1 WHERE F1/2=100 應改爲: SELECT * FROM T1 WHERE F1=100*2
2- 避免使用!=或<>、IS NULL或IS NOT NULL、IN ,NOT IN等這樣的操作符.
因爲這會使系統無法使用索引,而只能直接搜索表中的數據。例如: SELECT id FROM employee WHERE id != “B%” 優化器將無法通過索引來確定將要命中的行數,因此需要搜索該表的所有行。在in語句中能用exists語句代替的就用exists.
3- 儘量使用數字型字段
一部分開發人員和數據庫管理人員喜歡把包含數值信息的字段 設計爲字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因爲引擎在處理查詢和連接回逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。
4- 必要時強制查詢優化器使用某個索引
SELECT * FROM T1 WHERE nextprocess = 1 AND processid IN (8,32,45) 改成: SELECT * FROM T1 (INDEX = IX_ProcessID) WHERE nextprocess = 1 AND processid IN (8,32,45) 則查詢優化器將會強行利用索引IX_ProcessID 執行查詢。
5-  儘量避免在索引過的字符數據中,不要使用 like '%L%' 推薦 like 'L%'這也使得引擎無法利用索引。
見如下例子: SELECT * FROM T1 WHERE NAME LIKE ‘%L%’ SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’ SELECT * FROM T1 WHERE NAME LIKE ‘L%’ 即使NAME字段建有索引,前兩個查詢依然無法利用索引完成加快操作,引擎不得不對全表所有數據逐條操作來完成任務。而第三個查詢能夠使用索引來加快操作,不要習慣性的使用 ‘%L%’這種方式(會導致全表掃描),如果可以使用`L%’相對來說更好;

數據庫的備份和恢復

數據庫備份操作

C:\Users\Administrator>mysqldump -uroot -p emp > d:/emp.sql
Enter password: ******
# mysqldump -uroot -p 要備份的數據庫 > d:/emp.sql (要備份到的目錄文件)

數據庫的恢復

第一種

# mysql -uroot -p123456; # 登錄數據庫
USE test;  # 進入要恢復的數據庫
source d:/emp.sql; #使用 source 恢復數據庫

第二種

C:\Users\Administrator>mysql -uroot -p emp <  d:/emp.sql
Enter password: ******
# mysql -uroot -p 要恢復到的數據庫名(必須存在) <  d:/emp.sql(源文件)

數據庫數據的備份和恢復

#備份數據庫myschool中的student表中的studentno及studentname列到文件myschool3.sql中
USE myschool;
SELECT  studentno,  studentname  INTO  OUTFILE  'e:/myschool3.sql'  FROM  student;

#恢復文件myschool3.sql中的數據到test數據庫的t2表中來
USE test;
CREATE TABLE t2(
	id INT(4),
	sname VARCHAR(20)
)
LOAD  DATA  INFILE  'e:/myschool3.sql'  INTO  TABLE  t2(id, sname);

SELECT * FROM t2;

存儲過程

資料 https://www.cnblogs.com/chenhuabin/p/10142190.html

語法格式

delimiter $
create procedure 存儲過程名 ([參數的輸入輸出類型 參數名 參數數據類型] ...)
begin 
end $
delimiter;

參數的輸入輸出類型 in out inout

銀行轉賬案例

create table if not exists account(
	id int(5) primary key,
	name varchar(50) not null,
	balance double 
);
insert  into account values (10086,'移動',2000);
insert  into account values (10010,'聯通',2000);

案例1

要求 : 寫一個存儲過程 完成轉賬案例

# 三個參數  轉出人賬號,  轉入人賬號,  金額,
DELIMITER $$
USE `emp`$$
DROP PROCEDURE IF EXISTS `transfer`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `transfer`(
	in acc_out int,
	in acc_in int , 
	in money double)
begin 
	update account set balance = balance - money where id = acc_out;
	update account set balance = balance + money where id = acc_in;
end$$
DELIMITER ;

調用該存儲過程

set @acc_out = 10086;
set @acc_in = 10010;
set @money = 1000;
call transfer(@acc_out,@acc_in,@money);

案例2

創建一個帶參數的存儲過程,刪除emp表中empno爲指定值得記錄,並返回最高最高月薪,也返回大於指定月薪的人數。

DELIMITER $$
CREATE PROCEDURE `emp_pro`(
	in id int, # 要刪除的id
	out max1 int , # 返回值 最高月薪
	inout p_sal int # 傳參爲 指定月薪, 返回值爲高於此月薪的人數
	)
begin
	delete from emp where empno = id;
	select max(sal) into max1 from emp;
	select count(1) into p_sal from emp where sal > p_sal;
end $$
DELIMITER ;

調用

set @p_sal = 1250 ;
call emp_pro(7369 , @max , @p_sal);
select @max , @p_sal ;

觸發器

類似於 JavaScript 中的事件
當執行某種操作的時候, 觸發某種事件

參考 https://www.cnblogs.com/fps2tao/p/10400936.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章