1. 程式人生 > 其它 >MySQL(檢視、觸發器、事務、儲存過程、索引)

MySQL(檢視、觸發器、事務、儲存過程、索引)

MySQL(檢視、觸發器、事務、儲存過程、索引)

python操作MySQL

# python中支援操作MySQL的模組很多 其中最常見的當屬'pymysql'
為了使python連線上資料庫,你需要一個驅動,這個驅動是用於與資料庫互動的庫

PyMySQL : 這是一個使Python連線到MySQL的庫,它是一個純Python庫(資料夾)

PyMySQL 是一個純 Python 實現的 MySQL 客戶端操作庫,支援事務、存取過程、批量執行,實現增刪改查等

# 下載方式
1.命令列輸入(下載模組 pymysql)
pip3 install pymysql 

2.切換下載的源(倉庫)
pip3 install pymysql -i 源地址

pymysql的基本使用

import pymysql

# 連結服務端
conn_obj = pymysql.connect(  # 賦值給 conn連線物件
    host='127.0.0.1',  # MySQL服務端的IP地址
    port=3306,  # MySQL預設PORT地址(埠號)
    user='root',  # 使用者名稱
    password='123',  # 密碼  也可以簡寫 passwd
    database='db_01',  # 連線資料庫名稱 也可以簡寫 db
    charset='utf8'  # 字元編碼 不能寫utf-8
)

# 生成一個獲取命令的遊標物件(相當於cmd開啟mysql中的 mysql>)
cursor = conn.cursor(
    cursor=pymysql.cursors.DictCursor
)  # 括號內不寫引數 資料是元組要元組 不夠精確 新增引數則會將資料處理成字典

# 定義SQL語句
sql = 'select * from teacher'

# 執行SQL語句
 affect_rows = cursor.execute(sql)
 print(affect_rows)  # 執行SQL語句之後受影響的行數

# 獲取返回結果
res = cursor.fetchall()
print(res)

excute返回值

1.execute返回值是執行SQL語句之後受影響的行數

2.fetchall()獲取所有的結果
fetchone()獲取結果集第一個結果
fetchmany()括號內可以指定獲取幾個結果集

# 獲取返回結果
# res = cursor.fetchall()  # 列表套字典
# res = cursor.fetchone()  # 資料字典
# res = cursor.fetchmany(3)  # 列表套字典

cursor.scroll(1, 'relative')  # 相對於當前位置往後移動一個單位
cursor.scroll(1, 'absolute')  # 相對於起始位置往後移動一個單位

SQL注入問題

基本登入程式碼

import pymysql

# 建立連結
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',
    database='db_01',
    charset='utf8'
)

# 生成一個遊標物件
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)  # 讓資料自動組織成字典

# 獲取使用者名稱和密碼
username = input('username>>>:').strip()
password = input('password>>>:').strip()

# 構造SQL語句
sql = "select * from egg where name='%s' and password='%s'"%(username,password)
print(sql)
# 執行sql語句
cursor.execute(sql)
# 獲取所有返回結果
res = cursor.fetchall()
# if判斷
if res:
    print(res)
    print('登入成功')
else:
    print('使用者名稱或密碼錯誤')

注入問題發現

# 寫正確的使用者名稱錯誤的密碼也可以登入
	使用者名稱:jason' -- jhahsdjasdjasd
  密碼:直接回車
# 使用者名稱和密碼都不需要也可以登入
	使用者名稱:xxx' or 1=1 -- asdjasjdkajsd
  密碼:直接回車
"""上述現象就是典型的SQL注入問題"""
	上述情況利用的是MySQL註釋語法及邏輯運算子
	就是通過一線特殊符號的組合 達到某些特定的效果從而避免常規的邏輯

解決注入問題

#execute方法
自動將 使用者名稱和密碼放在對應的%s內,並且放之前會自動對使用者名稱和密碼做特殊符號的校驗,確保安全性
'''execute方法自帶校驗SQL注入問題 自動處理特殊符號
設計到敏感資料的拼接 全部交給execute方法即可'''
# 程式碼修改:
sql = "select * from userinfo where name=%s and password=%s;"
    
cursor.execute(sql, (name, password))

# execute批量插入資料
sql = 'insert into userinfo(name,password) values(%s,%s)'

cursor.executemany(sql,[('tom',123),('lavin',321), ('pony',333)])

commit()二次確認

#資料的增刪改查四個操作
查                           不會影響真正的資料 重要程度最低
增、改、刪                    都會影響真正的資料 重要程度較高
'''pymysql針對增、改、刪三個操作 都設定了二次確認 如果不確認則不會真正影響資料庫'''

# 方式1:程式碼直接編寫
	affect_row = cursor.execute(sql)
	conn_obj.commit()  # 手動二次確認
    
# 方式2:配置固定引數
conn_obj = pymysql.connect(
      host='127.0.0.1',  
      port=3306,  
      user='root',  
      password='jason123', 
      database='jp04_3',  
      charset='utf8',
      autocommit=True  # 自動二次確認
  )  

修改表SQL語句

# 1.修改表的名字  rename
	alter table t1 rename ttt;
    
# 2.新增欄位		 add
	alter table ttt add pwd int;  '''預設是尾部追加欄位'''
	alter table ttt add tid int after name;  '''指定追加位置'''
  alter table ttt add nid int first;  '''指定頭部新增欄位'''

# 3.修改欄位	change(名字型別都可)/modify(只能改型別不能改名字)
	alter table ttt change pwd password tinyint;
    
# 4.刪除欄位			 drop
	alter table ttt drop nid;

檢視

# 檢視的概念
檢視是虛擬的表


# 檢視的建立
	create view 檢視名 as sql語句
    
'''檢視雖然看似很好用 但是會造成表的混亂 畢竟檢視不是真正的資料來源
檢視只能用於資料的查詢 不能做增、刪、改的操作 可能會影響原始資料(視圖裡面的資料是直接來源於原始表 而不是拷貝一份)   '''

觸發器

# 觸發器概念
	在對錶資料進行增、刪、改的具體操作下,自動觸發的功能
    
# 觸發器作用
	專門針對表資料的操作 定製個性化配套功能
    
# 觸發器種類
	表資料新增之前、新增之後
	表資料修改之前、修改之後
	表資料刪除之前、刪除之後  
    
# 觸發器建立
	create trigger 觸發器名字 before/after insert/update/delete
	on 表名 for each row
		begin
			SQL語句
		end
# 觸發器的命名規範
    tri_after_insert_t1
    tri_before_update_t2
    tri_before_delete_t3
    '''更直觀的看出觸發器的作用'''
    
# 檢視當前庫下所有的觸發器資訊
    	show triggers\G;
    
# 刪除當前庫下指定的觸發器資訊
    	drop trigger 觸發器名稱;

觸發器具體案例

建立表

'''
臨時修改SQL語句的結束符
	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表插入資料的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');

事務

# 事務的概念
資料庫事務(Database Transaction),是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。

簡單的說:事務就是將一堆的SQL語句(通常是增刪改操作)繫結在一起執行,要麼都執行成功,要麼都執行失敗,即都執行成功才算成功,否則就會恢復到這堆SQL語句執行之前的狀態。

事務的四個特性ACID

# A:原子性(Atomicity)
一個事務是一個不可分割的整體 裡面的操作要麼都成立要麼都不成立

# C:一致性(Consistency)
事務必須使資料庫從一個一致性狀態變到另外一個一致性狀態

# I:隔離性(Isolation,又稱獨立性)
併發程式設計中 多個事務之間是相互隔離的 不會彼此干擾

# D:永續性(Durability)
事務一旦提交 產生的結果應該是永久的 不可逆的

隔離級別

# 讀未提交(Read uncommitted) 
安全性最差,可能發生併發資料問題,效能最好

# 讀提交(read committed) 
Oracle預設的隔離級別

# 可重複讀(repeatable read)
MySQL預設的隔離級別,安全性較好,效能一般

# 序列化(Serializable) 
表級鎖,讀寫都加鎖,效率低下,安全性高,不能併發

事務具體案例

建立表錄入資料

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;
    
# 編寫SQL語句(同屬於一個事務)
update user set balance=900 where name='jason';
update user set balance=1010 where name='kevin'; 
update user set balance=1090 where name='tank';

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

開啟事務:start transaction;
需要執行的SQL語句集
結束事務:commit(提交事務)或rollback(回滾事務)
'''

儲存過程

# 儲存過程的概念
本質上就是將一段SQL程式碼封裝起來,用於完成特定的操作,在使用時只需通過儲存過程的名稱呼叫即可(如果儲存過程需要引數的話還需要傳遞對應的引數)

# 類似於有參函式
  delimiter $$
  create procedure p1(
      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;  定義
  select @res;  檢視
  call p1(1,5,@res)  呼叫
  select @res  檢視

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

無參無返回值的儲存過程

# 相當於定義函式
delimiter $$
create procedure p1()
begin
	select * from cmd;
end $$
delimiter ;

# 相當於呼叫函式
call p1()

帶引數的儲存過程

# 類似於有參函式
  delimiter $$
  create procedure p1(
      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;  定義
  select @res;  檢視
  call p1(1,5,@res)  呼叫
    
    
'''
檢視儲存過程具體資訊
	show create procedure pro1;
檢視所有儲存過程
	show procedure status;
刪除儲存過程
	drop procedure pro1;
'''

函式

'''
mysql內建的函式只能在sql語句中使用
可以通過help 函式名   檢視幫助資訊
'''
# 1.移除指定字元
Trim、LTrim、RTrim

# 2.大小寫轉換
Lower、Upper

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

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

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

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

流程控制

# python if判斷
	if 條件:
    子程式碼
  elif 條件:
    子程式碼
  else:
    子程式碼
# js if判斷
	if(條件){
    子程式碼
  }else if(條件){
    子程式碼
  }else{
    子程式碼
  }
# MySQL if判斷
	if 條件 then
        子程式碼
  elseif 條件 then
        子程式碼
  else
        子程式碼
  end if;
  
# MySQL while迴圈
	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沒有任何約束功能只會幫你加速查詢

'foreign key不是用來加速查詢用的,不在我們研究範圍之內'

# 索引的基本用法
id	name	pwd		post_comment  addr  age 
	基於id查詢資料很快 但是基於addr查詢資料就很慢 
  	解決的措施可以是給addr新增索引
'''索引雖然好用 但是不能無限制的建立!!!'''

索引的底層資料結構是b+樹
	b樹 紅黑樹 二叉樹 b*樹 b+樹
  	上述結構都是為了更好的基於樹查詢到相應的資料

聚焦索引

聚集索引是將主鍵與行記錄儲存在一起,當根據主鍵進行查詢時,可直接在表中獲取到資料,不用回表查詢。InnonDB的所有的表都是索引組織表,主鍵與資料存放在一起。InnoDB選擇聚集索引遵循以下原則:

在建立表時,如果指定了主鍵,則將其作為聚集索引。
如果沒有指定主鍵,則選擇第一個NOT NULL(非空)的唯一索引作為聚集索引。
如果沒有唯一索引,則內部會產生一個6位元組的rowID(主鍵值)作為主鍵 

輔助索引

# 輔助索引
查詢資料的時候不可能都是用id作為篩選條件,也可能會用name,password等欄位資訊,那麼這個時候就無法利用到聚集索引的加速查詢效果。就需要給其他欄位建立索引

優勢:顯示指定的主鍵可以是普通的int型別,這樣儲存空間就是4位元組,在二級索引的葉子結點中儲存主鍵的所佔用空間就會變小

'''
葉子結點存放的是輔助索引欄位對應的那條記錄的主鍵的值(比如:按照name欄位建立索引,那麼葉子節點存放的是:{name對應的值:name所在的那條記錄的主鍵值})
資料查詢 如果一開始使用的是輔助索引 那麼還需要使用聚焦索引才可以獲取到真實資料
'''

覆蓋索引

#覆蓋索引:
只在輔助索引的葉子節點中就已經找到了所有我們想要的資料
select name from user where name='jason';

#非覆蓋索引:
雖然查詢的時候命中了索引欄位name,但是要查的是age欄位,所以還需要利用主鍵才去查詢
select age from user where name='jason';