1. 程式人生 > 其它 >SQL注入問題 觸發器 檢視 事物 函式 流程 儲存過程 索引

SQL注入問題 觸發器 檢視 事物 函式 流程 儲存過程 索引

python 中SQL注入問題

在python 用pymysql基於資料庫 來操作使用者登入的時候會遇到一些問題。
問題1:只填寫對使用者名稱就可以登入成功
問題2:填寫不對的使用者名稱和密碼也能登陸成功


SQL注入問題所在:
 python中
   sql ="insert into from userinfo(name,pwd) values( '%s','%s')"%(username,password)
	在登入的時候可能在使用者名稱後面添加了一些特殊符號 然後產生特殊含義 從而出現這種問題。
 select * from userinfo where name='jason' -- kasdjksajd' and pwd=''
  "使用者後面被添加了 -- 註釋符號"
select * from userinfo where name='xyz' or 1=1 -- aksdjasldj' and pwd=''
  "因為or運算且1=1恆成立,所以不需要使用者名稱和密碼直接登入。"

針對上述的SQL注入問題 核心在於手動拼接了關鍵資料 交給execute處理就能解決問題
註冊:
sql = "insert into from userinfo(name,pwd) values( %s,%s)"
cursor.execute(sql, (username, password))

登入:
sql = " select * from userinfo where name=%s and pwd=%s "
cursor.execute(sql, (username, password))


executemany 批量插入資料
"""
execute方法補充(瞭解)
	批量插入資料
sql = 'insert into userinfo(name,password) values(%s,%s)'
cursor.executemany(sql,[('tom',123),('lavin',321),('pony',333)])
"""

檢視

檢視通過查詢得到一張 臨時的表,通過方法儲存下來。下次直接使用
關鍵字 
create view
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

觸發器的命名有規律:
tri_before_insert_t1  增前
tri_after_delete_t2   刪後
tri_after_update_t2   改後

檢視當前庫下所有的觸發器資訊
    	show triggers\G;
刪除當前庫下指定的觸發器資訊
    	drop trigger 觸發器名稱;

案觸發器例

  "因觸發器中可能會用到分號; 所以需要把語句結尾符切換成別的符號"
delimiter 設定的結束符號

 1.先建立兩張表
  	# 案例
    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
    );
  2.需求:cmd表插入資料的success如果值為no 則去errlog表中插入一條記錄
    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 ;  # 結束之後記得再改回來,不然後面結束符就都是$$了
  3.僅僅往cmd表中插入資料
  	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');    
NOW()  # 獲取當前時間

事物

事物的四大特性(ACID)
A:原子性
    事務中的各項操作是不可分割的整體,要麼同時成功要麼同時失敗
C:一致性
	使資料庫從一個一致性狀態變道另一個一致性狀態
I:隔離性
    多個事物之間彼此不干擾
D:永續性
   	也稱為永久性,指一個事物一旦提交,它對資料庫中資料的改變應該是永久性的

其實事物就是我們在做一些事情中的操作。
  eg:A欠了B一筆錢
      還錢時,A拿著交行的銀行卡去招商銀行的ATM給B的建設銀行轉錢
        1、朝交行的伺服器傳送請求,修改A的賬戶
        2、朝建行的伺服器傳送請求,修改B的賬戶 

事物案例

create table user(
id int primary key auto_increment,
name char(32),
balance int
);

insert into user(name,balance)
values
('jason',1000),
('kevin',1000),
('tank',1000);

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

# 修改操作
update user set balance=900 where name='jason'; #買支付100元
update user set balance=1010 where name='kevin'; #中介拿走10元
update user set balance=1090 where name='tank'; #賣家拿到90元

# 回滾到上一個狀態
rollback;

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

"""事務回滾(返回執行事務操作之前的資料庫狀態)
    	rollback;  # 執行完回滾之後 事務自動結束
    事務確認(執行完事務的主動操作之後 確認無誤之後 需要執行確認命令)
    	commit;  # 執行完確認提交之後 無法回滾 事務自動結束
        """

事務相關關鍵字
	start transaction;   # 開啟事物操作
	rollback			# 事物回滾
	commit           # 確認提交
	savepoint         # 儲存事物節點  類似於遊戲存檔
   建立節點可以使用savepoint
    	savepoint sp01;
    回退到節點地址
    	rollback to sp01;    

事物的知識點 (隔離級別與 MVCC)

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

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

例如
剛插入第一條資料的時候,我們預設事務id為1,實際是這樣儲存的
    username		create_version		delete_version
    jason						1					
可以看到,我們在content列插入了kobe這條資料,在create_version這列儲存了1,1是這次插入操作的事務id。
然後我們將jason修改為jason01,實際儲存是這樣的
    username		create_version		delete_version
    jason						1									2
    jason01					2
可以看到,update的時候,會先將之前的資料delete_version標記為當前新的事務id,也就是2,然後將新資料寫入,將新資料的create_version標記為新的事務id
當我們刪除資料的時候,實際儲存是這樣的
		username		create_version		delete_version
    jason01					2									 3
"""
由此當我們查詢一條記錄的時候,只有滿足以下兩個條件的記錄才會被顯示出來:
   1.當前事務id要大於或者等於當前行的create_version值,這表示在事務開始前這行資料已經存在了。
   2.當前事務id要小於delete_version值,這表示在事務開始之後這行記錄才被刪除。
"""

函式

# mysql內建函式只能在sql語句中使用
'''我們可以通過help 函式名 的方式檢視幫組資訊'''
# 移除指定字元
Trim 、LTrim(左移除) 、 RTrim(右移除)
# 大小寫轉換
Lower、Upper

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

# 返回讀音相似值(對英文效果)
Soundex
"""
eg:客戶表中有一個顧客登記的使用者名稱為J.Lee
		但如果這是輸入錯誤真名其實叫J.Lie,可以使用soundex匹配發音類似的
		where Soundex(name)=Soundex('J.Lie')
"""

# 日期格式:date_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');

format(sub_time,'%Y-%m') # 切取時間 保留年月展示

1.where Date(sub_time) = '2015-03-01'       # date 年月日 篩選條件
    2.where Year(sub_time)=2016 AND Month(sub_time)=07;  # Year 年  month月 篩選條件
# 更多日期處理相關函式 
	adddate	    增加一個日期 
	addtime	    增加一個時間
	datediff	 計算兩個日期差值
  ...

儲存過程

# 有點類似於Python中的自定義函式

delimiter $$
create procedure p1()
begin
	select * from cmd;
end $$
delimiter ;

# 呼叫函式
call p1()


 有參函式:
 delimiter $$
   create procedure p2(
    in m int,  # in表示這個引數必須只能是傳入不能被返回出去
    in n int,  
    out res int  # out表示這個引數可以被返回出去,還有一個inout表示即可以傳入也可以被返回出去
  )
  begin
      select tname from userinfo where id > m and id < n;
      set res=0;  # 用來標誌儲存過程是否執行
  end $$
  delimiter ;
# 針對res需要先提前定義
  set @res=10;  定義  # res的值是用來判斷儲存過程是否被執行成功的依據,所以需要先定義一個變數@res儲存10
  select @res;  檢視  
  call p2(1,5,@res)  呼叫
  select @res  檢視  # # 執行成功,@res變數值發生了變化

檢視儲存過程具體資訊
	show create procedure pro1;
檢視所有儲存過程
	show procedure status;
刪除儲存過程
	drop procedure pro1;
    
# 2、在python程式中呼叫
pymysql連結mysql
產生的遊表cursor.callproc('p1',(2,4,10))  # 內部原理:@_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute('select @_p1_2;')    

流程控制

在mysql中也有 流程控制,其邏輯與python中相差無幾
# 分支結構
declare i int default 0;
IF i = 1 THEN
	SELECT 1;
ELSEIF i = 2 THEN
	SELECT 2;
ELSE
	SELECT 7;
END IF;

# 迴圈結構
DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
	SELECT num ;
	SET num = num + 1 ;
END WHILE ;

索引相關概念

# 索引就是一種資料結構
	類似於書的目錄。意味著以後再查資料應該先找目錄再找資料,而不是用翻頁的方式查詢資料
  
索引在MySQL中也叫做“鍵”,是儲存引擎用於快速找到記錄的一種資料結構
	primary key 	主鍵
  unique	key		唯一鍵
  index 	key   索引鍵
上面三種key前兩種除了有加速查詢的效果之外還有額外的約束條件(primary key:非空且唯一,unique key:唯一),而index key沒有任何約束功能只會幫你加速查詢
# ps:foreign key 是用來建立外來鍵關係的 
索引加快查詢的本質
	id int primary key auto_increment,
 	name varchar(32) unique,
  	province varchar(32)
 	age int
 	phone bigint
 	
	select name from userinfo where phone=18818888888;  # 一頁頁的翻
	select name from userinfo where id=99999;  # 按照目錄確定頁數找

# 索引的注意事項
	基於id查詢資料很快 但是基於別的非主鍵欄位查詢資料就很慢 
  	解決的措施可以是給非主鍵欄位新增索引  推薦使用 index key
'''索引雖然好用 但是不能無限制的建立!!!'''
**索引的影響:**
	* 在表中有大量資料的前提下,建立索引速度會很慢
	* 在索引建立完畢後,對錶的查詢效能會大幅度提升,但是寫的效能會降低

聚集索引(primary key)
	主鍵、主鍵索引
輔助索引(unique,index)
	除主鍵以外的都是輔助索引
覆蓋索引   
	select name from user where name='jason'; 
非覆蓋索引
	select age from user where name='jason';    

索引資料結構

索引底層其實是樹結構。
樹 是計算機的底層的資料結構

樹有很多種型別
	二叉樹,b樹,b+樹,b*樹
樹的關鍵字:
	根節點,枝節點,葉子節點 


二叉樹
    每個節點最多隻能分兩個子節點。

b樹
    所有節點都可以存放完整的資料

b+樹與b*樹
    根節點與枝節點只存放索引
    葉子節點用來存放完整的資料
    b+樹在葉節點添加了指向其他葉子節點的指標
    b*樹在葉節點與枝節點都添加了指向其他葉節點與枝節點的指標
    
通過學習這些 資料結構 得知:
輔助索引在查詢資料的時候 因 輔助索引的葉子節點存放的是資料的主鍵 最後還是要利用主鍵做聚集索引
"""
有時候就算採用索引欄位查詢資料 也可能不會走索引!!!
函式運算  count
模糊查詢  like "%_" 
成員運算子 not in 
數學運算
"""

慢查詢優化

explain  查詢表的 索引掃描型別 關鍵字

1. index		儘量避免
2. range	  # 我們能達到 range 就可以了	
3. ref
4. eq_ref
5. const
6. system
7. null

新增索引方法

建立index索引:
    alter table 表名 add index index_name(欄位名);  # 注意 新增索引的欄位型別長度不能過長
    create index indexname on 表名(欄位名);  
查詢索引:
	desc 表名;
	show index from 表名;
刪除index索引:
	alter table 表名 drop key index_name;  # 注意兩種建立辦法 一個是index_name 另一個是indexname
新增唯一性主鍵;
	alter table 表名 add unique key uni_鍵名可以自己起(欄位名);