pymysql ,主鍵, 索引


一、pymysql模塊的使用

1. 安裝pymysql

  • pymysql屬於第三方庫,要先下載安裝。

2. 連接MySQL

  • 連接並操作MySQL的步驟
    1. 創建連接

    2. 創建游標

    3. 創建SQL語句

    4. 執行SQL語句

    5. 接收查詢結果(當對數據進行查詢時,才會有此步驟)fetchall()/fetchone()/fetchmany(size)

      查詢結果是列表套字典的格式。若返回的是空,根據創建游標時,讓返回的數據是列表套字典形式還是默認的元組形式。對應為[]或()

    6. 提交(當對數據進行增、刪、修改時,才會有此步驟)即 commit()

    7. 關閉游標

    8. 關閉連接

  • 查詢實例:
import pymysql
conn = pymysql.connect(host='localhost',user='root',password='123qwe',database='test',charset='utf8')
# cursor = conn.cursor() ### 默認返回的值是元組類型*************
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) ### 返回的值是字典類型 (*********)

sql = "select * from userinfo"
cursor.execute(sql)

# res = cursor.fetchall()  ###取出所有的數據 返回的是列表套字典
# res = cursor.fetchone()  ###取出一條數據 返回的是字典類型
res = cursor.fetchmany(12) ### 指定獲取多少條數據 返回的是列表套字典,若超過最大數據行數,則只會返回全部的數據。

cursor.close()
conn.close()

  • 插入實例:
  • print(cursor.lastrowid) ### 獲取最后一行的ID值在插入時使用才有效果,在其他sql語句中,返回的是None
import pymysql

### 連接數據庫的參數
conn = pymysql.connect(host='localhost',user='root',password='123qwe',database='test',charset='utf8')
# cursor = conn.cursor() ### 默認返回的值是元組類型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) ### 返回的值是字典類型 (*********)


sql = "insert into user (name, password) values (%s,  %s)"

cursor.execute(sql, ('dshadhsa', 'dbsjabdjsa'))  ### 新增一條數據

print(cursor.lastrowid)   ### 獲取最后一行的ID值

# 一次性插入多條數據***************
# data 必須是列表套元組
# data = [
#     ('zekai1', 'qwe'),
#     ('zekai2', 'qwe1'),
#     ('zekai3', 'qwe2'),
#     ('zekai4', 'qwe3'),
# ]
# cursor.executemany(sql, data)  ### 新增多條數據

#### 加如下代碼
conn.commit()

cursor.close()
conn.close()

3. sql注入問題

  • 產生原因:因為過於相信用戶輸入的內容,對輸入的內容根本沒有任何的檢驗,導致內容中一些關鍵字符傳入,在執行含有這些關鍵字符的SQL語句時,返回了不正確的結果。

  • 實例:

    user_name = zekai ' or 1=1 #
    pwd = dsadsa
    
    # 我們即將提交的SQL語句
    sql = 'select * from user where name = %s and password = %s' % (user_name ,pwd)
    
    # 真正提交執行的SQL語句  '#'表示注釋的意思
    select * from user where name='zekai ' or 1=1 #' and password='dsadsa'
    
    # 查詢后返回的結果
    # 由於判斷密碼的條件被#注釋掉了,所以只會匹配前面的,而前面又有 關鍵字 or
    [{'id': 1, 'name': 'zhangsan', 'password': ''},{'id': 2, 'name': 'zekai', 'password': '123'}]
    
  • 解決的方法:注意下面的星號中間的內容

  1. 對用戶輸入的內容進行檢驗之后,再寫入SQL語句

  2. 利用pymysql的內置方法排查。

    以下是第二種方法的實例:

    user_name = zekai ' or 1=1 #
    pwd = dsadsa
    
    # 對用戶輸入的信息先不直接拼接進sql語句的字符串,對用戶輸入的信息先用 %s 代替。將用戶輸入的信息放在一個元組/列表 中,傳入execute方法中。
    
     ****************************************************************************************************
    # 此時要注意一點:就是pymysql的execute方法會把傳入的元組或列表中的元素依次取出來,依次傳給sql字符串中的 %s 。所以有多少個元素,就要有多少個%s 。
     ***************************************************************************************************
    
    sql = "select * from user where name=%s and password=%s"
    
    使用pymysql自帶的方法,把要傳的值放進去,就會自動校驗用戶輸入內容的敏感字符
    cursor.execute(sql, (user_name, pwd))
    
    # 查詢后返回的結果,為空
    # []
    

二、索引

1. 什么是索引

  • 索引在MySQL中也叫是一種“鍵”,是存儲引擎用於快速找到記錄的一種數據結構

2. 索引有什么用

  • 加快查詢速度。

  • mysql中的primary key,unique,聯合唯一也都是索引,這些索引除了加速查找以外,還有約束的功能

  • 注意1:雖然索引能加快查詢速度,但並不是索引越多越好,當數據過多時,創建索引需要的時間也越多,而且還會占用更多的磁盤空間。 若索引太多,應用程序的性能可能會受到影響。而索引太少,對查詢性能又會產生影響,要找到一個平衡點,這對應用程序的性能至關重要 。

  • 注意2:在向一張表中插入數據時,若之前已經對該表的字段建立了索引,則數據庫會先把索引刪掉,再插入數據,之后再把索引添加回來。

3. 索引的底層原理

  • 索引底層的算法使用的是 B+樹 。

4. 主鍵

1、數據庫的每張表只能有一個主鍵,不可能有多個主鍵。

2、所謂的一張表多個主鍵,我們稱之為聯合主鍵。

注:聯合主鍵就是用多個字段一起作為一張表的主鍵。

3、主鍵的作用是保證數據的唯一性和完整性,同時通過主鍵檢索表能夠增加檢索速度。

5. MySQL中索引的分類

  • 普通索引INDEX:加速查找
  • 唯一索引:
    • 主鍵索引PRIMARY KEY:加速查找+約束(不為空、不能重復)
    • 唯一索引UNIQUE:加速查找+約束(不能重復)
  • 聯合索引:
    • PRIMARY KEY(id,name):聯合主鍵索引
    • UNIQUE(id,name):聯合唯一索引
      • 添加了聯合唯一索引的這些字段,我們可以對其任意一個再添加單個的普通索引。如unique(a,b) , 之后還可以 alter table 表名 add index 字段名1(a) alter table 表名 add index 字段名2(b)
      • 對於添加了聯合唯一索引的字段,它的唯一性指的是全部字段數據作為一個整體,不能和其他整體相同。如 unique(a,b) a,b是字段名,int類型,我們插入(1,2)之后不能插入(1,2)了,但可以插入 (1.3)或(2,2)等等
    • INDEX(id,name):聯合普通索引
      • 添加了聯合普通索引的這些字段,我們可以對其任意一個再添加單個的普通索引

6. 索引的創建

(0)創建索引的技巧:

  1. 在開發程序時,一邊開發一邊創建, 在需要的地方添加索引
  2. 對查詢頻率高的字段創建合適類型的索引
  3. 盡量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0
  4. 注意2:在向一張表中插入數據時,若之前已經對該表的字段建立了索引,則數據庫會先把索引刪掉,再插入數據,之后再把索引添加回來。所以,我們在向表中錄入大量數據時:若是新表,錄入數據之后再創建索引;若是已經有索引的表:先把原來的索引刪掉,等數據錄入完成后,再把索引恢復。通過這樣的兩種方法這樣會大大提高數據庫錄入大量數據的效率。

其他注意的地方:

  • 避免使用select *
  • 避免使用count(*)
  • 創建表時盡量使用 char 代替 varchar
  • 表的字段順序固定長度的字段優先
  • 組合索引代替多個單列索引(由於mysql中每次只能使用一個索引,所以經常使用多個條件查詢時更適合使用組合索引)
  • 盡量使用短索引
  • 使用連接(JOIN)來代替子查詢(Sub-Queries),即代替嵌套查詢
  • 連表時注意條件類型需一致
  • 索引散列值(重復少)不適合建索引,例:性別不適合

(1)創建主鍵索引

  1. 創建表時創建主鍵索引

    1.
    create table xxx(
        			id int auto_increment ,
    				primary key(id) # 創建主鍵索引
    					)charset utf8;
    2.
    create table xxx( id int auto_increment primary key # 創建主鍵索引
                    )charset utf8;
    
  2. 創建表后創建主鍵索引

    1.
    alter table 表名 change 原字段名 新字段名 字段類型 auto_increment primary key;
    2.
    alter table 表名 modify 字段名 字段類型 auto_increment primary key;
    3.
    alter table 表名 add primary key (字段名);
    
  3. 刪除主鍵索引

    alter table 表名 drop primary key;
    

(2)創建唯一索引

  1. 創建表時創建唯一索引

    create table t2(
    						id int auto_increment primary key,
    						name varchar(32) not null default '',
    						unique u_name (name) #  創建唯一索引
    					)charset utf8;
    
  2. 創建表后創建唯一索引

    1.
    CREATE  UNIQUE   INDEX  索引名 ON 表名 (字段名) ;
    2.
    alter table 表名 add unique index 索引名 (字段名);
    
    
  3. 刪除唯一索引

    alter table 表名 drop index 索引名;
    

(3)創建普通索引

  1. 創建表時創建普通索引

    create table t3(
    						id int auto_increment primary key,
    						name varchar(32) not null default '',
    						index u_name (name) # 創建普通索引
    					)charset utf8;
    
  2. 創建表后創建普通索引

    1.
    CREATE  INDEX  索引名 ON 表名 (字段名) 
    2.
    alter table 表名 add  index 索引名 (字段名)
    
  3. 刪除普通索引

    alter table 表名 drop index 索引名;
    

(4)聯合索引的創建

  • 聯合索引的創建只是在創建其他索引時,字段是多個而已
聯合唯一索引:unique 索引名(name, email)
聯合索引: index 索引名(name, email)
  • 什么時候創建聯合索引
					根據公司的業務場景, 在最常用的幾列上添加索引
					
					select * from user where name='zekai' and email='zekai@qq.com';
					
					如果遇到上述業務情況, 錯誤的做法:
						index ix_name (name),
						index ix_email(email)
					
					正確的做法:
						index ix_name_email(name, email)

7. 索引未命中情況

  1. 范圍問題,或者說條件不明確,條件中出現這些符號或關鍵字:>、>=、<、<=、!= 、between...and...、like

a. 不能在SQl語句中,進行四則運算, 會降低SQL的查詢效率
				
b. 使用函數
	select * from tb1 where reverse(email) = 'zekai';
c. 類型不一致
	如果列是字符串類型,傳入條件是必須用引號引起來,否則不會命中索引
	select * from tb1 where email = 999;
				
#排序條件為索引,則select字段必須也是索引字段,否則無法命中
d. order by
	select name from s1 order by email desc;
	當根據索引排序時候,select查詢的字段如果不是索引,則速度仍然很慢
					
	select email from s1 order by email desc;
	特別的:如果對主鍵排序,則還是速度很快:
	select * from tb1 order by nid desc;
						
e. ***********count(1)或count(*)代替count(列)*************
    1.執行效率上:
列名為主鍵,count(列名)和count(1) 和  count(*) 執行效率是一樣的:因為 explain 中 type 類型都為 index
列名不為主鍵,而且列名沒有創建索引  但是 其他字段創建了索引:count(1) = count(*) > count(列名)  :因為expalin 中的 type 類型 count(1)  和 count(*) 類型都為 index  而 count(列名) 的 type 類型為 all

列名不為主鍵,但是 列名 創建索引  :count(1) = count(*)= count(列名)   :因為 explain 中 type 類型都為 index

如果表多個列並且沒有主鍵,則 count(1) 的執行 = 優於 count(*) 
如果表只有一個字段,則 select count(*)和 select count(1) and select count(字段名)執行效率一樣。

2. count(1) and count(字段) and count(*)

主要區別是

(1) count(1) 會統計表中的所有的記錄數,包含字段為null 的記錄。

(2) count(字段) 會統計該字段在表中出現的次數,忽略字段為null 的情況。即不統計字段為null 的記錄。

  (3)    count(*) 會統計表中的所有的記錄數,包含字段為null 的記錄。 

3.執行效果上:  
count(*)包括了所有的列,相當於行數,在統計結果的時候,不會忽略列值為NULL  
count(1)包括了忽略所有列,用1代表代碼行,在統計結果的時候,不會忽略列值為NULL  
count(列名)只包括列名那一列,在統計結果的時候,某個字段值為NULL時,不統計

				
f. 組合索引最左前綴***********************************************
   只有包含最左邊即首部第一個字段的where條件,才會命中索引,否則不會命中索引。如例子:	
					
	index (a,b,c,d)
						
	where a=2 and b=3 and c=4 and d=5   --->命中索引
	where a=2 and b=3 and c=4 ——>命中
                        
	where a=2 and c=3 and d=4   ----> 命中
    where b=2 and c=3	----->未命中
					
	什么時候會創建聯合索引?
						
	根據公司的業務場景, 在最常用的幾列上添加索引
						
	select * from user where name='zekai' and email='zekai@qq.com';
						
	如果遇到上述業務情況, 錯誤的做法:
	index ix_name (name),
	index ix_email(email)
						
	正確的做法:
	index ix_name_email(name, email)
										
	如果組合索引為:
    ix_name_email (name,email) ************
					
	where name='zekai' and email='xxxx'       -- 命中索引
					
	where name='zekai'   -- 命中索引
	where email='zekai@qq.com'                -- 未命中索引
					
											

8. explain查看SQL語句執行情況

explain 關鍵字的作用:
					
mysql> explain select * from user where name='zekai' and email='zekai@qq.com'\G
					*************************** 1. row ***************************
							   id: 1          
					  select_type: SIMPLE    
							table: user
					   partitions: NULL
							 type: ref       索引指向 all
					possible_keys: ix_name_email     可能用到的索引
							  key: ix_name_email     確實用到的索引
						  key_len: 214            索引長度
							  ref: const,const
							 rows: 1            掃描的行數
						 filtered: 100.00
							Extra: Using index   使用到了索引
                                

注意:判斷是否命中索引最准確的還是要看 rows 這一行的結果。

9. 索引覆蓋

select id from user where id=2000; 
#where 條件的字段和查詢字段相同

10.慢日志管理

步驟:

  1. 查看慢日志的變量

    show variables like '%slow%';
    
    +---------------------------+-----------------------------------------------+
    				| Variable_name             | Value                                         |
    				+---------------------------+-----------------------------------------------+
    				| log_slow_admin_statements | OFF                                           |
    				| log_slow_slave_statements | OFF                                           |
    				| slow_launch_time          | 2   # 指定最大查詢時間,超過就會被記錄到日志                                          |
    				| slow_query_log            | OFF   # 默認關閉慢SQl查詢日志, on                                          |
    				| slow_query_log_file       | D:\mysql-5.7.28\data\DESKTOP-910UNQE-slow.log | # 慢SQL記錄的位置
    				+---------------------------+-----------------------------------------------+
    				5 rows in set, 1 warning (0.08 sec)
    

配置慢SQL的變量:
		
			
set global slow_query_log = on; # 開啟日志功能
			
set global slow_query_log_file="D:/mysql-5.7.28/data/myslow.log"; # 設定日志保存的目錄
			
set global long_query_time=1; # 設定最大查詢時間


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM