MySQL— 索引,視圖,觸發器,函數,存儲過程,執行計劃,慢日誌,分頁性能

一.索引,分頁性能,執行計劃,慢日誌html

(1)索引的種類,建立語句,名詞補充(最左前綴匹配,覆蓋索引,索引合併,局部索引等):python

import sys
#  http://www.cnblogs.com/wupeiqi/articles/5716963.html 武老師索引補充
'''
索引種類:
    單列:
        1.普通索引 : 加速查找
        2.主鍵索引 : 加速查找 + 不能爲空+ 惟一
        3.惟一素銀 : 加速查找 + 惟一
    多列: 
        4.聯合(組合)索引 : 1.聯合惟一  2.聯合不惟一
            聯合主鍵索引
            聯合惟一索引
            聯合普通索引(本身取名來的)
'''
#主鍵索引
#生成普通索引:
    # create index ix_name on userinfo(列名)
    #drop index ix_name on table_name
#生成惟一索引:
    #create unique index index_name on table_name(field)
#生成聯合索引:
    # create index ix_name on userinfo(列名,列名...)
    #drop index ix_name on table_name
#局部索引:
    #create index index_name on table_name(列明(16)) 表示列名前16個字符作索引
    #如下幾個類型必定要作局部索引:
        #1. Text類型

'''#最左前綴匹配
# create index ix_name_email on userinfo(name,email,lie3,lie4..); 多列仍是最左前綴
        #設置普通聯合索引,仍是會以name爲準,只有帶上name做爲條件,才能查得快,只有email仍是慢
'''
#所以組合索引 效率比 索引合併效率高
#可是組合索引,由於有 最左前綴匹配。
# 要依據實際狀況去建立索引!!!
'''
兩種不是真實的索引,是概念:
    覆蓋索引:在索引文件中直接獲取數據,(而不用去數據表再獲取其餘數據)
            覆蓋索引:select email from userinfo where email = 'email213'
                      可是若是 select * 則還要去數據表中獲取其餘列數據
    
    索引合併:把多個單列索引,合併使用
            select * from userinfo where emai=xxx and id = xxx; (email和id都設置了索引)
    
    #局部索引: 若是字段先後有重複值,那麼重複值就不取,只取該字段不重複部分作索引
'''

#全文索引:生產過程當中不借助mysql,用第三方工具,知道就好

'''
總結:
    索引的種類1.主鍵索引,2.普通索引,3.惟一索引,4.聯合索引,5.覆蓋索引和索引合併
    索引建立的命令語句
    #特色:1.查找速度快,可是更刪改慢(索引不是越多越好!!!)
           2.額外硬盤空間放索引的存儲特殊的數據結構:1.哈希索引 2.btree索引
           3.索引要命中,纔有效果
    #聯合索引:最左前綴匹配
    #索引沒法命中的狀況
'''

#create table t1( id int auto_increment primary key,
                 # num int,
                 # name char(10),
                 #unique 索引名 (num)  #單列惟一索引
                 #index 索引名 (num)  #建立普通索引
                 #unique 索引名 (num,name) #聯合惟一索引: 不能由兩行如出一轍的 num和name
索引初識
#-- select * from userinfo where name='alex359613';   3.379秒
# select * from userinfo where id = 359613;          #id做爲索引 查詢只用了 0.063秒

#生成普通索引:
    # create index ix_name on userinfo(列名)
    #drop index ix_name on table_name
#生成惟一索引:
    #create unique index index_name on table_name(field)
#生成聯合索引:
    # create index ix_name on userinfo(列名,列名...)
    #drop index ix_name on table_name
#局部索引:
    #create index index_name on table_name(列明(16)) 表示列名前16個字符作索引
    #如下幾個類型必定要作局部索引:
        #1. Text類型
'''
無索引:從前到後一次查找
  索引:
        name   建立額外文件(某種格式存儲),
        name,email   建立額外文件(某種格式存儲),
            當搜索的時候,先來這個文件裏找數據在哪一個文職,而後去表裏定位
        
        
'''

# create index ix_name_email on userinfo(name,email);
        #最左前綴匹配
        #設置普通聯合索引,仍是會以name爲準,只有帶上name做爲條件,才能查得快,只查email仍是慢

'''
索引種類
    hash索引:建立了一個索引表,把全部的name都轉換爲hash值+這行所在的內存地址,而後直接根據地址,去找到數據
              哈希表的值,和數據庫表裏的值,順序是不同的
              缺點:取範圍速度沒那麼快。
                    好比咱們找id>3在普通表就很快找了,由於遍歷只要到3,可是哈希索引由於是亂序的因此這種狀況下反倒沒那麼快
              優勢: 可是哈希索引在尋找單個值的時候,速度是很是快的
    btree索引:二叉樹的格式,用二分法查找方法,應用較廣(innodb引擎)。
                
'''
#要想查找速度快
    #創建索引:查詢快,增改刪變慢
        #1.額外的文件,保存特殊的數據結構,
        #2.建立了索引,致使咱們 insert或者update或者delete的時候也要去更新索引,因此插入和更新速度會變慢
        #3.命中索引:建立了要用,纔能有價值
hash索引和btree索引
#索引沒法命中的狀況
#執行計劃explain: 可是之後在公司的時候 若是忘記了如下狀況,能夠去sql裏面本身運行語句測試一下


# 不走索引的狀況,避免如下狀況
'''
- like '%xx'    (生產過程當中數據量大會用到第三方工具)
    select * from tb1 where name like '%cn';
    
        關於like:
            用戶量少的時候: 用like和佔位符模糊匹配
            用戶量巨大的時候:
                第三方工具 sifinks(發音)
                    他會把字段 存在文件裏, 好比:title字段
                        鎮化,增加率,省份..  對應的id有  1,,4,5,
                        而後獲取到對應id後,再到 sql中進行  select * from userinfo where id in (...)

- 使用函數
    select * from tb1 where reverse(name) = 'wupeiqi';
                            因此讓本身的字符串條件翻轉就好啦
- or
    select * from tb1 where nid = 1 or email = 'seven@live.com';
    特別的:當or條件中有未創建索引的列才失效,如下會走索引
            select * from tb1 where nid = 1 or name = 'seven';
            select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'

- 類型不一致
    若是列是字符串類型,傳入條件是必須用引號引發來,否則...
    select * from tb1 where name = 999; 若是和數據表裏數據類型不一致,就會失效
    #主鍵索引除外~~
    
- !=
    select * from tb1 where name != 'alex'
    特別的:若是是主鍵,則仍是會走索引
        select * from tb1 where nid != 123
        
- >
    select * from tb1 where name > 'alex'
    特別的:若是是主鍵或索引是整數類型,則仍是會走索引
        select * from tb1 where nid > 123
        select * from tb1 where num > 123
        
- order by
    select !email! from tb1 order by !name! desc;
    當根據索引排序時候,選擇的映射若是不是索引,則不走索引 
    
    特別的:若是對主鍵排序,則仍是走索引:
        select * from tb1 order by nid desc;
 
- 組合索引最左前綴
    若是組合索引爲:(name,email)
    name and email       -- 使用索引
    name                 -- 使用索引
    email                -- 不使用索引
'''
沒法命中索引的幾種狀況
import os

'''打星號爲基礎中的基礎,要背下來
1.******* 避免使用select *  (聯想覆蓋索引)

2.*******count(1)或者count(列),代替 count(*)

3.*******建立表的時 char 代替 varchar

4.表的字段順序固定長度字段優先

5.組合索引代替多個單列索引 (這個要根據實際狀況,若是單列查得多,仍是用單列好),注意有 最左前綴匹配

6.儘可能使用短索引: 局部索引,參見此day39 文件1

7.使用鏈接(join)來代替子查詢(Sub-Queries):mysql裏能夠先不用在乎這個,sql sever中有差異

8.連表時注意條件類型一致

9.*******索引散列值(重複少)不適合建索引,好比:性別這個列
'''
索引注意事項-有3點必定要會

 

(2).limit分頁性能幾種方案mysql

import os

''' 
    limit 數值越大越慢,好比 limit 300000,10 它會先掃描30萬行,而後再取下10行
'''

'''
那要怎麼解決這個分頁問題呢:
    1.不讓看 好比博客園,首頁最多隻能瀏覽 200頁的內容
    2.索引表中掃描: 用到了覆蓋索引,可是速度仍是不快
        select * from userinfo where id in (
                select id from userinfo limit 200000,10)
    3.較優解法:
        結合python語句,記錄當前頁最大或最小id:
            1.頁面只有上一頁和下一頁
            下一頁: select * from userinfo where id > 200000 limit 10;
            上一頁: select * from userinfo where id < 200000 order by id desc limit 10;
            那好比 197 198 199 200 201 咱們網頁上是這麼顯示的
            此時若是我在197 想跳轉到200頁怎麼操做呢?每頁10行
                    select * from userinfo where id > 200000 limit 10*(200-197),10;  #大於200000後,從第30行開始,取10行
              或者   select * from userinfo where id in      
              (select id from (select * from userinfo where id > [當前頁maxid] limit 30) as N order by N.id desc limit 10 );  
                                                        #大於200000後,取30行,設置臨時表,再從大到小排序,取前十行的id,再表中選擇
        
        
那若是沒有上下頁關聯,我就是想從第1頁跳轉到第1萬頁呢
這在sql中目前實現不了,只能慢慢等待掃描了。  

有個想法是 between and
select * from userinfo where id between 1 and 10; 
可是要考慮到 id是否必定是連續的呢, 好比 delete from userinfo where id = 4;
那麼 此時1 - 10 就只有9條數據了 
!!!***
所以有個重要前提是 【limit的時候,必定要考慮到 id是不是連續的!!】   
'''



#武老師博客的分頁代碼
'''
每頁顯示10條:
當前 118 120, 125

倒序:
            大      小
            980    970  7 6  6 5  54  43  32

21 19 98     
上一頁:

    select 
        * 
    from 
        tb1 
    where 
        nid < (select nid from (select nid from tb1 where nid < 當前頁最小值 order by nid desc limit 每頁數據 *【頁碼-當前頁】) A order by A.nid asc limit 1)  
    order by 
        nid desc 
    limit 10;



    select 
        * 
    from 
        tb1 
    where 
        nid < (select nid from (select nid from tb1 where nid < 970  order by nid desc limit 40) A order by A.nid asc limit 1)  
    order by 
        nid desc 
    limit 10;


下一頁:

    select 
        * 
    from 
        tb1 
    where 
        nid < (select nid from (select nid from tb1 where nid > 當前頁最大值 order by nid asc limit 每頁數據 *【當前頁-頁碼】) A order by A.nid asc limit 1)  
    order by 
        nid desc 
    limit 10;


    select 
        * 
    from 
        tb1 
    where 
        nid < (select nid from (select nid from tb1 where nid > 980 order by nid asc limit 20) A order by A.nid desc limit 1)  
    order by 
        nid desc 
    limit 10;
'''
limit分頁性能幾種方案

 

(3).執行計劃介紹:程序員

import os

#執行計劃
#預估一條sql語句的執行時間
'''
mysql> explain select * from userinfo \G;
*************************** 1. row ***************************
           id: 1        #註釋1
  select_type: SIMPLE  :代指簡單查詢
        table: userinfo #註釋2
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 2837841
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)
'''
#註釋1:
#id:1  標識符,代指如今預估哪一條sql,和順序無關. 這裏只有一句因此就1
        #select * from tb where id in (select id from tb2); 這裏就有2句sql了
#註釋2:
    #ALL : 全表掃描 #可是all不必定慢,好比 select * from tb limit 1;
    #range : where id > 3
    #const : where id = 3  #constant
    #ref : 經過索引查詢
    #總結:查詢時的訪問方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
            #具體各個值代指什麼,見最下方

'''
#那若是查詢的字段有 索引,他是怎麼預估的呢
mysql> explain select * from userinfo where name='alex465456' \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: userinfo
   partitions: NULL
         type: ref
possible_keys: ix_name_email
          key: ix_name_email
      key_len: 91
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)
'''
explain執行計劃介紹
'''
 type
        查詢時的訪問方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
            ALL             全表掃描,對於數據表從頭至尾找一遍
                            select * from tb1;
                            特別的:若是有limit限制,則找到以後就不在繼續向下掃描
                                   select * from tb1 where email = 'seven@live.com'
                                   select * from tb1 where email = 'seven@live.com' limit 1;
                                   雖然上述兩個語句都會進行全表掃描,第二句使用了limit,則找到一個後就再也不繼續掃描。

            INDEX           全索引掃描,對索引從頭至尾找一遍
                            select nid from tb1;

            RANGE          對索引列進行範圍查找
                            select *  from tb1 where name < 'alex';
                            PS:
                                between and
                                in
                                >   >=  <   <=  操做
                                注意:!= 和 > 符號


            INDEX_MERGE     合併索引,使用多個單列索引搜索
                            select *  from tb1 where name = 'alex' or nid in (11,22,33);

            REF             根據索引查找一個或多個值
                            select *  from tb1 where name = 'seven';

            EQ_REF          鏈接時使用primary key 或 unique類型
                            select tb2.nid,tb1.name from tb2 left join tb1 on tb2.nid = tb1.nid;



            CONST           常量
                            表最多有一個匹配行,由於僅有一行,在這行的列值可被優化器剩餘部分認爲是常數,const表很快,由於它們只讀取一次。
                            select nid from tb1 where nid = 2 ;

            SYSTEM          系統
                            表僅有一行(=系統表)。這是const聯接類型的一個特例。
                            select * from (select nid from tb1 where nid = 1) as A;
'''
type屬性詳解

 

(4).慢日誌記錄:sql

import os
#DBA工做
#在mysql的服務端進行記錄

'''
mysql> show variables like '%quer%';
+----------------------------------------+---------------------------------------------+
| Variable_name                          | Value                                       |
+----------------------------------------+---------------------------------------------+
| binlog_rows_query_log_events           | OFF                                         |
| ft_query_expansion_limit               | 20                                          |
| have_query_cache                       | NO                                          |
| log_queries_not_using_indexes          | OFF        # 語句未使用索引                                  |
| log_throttle_queries_not_using_indexes | 0                                           |
| long_query_time                        | 10.000000  #運行時間大於10秒 則記錄                            |
| query_alloc_block_size                 | 8192                                        |
| query_prealloc_size                    | 8192                                        |
| slow_query_log                         | OFF         # 慢查詢日誌是否開啓                                  |
| slow_query_log_file   #慢日誌目錄       | D:\mysql-8.0.12-winx64\data\Gkx-PC-slow.log |
+----------------------------------------+---------------------------------------------+
10 rows in set, 1 warning (0.00 sec)


注:查看當前配置信息:
       show variables like '%query%'
     修改當前配置:
    set global 變量名 = 值
       set global slow_query_log = ON;
'''

#配置:
    #以上是依據內存進行設置: 設置完立刻生效

    #還能夠依據配置文件: mysqld
            # mysqld --defaults-file='D:\mysql-8.0.12-winx64\my.ini' #  就是咱們最早設置的 my.ini 啦,因此能夠告訴它這個ini配置文件的路徑,而後咱們在ini裏寫好配置便可
                #好比所在 d:/my.ini  而後在這個 ini文件裏 把slow_query_log = ON;
                #想要設置的變量寫進去
                # slow_query_log = ON
                # slow_query_log_file = '路徑'...
'''
                修改配置文件前,必定要備份
                修改配置文件後,要重啓才能生效
'''
慢日誌記錄

 

二.視圖數據庫

視圖view

 

三.觸發器網絡

# 定義 : 對某個表進行【增/刪/改】操做的先後若是但願觸發某個特定的行爲時,可使用觸發器,(能夠插入多張表也沒問題)
# 觸發器用於定製用戶對錶的行進行【增/刪/改】先後的行爲。
#注意觸發器是沒有 select 的引起行爲的,只有 insert,delete,update
#若是tb1 在一行語句插入多行數據,那麼觸發器也會對應插入多行數據

# 要建立觸發器前,應該先修改分隔符  delimiter
#insert into tb (....)
# -- delimiter //
# -- create trigger t1 BEFORE INSERT on student for EACH ROW
# -- BEGIN
# --     INSERT into teacher(tname) values(NEW.sname);  #由於不修改的話,執行到這裏的分號語句就結束了,下面的執行不到
# --     INSERT into teacher(tname) values(NEW.sname);  #NEW.sname 表示你插入tb1 的那條字段的新數據
# --     INSERT into teacher(tname) values(NEW.sname);
# --     INSERT into teacher(tname) values(NEW.sname);
# -- END //
# -- delimiter ;  #修改完記得改回來

# -- insert into student(gender,class_id,sname) values('女',1,'陳濤'),('女',1,'張根');
# -- NEW,代指新數據 (用於 insert)
# -- OLD,代指老數據 (用於 delete / update)

#總結觸發器以下:
# # 插入前
# CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
# BEGIN
#     INSERT into teacher(tname) values(NEW.sname); (在插入數據以前,要作這個操做)也就是你每往tb1插入一條數據,tb2也會插入數據
# END                                                  #若是tb1 在一行語句插入多行數據,那麼觸發器也會對應插入多行數據
#
# # 插入後
# CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
# BEGIN
#     ...
# END
#
# # 刪除前
# CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
# BEGIN
#     ...
# END
#
# # 刪除後
# CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
# BEGIN
#     ...
# END
#
# # 更新前
# CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
# BEGIN
#     ...
# END
#
# # 更新後
# CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
# BEGIN
#     ...
# END
trigger觸發器

 

四.函數session

#MySQL也有 內置函數 和 自定義函數  :函數是一定有返回值的

# select now()

# 老師博客: http://www.cnblogs.com/wupeiqi/articles/5713323.html
# 官方解釋:https://dev.mysql.com/doc/refman/5.7/en/functions.html
#時間格式化
#select DATE_FORMAT(時間字段,'%Y-%m'),count(1) from table_name group by DATE_FORMAT(時間字段,'%Y-%m')
#格式相似 time模塊

#自定義函數:
    # delimiter \\
    # create function f1(
    #     i1 int,
    #     i2 int) #傳參
    # returns int #返回值數據類型
    # BEGIN
    #     declare num int;  #必定不能寫 select
    #     set num = i1 + i2;
    #     return(num);
    # END \\
    # delimiter ;
#刪除函數:drop function func_name;
#執行函數: select f1(0,100) ; 輸出 100
    # # 獲取返回值
    # declare @i VARCHAR(32);
    # select UPPER('alex') into @i;
    # SELECT @i;
    #
    # # 在查詢中使用
    # select f1(11,nid) ,name from tb2;
函數介紹
#部份內置函數—字符串相關的
# CHAR_LENGTH(str)
#         返回值爲字符串str 的長度,長度的單位爲字符。一個多字節字符算做一個單字符。
#         對於一個包含五個二字節字符集, LENGTH()返回值爲 10, 而CHAR_LENGTH()的返回值爲5。
#
#     CONCAT(str1,str2,...)
#         字符串拼接
#         若有任何一個參數爲NULL ,則返回值爲 NULL。
#     CONCAT_WS(separator,str1,str2,...)
#         字符串拼接(自定義鏈接符)
#         CONCAT_WS()不會忽略任何空字符串。 (然而會忽略全部的 NULL)。
#
#     CONV(N,from_base,to_base)
#         進制轉換
#         例如:
#             SELECT CONV('a',16,2); 表示將 a 由16進制轉換爲2進制字符串表示
#
#     FORMAT(X,D)
#         將數字X 的格式寫爲'#,###,###.##',以四捨五入的方式保留小數點後 D 位, 並將結果以字符串的形式返回。若  D 爲 0, 則返回結果不帶有小數點,或不含小數部分。
#         例如:
#             SELECT FORMAT(12332.1,4); 結果爲: '12,332.1000'
#     INSERT(str,pos,len,newstr)
#         在str的指定位置插入字符串
#             pos:要替換位置其實位置
#             len:替換的長度
#             newstr:新字符串
#         特別的:
#             若是pos超過原字符串長度,則返回原字符串
#             若是len超過原字符串長度,則由新字符串徹底替換
#     INSTR(str,substr)
#         返回字符串 str 中子字符串的第一個出現位置。
#
#     LEFT(str,len)
#         返回字符串str 從開始的len位置的子序列字符。
#
#     LOWER(str)
#         變小寫
#
#     UPPER(str)
#         變大寫
#
#     LTRIM(str)
#         返回字符串 str ,其引導空格字符被刪除。
#     RTRIM(str)
#         返回字符串 str ,結尾空格字符被刪去。
#     SUBSTRING(str,pos,len)
#         獲取字符串子序列
#
#     LOCATE(substr,str,pos)
#         獲取子序列索引位置
#
#     REPEAT(str,count)
#         返回一個由重複的字符串str 組成的字符串,字符串str的數目等於count 。
#         若 count <= 0,則返回一個空字符串。
#         若str 或 count 爲 NULL,則返回 NULL 。
#     REPLACE(str,from_str,to_str)
#         返回字符串str 以及全部被字符串to_str替代的字符串from_str 。
#     REVERSE(str)
#         返回字符串 str ,順序和字符順序相反。
#     RIGHT(str,len)
#         從字符串str 開始,返回從後邊開始len個字符組成的子序列
#
#     SPACE(N)
#         返回一個由N空格組成的字符串。
#
#     SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
#         不帶有len 參數的格式從字符串str返回一個子字符串,起始於位置 pos。帶有len參數的格式從字符串str返回一個長度同len字符相同的子字符串,起始於位置 pos。 使用 FROM的格式爲標準 SQL 語法。也可能對pos使用一個負值。倘若這樣,則子字符串的位置起始於字符串結尾的pos 字符,而不是字符串的開頭位置。在如下格式的函數中能夠對pos 使用一個負值。
#
#         mysql> SELECT SUBSTRING('Quadratically',5);
#             -> 'ratically'
#
#         mysql> SELECT SUBSTRING('foobarbar' FROM 4);
#             -> 'barbar'
#
#         mysql> SELECT SUBSTRING('Quadratically',5,6);
#             -> 'ratica'
#
#         mysql> SELECT SUBSTRING('Sakila', -3);
#             -> 'ila'
#
#         mysql> SELECT SUBSTRING('Sakila', -5, 3);
#             -> 'aki'
#
#         mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
#             -> 'ki'
#
#     TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str)
#         返回字符串 str , 其中全部remstr 前綴和/或後綴都已被刪除。若分類符BOTH、LEADIN或TRAILING中沒有一個是給定的,則假設爲BOTH 。 remstr 爲可選項,在未指定狀況下,可刪除空格。
#
#         mysql> SELECT TRIM('  bar   ');
#                 -> 'bar'
#
#         mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');
#                 -> 'barxxx'
#
#         mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');
#                 -> 'bar'
#
#         mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');
#                 -> 'barx'
#
# 部份內置函數
字符串相關的函數
最經常使用的:字符串去重,拼接,取子序列,時間的操做

五.存儲過程數據結構

import time
'''
#存儲過程,相比 視圖,觸發器,函數 更爲重要
#存儲過程在服務端的mysql(視圖,觸發器,函數也是),客戶端只要知道存儲過程的名字就能夠了
#定義: 保存在mysql上的一個別名 ---> 一串SQL語句,使用別名就能查到語句
#沒有返回值的概念 有三個參數類型 in(傳進去要使用的)  out(傳進去要拿出來的)  inout(傳進去既可使用也能夠拿出來的)
'''
#和視圖的區別:
    #視圖:別名代指臨時表,只能select,動態的顯示錶,並不建議修改
    #存儲過程:存了不少sql語句,比視圖多。調用更簡單,實現功能更多
            #用來替代sql語句的,讓程序員不在寫程序語句了
            #直接調用 存儲過程 別名,就能夠直接使用sql語句了
            #將sql語句和代碼進行解耦了,好比網絡傳輸只須要傳別名

#程序員寫sql,而後交給dba審覈
#慢日誌:pymysql 能夠記錄執行較慢的 sql語句。

# 方式一:
#     mysql:存儲過程
#     程序:調用存儲過程
# 方式二:
    # mysql:啥也不作
    #程序:寫SQL語句
#方式三:
    #mysql:啥也不作
    #程序:寫類和對象  ---> 轉化爲 SQL語句

#存儲過程初識:
    # 一 不帶參數
        # delimiter //
        # create procedure p1()
        # begin
        #     select * from student;
        #     insert into teacher(tname) values('ct');
        # end //
        # delimiter ;
        #執行procedure : call p1();
        #cursor.callproc('p1')

    #二 帶參數的:in (傳進去使用的) 參數表示有 int out inout
        # delimiter //
        # create procedure p2(
        #           in n1 int,
        #           in n2 int
        #                     )
        # begin
        #     select * from student where sid between n1 and n2;
        #
        # end //
        # delimiter ;
        # 執行procedure : call p1(12,2);
        # cursor.callproc('p2',(12,2))
'''
    #三. 帶參數的:out (傳進去後要拿出來的)  參數表示有 int out inout
        # delimiter //
        # create procedure p3(
        #           in n1 int,
        #           out n2 int  #n2必須是變量
        #                     )
        # begin
        #    set n2 = 11111;
        #     select * from student where sid > n1;
        #
        # end //
        # delimiter ;
        # 執行procedure :
                    # set @v1 = 1;
                    # call p3(12,@v1); 此時 n2 做爲out類型,要傳一個變量,而後能夠在外部再獲取到這個變量
                    # select @v1;
        # cursor.callproc('p2',(12,2)) 首先拿結果集
        # cursor.execute('select @_p3_0,@_p3_1')
        # ret = cursor.fetchone();print(ret)------> ((12,11111))----》((n1的值,n2修改後的值))

        #mysql中有一個session的概念
        # set  @v1 = 0;  #表示建立了一個 session級別的變量 v1 . 【@】相似python中的 global
        # # 執行procedure : call p3(12,@v1);
        #而後再使用 @v1的時候,@v1值就變爲了 11111 --> (select @v1;)
'''

# 特性======>
    #1.可傳參 in out inout
    #2.MySQL中用call調用,pymysql用  cursor.callproc(procedure,(arg1,arg2..))
    #3.沒有返回值,可是能夠用 out 僞造一個返回值

'''
爲何有結果集又有out僞造的返回值?
    out 返回值,通常狀況下是:用於標識存儲過程當中的執行結果
            好比返回 1,2,3       1:成功  2:失敗  3:一部分紅功
            語句1執行 執行完  set n1 =1 表示成功
            語句2執行 執行完  set n2 =2 表示失敗.....
'''
procedure存儲過程的參數:in out inout
import sys
#1.無參   2.帶參數in   3.帶參數 in  out

# #接下來第四種: 事務:原子性操做
# 在pymysql中能夠進行 try一下,若是報錯,則回滾
# ~~~~~~~~~~中文描述
# delimiter //
# create procedure p4(
# out status int
# )
# BEGIN
# 1. 聲明若是出現異常則執行{
# set status = 1;
# rollback;
# }
#
# 開始事務
# -- 由秦兵帳戶減去100
# -- 方少偉帳戶加90
# -- 張根帳戶加10
# commit;
# 結束
# set status = 2;
'''
#接下來第四種: 事務:原子性操做
在pymysql中能夠進行 try一下,若是報錯,則回滾  
代碼以下:
delimiter \\
create PROCEDURE p4(
    OUT p_return_code tinyint
)
BEGIN
    DECLARE exit handler for sqlexception  #若是出現異常,執行下面begin裏的語句
    # https://www.cnblogs.com/datoubaba/archive/2012/06/20/2556428.html
BEGIN
-- ERROR
set p_return_code = 1; #錯誤返回1
rollback;
END;

START TRANSACTION;   #開始事物
DELETE from tb1;
insert into tb2(name)values('seven');
COMMIT;

-- SUCCESS
set p_return_code = 2; #正確返回2

END\\
delimiter ;
'''
#第四代碼:
存儲過程—事務
#防 sql注入
#1.pymysql 程序級別去避免
#2.動態的存儲過程

#第五:
   #遊標:老師沒講

#第六:
# 動態的執行 SQL
'''
6. 動態執行SQL(防SQL注入)

delimiter //
create procedure p7(
in tpl varchar(255),
in arg int
)
begin
1. 預檢測某個東西 SQL語句合法性
2. SQL =格式化 tpl + arg
3. 執行SQL語句

set @xo = arg;  #arg 要賦值給一個 session變量
PREPARE xxx FROM 'select * from student where sid > ?';
EXECUTE xxx USING @xo;
DEALLOCATE prepare prod;
end  //
delimter ;
# call p7("select * from tb where id > ?",9)

'''
#真代碼以下:
# ===>
#
# delimiter \\
# CREATE PROCEDURE p8 (
# in nid int
# )
# BEGIN
# set @nid = nid;
# PREPARE prod FROM 'select * from student where sid > ?';
# EXECUTE prod USING @nid;
# DEALLOCATE prepare prod;
# END\\
# delimiter ;
存儲過程—遊標,動態sql(防止sql注入)
import pymysql

conn = pymysql.connect(host='localhost',user='root',password='gkx321',database='sql_homework',charset='utf8')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

cursor.callproc('p3',(12,2)) #n1是12,無所謂n2是多少,不影響
ret = cursor.fetchall()
print(ret)

#out的值要這麼獲取,固定寫法
cursor.execute('select @_p3_0,@_p3_1') #能夠只寫一個 0獲取n1,1獲取n2
ret = cursor.fetchall()
print(ret)

'''
爲何格式是這樣呢,由於pymysql內部幫咱們作了這樣子的操做
set @ _p3_0 = 12
set @ _p3_1 = 2
call p3( @ _p3_0,@_p3_1)
select @ _p3_0,@_p3_1
'''

# cursor.execute('select * from teacher')
# ret = cursor.fetchall()
# print(ret)

cursor.close()
conn.close()
pymysql調用存儲過程
存儲過程總結:
1.無參數
2.有參數 in
3.有參數 in out
爲何有結果集又要有out? ----> 用於標識存儲過程當中的執行結果
4.事務
5.遊標
6.動態執行sql(防sql注入)

數據庫相關操做:
1.sql語句:本身寫sql所有都能實現
想要利用函數的時候,先找mysql內置函數,找不到再本身寫 #函數可能會形成性能低:又索引,函數不會使用索引
-- select xx() from xx;
最經常使用的:字符串去重,拼接,取子序列
時間的操做

2.利用mysql內部提供的功能:視圖,觸發器,函數,存儲過程
相關文章
相關標籤/搜索