1. 程式人生 > 其它 >python操作MySQL,SQL注入的問題,SQL語句補充,檢視觸發器儲存過程,事務,流程控制,函式

python操作MySQL,SQL注入的問題,SQL語句補充,檢視觸發器儲存過程,事務,流程控制,函式

python操作MySQL

使用過程:
  引用API模組
  獲取與資料庫的連線
  執行sql語句與儲存過程
  關閉資料庫連線

  由於能操作MySQL的模組是第三方模組,我們需要pip安裝。
    pip3 install pymysql
import pymysql
1.連線資料庫
conn = pymysql.connect(
  host='localhost', #MySQL服務端的IP地址或者'127.0.0.1'
  port = 3306, # MySQL預設PORT地址(埠號)
  user = 'root', # 使用者名稱
  passwd = '123456', # 密碼  也可以簡寫 passwd
  database = 'test', # 庫名稱  也可以簡寫 db(db='test')
  charset = 'utf8' # 字元編碼 千萬不要加槓utf-8
)
'''
  要想操作資料庫,光連線資料是不夠的,必須拿到操作資料庫的遊標,才能進行後續的操作,比如
讀取資料、新增資料。通過獲取到的資料庫連線例項conn下的cursor()方法來建立遊標。遊標用來接收返回結果
'''
2.產生遊標物件
cursor=conn.cursor(
  cursor=pymysql.cursors.DictCursor# 括號內不寫引數 資料是元組要元組 不夠精確 新增引數則會將資料處理成字典
)  #產生遊標

3.SQL語句編寫
sql = 'select * from class'

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

5.獲取結果
res = cursor.fetchall()
print(res)

執行sql語句execute和executemany

execute(query,args=None)
  函式作用:執行單條的sql語句,執行成功後返回受影響的行數
  引數說明:
  query:要執行的sql語句,字串型別
  args:可選的序列或對映,用於query的引數值。如果args為序列,query中必須使用%s做佔位符;如果args為對映,query中必須使用%(key)s做佔位符

executemany(query,args=None)
  函式作用:批量執行sql語句,比如批量插入資料,執行成功後返回受影響的行數
  引數說明:
  query:要執行的sql語句,字串型別
  args:巢狀的序列或對映,用於query的引數值

建立資料庫案例

'''建立資料庫'''
import pymysql
#開啟資料庫連線,不需要指定資料庫,因為需要建立資料庫
conn = pymysql.connect('localhost',user = "root",passwd = "123456")
#獲取遊標
cursor=conn.cursor()
#建立pythonBD資料庫
cursor.execute('CREATE DATABASE IF NOT EXISTS pythonDB DEFAULT CHARSET utf8 COLLATE utf8_general_ci;')
cursor.close()#先關閉遊標
conn.close()#再關閉資料庫連線
print('建立pythonBD資料庫成功')

插入單條資料

'''插入單條資料'''
import pymysql
#開啟資料庫連線,不指定資料庫
conn=pymysql.connect('localhost','root','123456')
conn.select_db('pythondb')

cur=conn.cursor()#獲取遊標

#建立user表
cur.execute('drop table if exists user')
sql="""CREATE TABLE IF NOT EXISTS `user` (
	  `id` int(11) NOT NULL AUTO_INCREMENT,
	  `name` varchar(255) NOT NULL,
	  `age` int(11) NOT NULL,
	  PRIMARY KEY (`id`)
	) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=0"""

cur.execute(sql)

insert=cur.execute("insert into user values(1,'tom',18)")
print('新增語句受影響的行數:',insert)

#另一種插入資料的方式,通過字串傳入值
sql="insert into user values(%s,%s,%s)"
cur.execute(sql,(3,'kongsh',20))

cur.close()
conn.commit()
conn.close()
print('sql執行成功')

批量插入多條資料

'''插入多條資料'''
import pymysql
#開啟資料庫連線,不指定資料庫
conn=pymysql.connect('localhost','root','123456')
conn.select_db('pythondb')
#獲取遊標
cur=conn.cursor()

#另一種插入資料的方式,通過字串傳入值
sql="insert into user values(%s,%s,%s)"
insert=cur.executemany(sql,[(4,'wen',20),(5,'tom',10),(6,'test',30)])
print ('批量插入返回受影響的行數:',insert)
cur.close()
conn.commit()
conn.close()
print('sql執行成功')

注意:批量插入多條sql語句採用的是executemany(sql,args)函式,返回受影響的行
數。args引數是一個包含多個元組的列表,每個元組對應一條mysql中的一條資料。這裡的%s不需要
加引號,否則插入資料的資料會型別錯誤。

查詢資料

使用execute()函式得到的只是受影響的行數,並不能真正拿到查詢的內容。cursor物件還提供了3
種提取資料的方法:fetchone、fetchmany、fetchall.。每個方法都會導致遊標動,所以必須
注意遊標的位置。

cursor.fetchone():獲取遊標所在處的一行資料,返回元組,沒有返回None
cursor.fetchmany(size):接受size行返回結果行
cursor. fetchall():接收全部的返回結果行。

fetchone()

  從execute()函式的查詢結果中取資料,以元組的形式返回遊標所在處的一條資料,如果遊標
所在處沒有資料,將返回空元組,該資料執行一次,遊標向下移動一個位置。fetchone()函式必須
跟exceute()函式結合使用,並且在exceute()函式之後使用。

fetchmany()

  從exceute()函式結果中獲取遊標所在處的size條資料,並以元組的形式返回,元組的每一個元素
都也是一個由一行資料組成的元組,如果size大於有效的結果行數,將會返回cursor.arraysize條
資料,但如果遊標所在處沒有資料,將返回空元組。查詢幾條資料,遊標將會向下移動幾個位置。
fetmany()函式必須跟exceute()函式結合使用,並且在exceute()函式之後使用。

fetchall()

  獲取遊標所在處開始及以下所有的資料,並以元組的形式返回,元組的每一個元素都也是一個由一
行資料組成的元組,如果遊標所在處沒有資料,將返回空元組。執行完這個方法後,遊標將移動到數
據庫表的最後。

控制游標

移動游標,scroll(value, mode)方法
引數:
    當mode='relative'時,代表相對移動,預設值,value就是移動的長度,
    value>0向後移動(從位置0移動到位置2),value<0向前移動(比如從位置2移動到位置0)
    
    當mode='absolute'時,代表絕對移動,value就代表移動的絕對位置,
    value=0就代表移動到位置0處,就是結果集開頭,value=3就是移動到位置3處,也就是第4條記錄處。
  cursor.scroll(1, 'relative')  # 相對於當前位置往後移動一個單位
  cursor.scroll(1, 'absolute')  # 相對於起始位置往後移動一個單位

SQL注入

  SQL 注入是一種非常常見的資料庫攻擊手段,SQL 注入漏洞也是網路世界中最普遍的漏洞 之一。
  SQL 注入其實就是惡意使用者通過在表單中填寫包含 SQL 關鍵字的資料來使資料庫執行非常 規程式碼
的過程。簡單來說,就是資料「越俎代庖」(yuè zǔ dài páo)做了程式碼才能乾的 事情。這個問題
的來源是,SQL 資料庫的操作是通過 SQL 語句來執行的,而無論是執行代 碼還是資料項都必須寫在
 SQL 語句之中,這就導致如果我們在資料項中加入了某些 SQL 語 句關鍵字(比如說 SELECT、
DROP 等等),這些關鍵字就很可能在資料庫寫入或讀取資料 時得到執行。
在正常的情況下,在登入時,我們需要輸入正確的使用者名稱和密碼才能登入。
import pymysql
1.連線資料庫
conn = pymysql.connect(
  host='localhost', 
  port = 3306, 
  user = 'root', 
  passwd = '123456',
  database = 'test',
  charset = 'utf8'
)
cursor=conn.cursor(
  cursor=pymysql.cursors.DictCursor
) 
# 1.獲取使用者名稱和密碼
name = input('請輸入您的使用者名稱>>>:').strip()
password = input('請輸入您的密碼>>>:').strip()
# 2.拼接查詢語句
sql = "select * from userinfo where name=%s and password=%s;"
# 3.執行SQL語句
cursor.execute(sql, (name, password))
res = cursor.fetchall()
if res:
    print('登入成功')
else:
    print('使用者名稱或密碼錯誤')

# 寫正確的使用者名稱錯誤的密碼也可以登入
	使用者名稱:春遊去動物園' -- 1111
相當與select * from userinfo where name='春遊去動物園' --1111 and password=%s;在MySQL中--表示註釋,那麼密碼驗證部分就被註釋了。
        密碼:直接回車
# 使用者名稱和密碼都不需要也可以登入
	使用者名稱:xxx' or 1=1 -- 1111
相當與select * from userinfo where name='春遊去動物園' or 1=1 --1111 and password=%s;因為or運算且1=1恆成立,所以不需要使用者名稱和密碼直接登入。
        密碼:直接回車
"""上述現象就是典型的SQL注入問題"""
	上述情況利用的是MySQL註釋語法及邏輯運算子

# 解決SQL注入的問題其實也很簡單 就是想辦法過濾掉特殊符號
    execute方法自帶校驗SQL注入問題 自動處理特殊符號
  ps:設計到敏感資料的拼接 全部交給execute方法即可!!!
    sql = "select * from userinfo where name=%s and password=%s;"
    cursor.execute(sql, (name, password))

二次確認

"""
資料的增刪改查四個操作是有輕重之分的
    查                           不會影響真正的資料 重要程度最低
    增、改、刪                    都會影響真正的資料 重要程度較高
pymysql針對增、改、刪三個操作 都設定了二次確認 如果不確認則不會真正影響資料庫
"""
方式1:程式碼直接編寫
    affect_row = cursor.execute(sql)
    conn.commit()  # 手動二次確認
方式2:配置固定引數
    conn = pymysql.connect(
    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;

檢視

  檢視是一個虛擬表,是從資料庫中一個或多個表中匯出來的表,其內容由查詢定義。同真實表一
樣,檢視包含一系列帶有名稱的列和行資料。但是,資料庫中只存放了檢視的定義,而並沒有存放
檢視中的資料。這些資料存放在原來的表中。使用檢視查詢資料時,資料庫系統會從原來的表中取出
對應的資料。因此,檢視中的資料是依賴於原來的表中的資料的。一旦表中的資料發生改變,顯示在
檢視中的資料也會發生改變。

  檢視是儲存在資料庫中的查詢的SQL語句,它主要出於兩種原因:安全原因,檢視可以隱藏一些數
據,例如,員工資訊表,可以用檢視只顯示姓名、工齡、地址,而不顯示社會保險號和工資數等;另
一個原因是可使複雜的查詢易於理解和使用。

檢視的製作
	create view 檢視名 as sql語句

觸發器

觸發器是一種特殊型別的儲存過程,它不同於儲存過程,主要是通過事件觸發而被執行的,即不是主
動呼叫而執行的;而儲存過程則需要主動呼叫其名字執行

作用:
  1.可在寫入資料前,強制檢驗或者轉換資料(保證護資料安全)
  2.觸發器發生錯誤時,前面使用者已經執行成功的操作會被撤銷,類似事務的回滾

基本語法

delimiter 自定義結束符號
create trigger 觸發器名字 觸發時間 觸發事件 on 表 for each row
begin
    -- 觸發器內容主體,每行用分號結尾
end 自定義的結束符合
 
delimiter ;

觸發時間
  當 SQL 指令發生時,會令行中資料發生變化,而每張表中對應的行有兩種狀態:資料操作前和操作後
  before:表中資料發生改變前的狀態
  after:表中資料發生改變後的狀態
  PS:如果 before 觸發器失敗或者語句本身失敗,將不執行 after 觸發器(如果有的話)
 

觸發事件
  觸發器是針對資料傳送改變才會被觸發,對應的操作只有
  INSERT
  DELETE
  UPDATE

注意事項
  1.在 MySQL 5 中,觸發器名必須在每個表中唯一,但不是在每個資料庫中唯一,即同一資料庫中
的兩個表可能具有相同名字的觸發器
  2.每個表的每個事件每次只允許一個觸發器,因此,每個表最多支援 6 個觸發器,before/after insert、before/after delete、before/after update

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

案例

	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');

事務

定義

  事務即 (transaction) ,是資料庫系統區別於檔案系統的重要特性之一。在檔案系統中,如果
我們正在寫檔案,但是作業系統崩潰了,那麼檔案中的資料可能會丟失。但是資料庫可以通過事務機
制來確保這一點。

使用事務的目的

  事務會把資料庫從一中狀態轉換為另一種狀態。在資料庫提交工作時,可以確保要麼所有工作都已
經儲存了,要麼所有修改都不儲存。

事務的四大特性

原子性(atomicity)
  所謂原子性,是指整個資料庫的每個事務都是不可分割的單位。只有事務中的所有 SQL 語句都執行成功,才算整個事務成功,事務才會被提交。如果事務中任何一個 SQL 語句執行失敗,整個事務都應該被回滾。
  場景:如在銀行取款機取款,要麼取款成功、要麼取款失敗。不能存在卡里錢扣了,取款機並沒有出金額;或者錢取到了,但是卡里並沒有減去該金額。

一致性(consistency)
  所謂一致性,是指將資料庫從一種一致性狀態轉換為下一種一致性狀態。不允許資料庫中的資料出現新老資料都有的情況,要麼都是老資料,要麼都是新資料。用更書面化的表達就是:資料的完整性約束沒有被破壞。
  場景:如在一個使用者表中,存在一個身份證號的欄位,且身份證號滿足唯一約束條件;如果一個事務對身份證號進行了修改,在事務進行提交或回滾後,身份號資訊變的不具有唯一性了,這就破壞了事務的一致性。

隔離性(isolation)
  所謂隔離性,是指一個事務的影響在該事務提交前對其他事務都不可見,它通過鎖機制來實現。
  場景:多個並行交叉的事務間的操作可以相互分離,即多個事務對於其他事務不可見。

永續性(durability)
  所謂永續性,是指事務一旦被提交,其結果就是永久性的。即使發生宕機等故障,資料庫也能將資料恢復。
  場景:事務提交後,所有變化都是永久的,即使資料庫崩潰而需要恢復時也能保證恢復後提交的數不會丟失。

事務開啟的標誌?事務結束的標誌?

以第一個DML(insert、update、delete) 語句的執行作為開始,以下面的其中之一作為結束:

  COMMIT 或ROLLBACK 語句
  DDL 或DCL 語句(自動提交)
  使用者會話正常結束
  系統異常終了
關於DDL和DCL參考下圖:

案例

#事務的建立
/*
隱式的事務:事務沒有明顯的開啟和結束的標記
比如insert、update、delete語句

delete from 表 where id=1;
顯示事務:事物具有明顯的開啟和結束的標記
前提:必須先設定自動提交功能為禁用
set autocommit=0;
步驟1:開啟事務
set autocommit=0;
start transaction;可選的
步驟2:編寫事務中的sql語句(select、insert、update、delete)
語句1;
語句2;
......
步驟3:結束事務
commit;提交事務
rollback;回滾事務

開啟事務的語句
update 表 set 張三丰的餘額=500 where name='張三丰';
update 表 set 郭襄的餘額=1500 where name='郭襄';
結束事務的語句;


*/
SHOW VARIABLES LIKE 'autocommit';
DROP TABLE IF EXISTS  account;
CREATE TABLE account(
        id INT PRIMARY KEY AUTO_INCREMENT,
        username VARCHAR(20),
        balance DOUBLE

);
INSERT  INTO  account(username,balance)
VALUES('張無忌',1000),('趙敏',1000);
#演示事務的使用步驟

#開啟事務
SET autocommit=0;
START TRANSACTION;
#編寫一組事務的語句
UPDATE account SET balance=500 WHERE username='張無忌';
UPDATE account SET balance=1500 WHERE username='趙敏';
#結束事務
COMMIT;
SELECT * FROM account;

#試一下回滾的

#開啟事務
SET autocommit=0;
START TRANSACTION;
#編寫一組事務的語句
UPDATE account SET balance=1000 WHERE username='張無忌';
UPDATE account SET balance=1000 WHERE username='趙敏';
#結束事務
ROLLBACK;
#在沒有明確的結束標誌,只是滯留在記憶體了。

儲存過程

# 相當於定義函式
1. 結束符
DELIMITER $$  #應用於shell指令行

2. 宣告儲存過程
CREATE PROCEDURE 名稱(入參或回參)

3. 開始與結束符
BEGIN
...
END

4. 變數賦值
SET @變數名 = 1

5. 定義變數
DECLARE 變數名 int unsigned default 100;

6. 儲存過程體
create function 儲存函式名(引數)

7. 呼叫儲存過程
call 儲存過程名(入參)

8. 刪除儲存過程
drop procedure 名稱

9. 檢視某庫中儲存過程
show procedure status where db = '資料庫名'

10. 檢視特定儲存過程
show create procedure 資料庫名.儲存過程名

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

引數

儲存過程的引數用在儲存過程定義時,共有三種引數型別。
  IN:表示呼叫者需要對儲存過程傳入引數。
  OUT:表示呼叫者會得到一個或多個返回值。
  INOUT:表示呼叫者既要傳入值,又要傳出值。

in輸入引數

create PROCEDURE test(in data_in int)
begin
     SELECT data_in;
     set data_in = 5;
     select data_in;
end

set @data = 1

call test(@data)  #首先查出data_in = 1 修改之後 data_in = 5

select @data  #data = 1,因為儲存過程中修改的是區域性變數,不影響全域性

out回傳引數

create PROCEDURE test(out data_out int)
begin
     set data_out = 5;
end

set @data = 1

call test(@data)

select @data  #data被修改,因為是回傳引數。

inout輸出輸出引數

create PROCEDURE test(inout data_inout int)
begin
     select data_inout;   #data_inout = 1
     set data_inout = 5;
end

set @data = 1

call test(@data) 

select @data  #data = 5

變數

1. 定義變數
  declare 名稱 型別 預設值(可選)

  declare data int default 50
  #或
  declare data int
  set data = 50

2. 變數賦值
  set 變數名 = 

3. 使用使用者變數(全域性變數)
  create PROCEDURE test() 
  begin
       select @data;
       set @data = 5;
       select @data;
  end

  set @data = 1

  call test()

流程控制

IF

  if 條件 then
        子程式碼
  elseif 條件 then
        子程式碼
  else
        子程式碼
  end if;

while迴圈

  DECLARE num INT ;
  SET num = 0 ;
  WHILE num < 10 DO
    SELECT num ;
    SET num = num + 1 ;
  END WHILE ;

索引

什麼是索引

    官方介紹索引是幫助MySQL高效獲取資料的資料結構。更通俗的說,資料庫索引好比是一本書前
面的目錄,能加快資料庫的查詢速度。

    一般來說索引本身也很大,不可能全部儲存在記憶體中,因此索引往往是儲存在磁碟上的檔案中的
(可能儲存在單獨的索引檔案中,也可能和資料一起儲存在資料檔案中)。

    我們通常所說的索引,包括聚集索引、覆蓋索引、組合索引、字首索引、唯一索引等,沒有特別
說明,預設都是使用B+樹結構組織(多路搜尋樹,並不一定是二叉的)的索引。

索引的優勢和劣勢

優勢:
    可以提高資料檢索的效率,降低資料庫的IO成本,類似於書的目錄。

    通過索引列對資料進行排序,降低資料排序的成本,降低了CPU的消耗。

      被索引的列會自動進行排序,包括【單列索引】和【組合索引】,只是組合索引的排序要複雜一些。
      如果按照索引列的順序進行排序,對應order by語句來說,效率就會提高很多。


劣勢:

  索引會佔據磁碟空間

  索引雖然會提高查詢效率,但是會降低更新表的效率。比如每次對錶進行增刪改操作,MySQL不僅
要儲存資料,還有儲存或者更新對應的索引檔案。
索引在MySQL中也叫做“鍵”,是儲存引擎用於快速找到記錄的一種資料結構
  primary       key 	主鍵
  unique	key	唯一鍵
  index 	key     索引鍵
'''索引雖然好用 但是不能無限制的建立!!!'''
**索引的影響:**
	* 在表中有大量資料的前提下,建立索引速度會很慢
	* 在索引建立完畢後,對錶的查詢效能會大幅度提升,但是寫的效能會降低

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

只有葉子結點存放真實資料,根和樹枝節點存的僅僅是虛擬資料
查詢次數由樹的層級決定,層級越低次數越少
一個磁碟塊兒的大小是一定的,那也就意味著能存的資料量是一定的。如何保證樹的層級最低呢?一個磁碟塊兒存放佔用空間比較小的資料項
思考我們應該給我們一張表裡面的什麼欄位欄位建立索引能夠降低樹的層級高度>>> 主鍵id欄位

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

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

覆蓋索引:只在輔助索引的葉子節點中就已經找到了所有我們想要的資料
	select name from user where name='春遊去動物園';
非覆蓋索引:雖然查詢的時候命中了索引欄位name,但是要查的是age欄位,所以還需要利用主鍵才去查詢
	select age from user where name='春遊去動物園';
"""