1. 程式人生 > 其它 >sql檢視 觸發器 事務 隔離級別 以及sql語句中的函式

sql檢視 觸發器 事務 隔離級別 以及sql語句中的函式

今日內容概要

SQL注入問題

在使用python中使用sql語句來操作資料庫的時候在獲取使用者輸入使用格式化輸出 就會出現一個sql注入問題
eg:
sql = " select * from userinfo where name=%s and pwd=%s "
cursor.execute(sql)

#現象一 輸對使用者名稱就可以登入
select * from userinfo where name = '使用者名稱' --snjandj'

#現象二 不需要對的使用者名稱和密碼也可以登入
select * from userinfo where name='xyz' or 1=1 -- aksdjasldj' and pwd='' 

產生原因>>>利用sql語句中的註釋輸入--後 將會把後面的條件全部註釋掉

sql注入:利用特殊符號的組合產生特殊的含義 從而避開正常的業務邏輯
   
解決:
sql = "select * from userinfo where name=%s and pwd=%s"
cursor.execute(sql,(username,password)) 
# pymysql 會自動處理有特殊含義的符號
'''
補充:
	如果想要一次性執行多個sql語句
	executemany(sql,[(),(),().....])
'''

檢視

檢視就是通過查詢一張虛擬表 然後儲存下來 下次直接使用

create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;

1.檢視的表只能用來查詢不能做其他操作
2.檢視儘量少用 會和真正的表產生混淆 從而干擾操作者

觸發器

達到某個條件之後自動觸發執行
在mysql中詳細的說明是觸發器:針對表執行增、刪、改操作能夠自動觸發
#主要有六種情況:增前、增後、刪前、刪後、改前、改後

create trigger 觸發器的名字 before/after insert/update/delete on 表名 for each row
begin
	sql語句
end

# begin表示著開始 end表示著結束

1.觸發器命名也是有一定的規律
	tri_before_insert_t1
	tri_after_delete_t2
	tri_after_update_t2
2.臨時修改sql語句的結束符
	因為有些操作中需要使用分號
    
# 觸發器實際運用
CREATE TABLE cmd (
	id INT PRIMARY KEY auto_increment,
	USER CHAR(32),
    priv char(32),
    cmd char(32),
    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 $$  # 將mysql預設的結束符由;換成$$
create trigger tri_after_insert_cmd after insert on cmd for each row
begin
    if NEW.success = 'no' then  # 新記錄都會被MySQL封裝成NEW物件
        insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
    end if;
end $$
delimiter ;  # 結束之後記得再改回來,不然後面結束符就都是$$了

#往表cmd中插入記錄,觸發觸發器,根據IF的條件決定是否插入錯誤日誌
INSERT INTO cmd (
    USER,
    priv,
    cmd,
    sub_time,
    success
)
VALUES
    ('kevin','0755','ls -l /etc',NOW(),'yes'),
    ('kevin','0755','cat /etc/passwd',NOW(),'no'),
    ('kevin','0755','useradd xxx',NOW(),'no'),
    ('kevin','0755','ps aux',NOW(),'yes');

# 查詢errlog表記錄
select * from errlog;
# 檢視所有的觸發器
show triggers;
# 刪除觸發器
drop trigger tri_after_insert_cmd;

事務

事務的四大特性(ACID)
	A:原子性
        事務中的各項操作是不可分割的整體 要麼同時成功要麼同時失敗
  	C:一致性
        是資料庫從一個一致性狀態變到另一個一致性狀態
    I:隔離性
        多個事務之間彼此不干擾
  	D:永續性
        也稱永久性 指一個事務一旦提交 它對資料庫中資料的改變就應該是永久性的
  
create table user(
id int primary key auto_increment,
name char(32),
balance int
);

insert into user(name,balance)
values('xiaochen',1000),('aaaa',1000),('tack',1000);

#修改資料之前先開啟事務操作
start transaction;

#修改操作
update user set balance=900 where name='xiaochen'; # 支付100
update user set balance=1010 where name='aaa'; # 中介收10元
update user set balance=1090 where name='tack'; #賣家收90元

# 回滾到上一個狀態
rollback;

# 開啟事務之後,只要沒有執行commit操作,資料其實沒有真正重新整理到硬碟
commit;

'''
事務相關關鍵字
	stare transaction >>> 開始事務
	rollback          >>> 回滾到上一個狀態(事務開始時)
	commit            >>> 儲存
	savepoint         >>> 節點操作 使rollback回退到節點位置
'''

隔離級別

在sql標準中定義了四種隔離級別,每一種級別都規定了一個事務所作的修改
InnoDB支援所有隔離級別
	set transaction isolaation level 級別
#1.read uncommitted(未提交讀)
	事務中的修改其實沒有提交 對其他事務也都是可見的 事務可以讀取未提交的資料 這一現象稱之為'髒讀'
#2.read committed(提交讀)
	大多數資料庫系統預設的隔離級別
  	一個事務從開始直到提交之前所作的任何修改對其他都是不可見的 這個級別也可以叫做'不可重複讀'
#3.repeatable read(可重複讀) MYSQL預設隔離級別
	能夠解決'髒讀'問題 但是無法解決'幻讀'
    幻讀:指的是當某個事務在讀取某個範圍內的記錄時另外一個事務又在該範圍內插入新的記錄,當之前的事務子再次讀取該範圍的記錄會產生幻行,innodb和xtradb通過多版本併發控制(mvcc)之間隙鎖策略解決該問題
#4.serilizable(可序列讀)
	強制事務序列執行 很少使用該級別
    
macc只能在read committed(提交讀)、repeatable read(可重複讀)兩種隔離級別下工作,其他兩個不相容(read uncommitted:總是讀取最新serializable:所有的行都加鎖)

InnoDB的MVCC通過在每行記錄後面儲存兩個隱藏的列來實現MVCC
	一個列儲存了行的建立時間
 	一個列儲存了行的過期時間(或刪除時間)  # 本質是系統版本號
沒開始一個新的事務版本號都會自動遞增 事務開始時刻的系統版本號會作為事務的版本號用來和查詢到每行記錄版本號進行比較

'''
由此當我們查詢一條記錄的時候,只有滿足以下兩個條件的記錄才會被顯示出來:
	1.當前事務id要大於或者等於當前行的create_version值 這表示在事務開始前這行資料已經存在了
	2.當前事務id要小於delete_vresion值 這表示在事務開始之後這行記錄才被刪除
'''

儲存過程

可以看成是python中的自定義函式

# 無參函式
delimiter $$
create procedure p1()
begin
	select * from cmd;
end $$
delimiter ;
'''使用完記得把結束符換回;'''

# 呼叫
call p1()

# 有參函式
delimiter $$
create procedure p2(
	in m int, # in表示這個引數必須只能是傳入不能被返回出去
    out res int # out表示這個引數可以被返回出去 還有一個input表示既可以傳入也可以被返回出去
)
begin
	select * from cmd where id > m;
  	set res=0; # 用來標誌儲存過程是否執行
end $$
delimiter ;

# 針對res需要先提前定義
set @res=10; #定義
select @res; #檢視
call p1(1,5,@res) #呼叫
select @res #檢視
>>>>不能直接傳入 需要傳入的是一個變數名 不讓改值我們也無法知道

'''
檢視儲存過程具體資訊
	show crete procedure pro1;
檢視所有儲存過程
	show procedure status;
刪除儲存過程
	drop procedure pro1;
'''

# 大前提:儲存過程在哪個庫下面建立的只能在對應的庫下面才能使用

# 1.直接在mysql中呼叫
set @res=10 # 一定需要先定義
call p1(2,4,10); # 報錯 修改了10的資料沒有意義 無法查詢
call p1(2,4,@res);

# 檢視結果
select @res;

# 2.在python程式中呼叫
pymysql連結mysql
產生的遊標cursor.callproc('p1',(2,4,10)) 
# 內部原理 @_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute('select @_p1_2;')

函式

可以看成是python中的內建函式

'ps:可以通過help 函式名  檢視幫助資訊!'
# 1.移除指定字元
Trim、LTrim、RTrim

# 2.大小轉換
Lower、Upper

# 3.獲取左右起始指定個數字元
Left、Right

# 4.返回讀音相似值(只針對英文)
Soundex
'''
客戶表中有一個顧客登記的使用者名稱為J.Lee
但如果這是輸入錯誤真名其實叫J.Lie,可以使用soundex匹配發音類似的
	where Soundex(name)=Soundex('J.Lie')
'''
# 5.日期格式:data_format
'''在MYsql中表示時間格式儘量採用2022-11-11形式'''
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');

1.獲取指定的時間
where Data(sub_time)='2015-03-01'

2.獲取指定的年份和月份
where Year(sub_time)=2016 AND Monrh(sub_time)=07;
# 更多日期處理相關函式
	adddate 增加一個日期
 	addtime	增加一個時間
  	detediff 計算兩個日期差值

流程控制

# 分支結構
declare i int default 0;
IF i = 1 THEN
	select 1;
ELSEIF i = 2 THEN
	select 2;
ELSE
	select 3;
END IF;
'''
注意:
	在python中if後面用冒號結尾 而在sql中使用than作為if語句的結尾
	在編寫流程控制的時候 記得加上end if
'''

# 迴圈結構
DECLARE num INT;
set num=0;
WHILE num < 10 DO
	select num;
    set num = num + 1;
END WHILE;
'''
注意:
	在python中where用冒號結尾 而在sql中使用的是do結尾
	寫完記得寫上end while
'''

索引相關概念

索引相當於是一本書的目錄 可以讓你更快速的查詢到你要的內容
讓獲取的資料更有目的性 從而提高資料庫檢索資料的效能

索引在MySQL中也叫做'鍵' 是儲存引擎用於快速查詢到記錄的一種資料結構
	* primary key
  	* unique key
    * index key
#1.上述的三個key都可以加快資料的查詢
#2.primary key和unique key除了可以加快查詢本身還自帶限制條件而index key很單一就是用來加快資料查詢
#3.外來鍵不屬於索引鍵的範疇 是用來建立關係的 與加快查詢無關

索引加快查詢的本質
	id int primary key auto_inctement,
    name vacher(32) unique,
    province varchar(32)
    age int
    phone bigint
    
    select name from userinfo where phone=12121212;
    #沒有利用索引 相當於是一頁一頁的查詢
    select name from userinfo where id = 9999;
    #利用索引 相當於是按照目錄確定頁數的查詢

索引可以加快資料查詢 但是會降低增刪的速度
通常情況下我們頻繁使用某些欄位查詢資料
	為了提升查詢的速度可以將該欄位建立索引
  
#聚焦索引(primary key)
	主鍵、主鍵索引
#輔助索引(unique,index)
	除主鍵意外的都是輔助索引
#覆蓋索引
	select name from user where name='jaosn';
'''已知name的情況下 還是用name去索引找值'''
#非覆蓋索引
	select age from user where name='jason';

索引資料結構

索引底層就是樹結構>>>:樹是計算機底層的資料結構
    
二叉樹
	二叉樹裡面還可以細分成很多領域 我們簡單的瞭解即可 
  	二叉意味著每個節點最大隻能分兩個子節點
B樹
	所有的節點都可以存放完整的資料
B+\*樹
	只有葉子節點才會存放真正的資料 其他節點只存放索引資料
 	B+葉子節點增加了指向其他葉子節點的指標
  	B*葉子節點和枝節點都有指向其他節點的指標

輔助索引在查詢資料的時候最會還是需要藉助於聚集索引
	輔助索引葉子節點存放的是資料的主鍵值

有時候就算採用索引欄位查詢資料 也可能不會走索引!!!
	最好能記三個左右的特殊情況

慢查詢優化

#我們怎麼去判斷自己寫的sql語句效率到底如何?
在sql語句前面加上 explain 可以檢視sql語句的等級
'''在sql語句中存在七個等級'''
1)index
2)range		
3)ref
4)eq_ref
5)const
6)system
7)null
	從上至下等級逐漸更高 我們在編寫sql語句時 至少做到等級在range及以上 儘量避免index等級的出現 
# index等級代表著全域性搜尋 生產中,mysql在使用全表掃描時的效能是極其差的,所以MySQL儘量避免出現全表掃描

全文檢索

MySQL的全文檢索功能MYISAM儲存引擎支援而InnoDB儲存引擎不支援
一般在建立表的時候啟用全文檢索功能
create table t1(
	id int primary key auto_increment,
  content text
	fulltext(content)
)engine=MyISAM;

# match括號內的值必須是fulltext括號中定義的(單個或者多個)
select content from t1 where match(content) against('jason')
'''上述語句可以用like實現但是查詢出來的結果順序不同 全文檢索會以文字匹配的良好程度排序資料再返回效果更佳'''

# 查詢擴充套件
select note_text from productnotes where Math(note_text) Against('jason' with query expansion);
"""
返回除jason外以及其他jason所在行相關文字內容行資料
eg:
	jason is handsome and cool,every one want to be cool,tony want to be more handsome;
	二三句雖然沒有jason關鍵字 但是含有jason所在行的cool和handsome
"""

# 布林文字搜尋
即使沒有定義fulltext也可以使用,但是這種方式非常緩慢效能低下
select note_text from productnotes where Match(note_text) Against('jason' in boolean mode);

# 注意事項
1.三個及三個以下字元的詞視為短詞,全文檢索直接忽略且從索引中排除
2.MySQL自身自帶一個非用詞列表,表內詞預設均被忽略(可以修改該列表)
3.出現頻率高於50%的詞自動作為非用詞忽略,該規則不適用於布林搜尋
4.針對待搜尋的文字內容不能少於三行,否則檢索不返回任何結果
5.單引號預設忽略