python 全棧開發,Day64(視圖,觸發器,函數,存儲過程,事務)

 昨日內容回顧

pymysql:屬於python的一個模塊 pip3 install pymysql conn = pymysql.connect(...,charset = 'uft8') 建立遊標 conn.cursor(dic) (1)修改數據,刪除數據,增長數據必定要commit cursor.execut(sql,{}|()|[]) 必定要注意sql注入 (2)fetchone() fetchmany(4) fetchall() cursor.close() conn.close()
View Code

 

1、視圖

1、視圖的定義

視圖是虛擬表或邏輯表,它被定義爲具備鏈接的SQL SELECT查詢語句。由於數據庫視圖與數據庫表相似,它由行和列組成,所以能夠根據數據庫表查詢數據。其內容由查詢定義。 可是,視圖並不在數據庫中以存儲的數據值集形式存在,行和列數據來自由定義視圖的查詢所引用的表,而且在引用視圖時動態生成。簡單的來講視圖是由其定義結果組成的表;

2、視圖的優勢

一、數據庫視圖容許簡化複雜查詢,經過數據庫視圖,您只需使用簡單的SQL語句,而不是使用具備多個鏈接的複雜的SQL語句。 二、安全性。通常是這樣作的:建立一個視圖,定義好該視圖所操做的數據。以後將用戶權限與視圖綁定。這樣的方式是使用到了一個特性:grant語句能夠針對視圖進行授予權限。

3、視圖的缺點

一、性能:從數據庫視圖查詢數據可能會很慢,特別是若是視圖是基於其餘視圖建立的。 二、表依賴關係:將根據數據庫的基礎表建立一個視圖。每當更改與其相關聯的表的結構時,都必須更改視圖。 4、建立視圖

4、建立視圖

語法:html

CREATE VIEW 視圖名稱 AS  SQL語句

臨時表應用舉例:python

#兩張有關係的表,分別是課程表和老師表 #建立課程表 CREATE TABLE `course` ( `cid` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '課程id', `cname` varchar(20) DEFAULT NULL COMMENT '課程名', `teacher_id` int(11) DEFAULT NULL COMMENT '老師id', PRIMARY KEY (`cid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='課程表'; #建立老師表 CREATE TABLE `teacher` ( `tid` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '老師id', `tname` varchar(20) DEFAULT NULL COMMENT '老師名', PRIMARY KEY (`tid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='老師表'; #插入數據 INSERT INTO `course` (`cid`, `cname`, `teacher_id`) VALUES ('1', '生物', '1'); INSERT INTO `course` (`cid`, `cname`, `teacher_id`) VALUES ('2', '物理', '2'); INSERT INTO `course` (`cid`, `cname`, `teacher_id`) VALUES ('3', '體育', '3'); INSERT INTO `course` (`cid`, `cname`, `teacher_id`) VALUES ('4', '美術', '2'); INSERT INTO `teacher` (`tid`, `tname`) VALUES ('1', '張磊'); INSERT INTO `teacher` (`tid`, `tname`) VALUES ('2', '李平'); INSERT INTO `teacher` (`tid`, `tname`) VALUES ('3', '劉海燕'); INSERT INTO `teacher` (`tid`, `tname`) VALUES ('4', '朱雲海'); INSERT INTO `teacher` (`tid`, `tname`) VALUES ('5', '李傑'); #查詢李平老師教授的課程名 #子查詢出臨時表,做爲teacher_id等判斷依據 mysql> select cname from course where teacher_id = (select tid from teacher where tname='李平'); +--------+ | cname | +--------+ | 物理 | | 美術 | +--------+ 2 rows in set (0.01 sec)
View Code

視圖的建立:mysql

#建立老師表的視圖 mysql> create view teacher_view as select * from teacher; Query OK, 0 rows affected (0.39 sec) #查詢視圖 mysql> select * from teacher_view; +-----+-----------+ | tid | tname | +-----+-----------+ | 1 | 張磊 | | 2 | 李平 | | 3 | 劉海燕 | | 4 | 朱雲海 | | 5 | 李傑 | +-----+-----------+ 5 rows in set (0.00 sec) #查詢老師表 mysql> select * from teacher; +-----+-----------+ | tid | tname | +-----+-----------+ | 1 | 張磊 | | 2 | 李平 | | 3 | 劉海燕 | | 4 | 朱雲海 | | 5 | 李傑 | +-----+-----------+ 5 rows in set (0.00 sec) #發現2個表的數據是一摸同樣的,沒有區別
View Code

 查看數據庫文件,發現teacher_view是沒有數據文件ibd的,算法

視圖並不在數據庫中以存儲的數據值集形式存在。行和列數據來自由定義視圖的查詢所引用的表,而且在引用視圖時動態生成。sql

 

5、使用視圖

舉例1:數據庫

# 往真實表中插入一條數據,查看一下視圖,發現視圖表也會跟着更新 mysql> INSERT INTO teacher (tname) VALUES ('高圓圓'); Query OK, 1 row affected (0.10 sec) #查看視圖,發現更新了一條數據 mysql> select * from teacher_view; +-----+-----------+ | tid | tname | +-----+-----------+ | 1 | 張磊 | | 2 | 李平 | | 3 | 劉海燕 | | 4 | 朱雲海 | | 5 | 李傑 | | 6 | 高圓圓 | +-----+-----------+ 6 rows in set (0.00 sec)
View Code

舉例2:緩存

#往視圖表中插入一條數據,真實表也會發現變化 mysql> INSERT INTO teacher_view (tname) VALUES ('董卿'); Query OK, 1 row affected (0.09 sec) #查看真實表,發現也更新了 mysql> select * from teacher; +-----+-----------+ | tid | tname | +-----+-----------+ | 1 | 張磊 | | 2 | 李平 | | 3 | 劉海燕 | | 4 | 朱雲海 | | 5 | 李傑 | | 6 | 高圓圓 | | 7 | 董卿 | +-----+-----------+ 7 rows in set (0.00 sec)
View Code

若是將2張表聯合生產視圖,是不能修改視圖的數據的。由於它不知道,該從哪一個表裏面插入數據安全

舉例:服務器

#建立聯合表的視圖 create view ct as select * from course left join teacher on teacher.tid = course.teacher_id; #查看視圖記錄 mysql> select * from ct; +-----+--------+------------+------+-----------+ | cid | cname | teacher_id | tid | tname | +-----+--------+------------+------+-----------+ | 1 | 生物 | 1 | 1 | 張磊 | | 2 | 物理 | 2 | 2 | 李平 | | 3 | 體育 | 3 | 3 | 劉海燕 | | 4 | 美術 | 2 | 2 | 李平 | +-----+--------+------------+------+-----------+ 4 rows in set (0.00 sec) #插入失敗 mysql> insert into ct values(7,'哈哈',7,7,'張三丰'); ERROR 1471 (HY000): The target table ct of the INSERT is not insertable-into
View Code

6、修改視圖

# 語法:ALTER VIEW 視圖名稱 AS SQL語句 mysql> alter view teacher_view as select * from teacher where tid > 3; Query OK, 0 rows affected (0.09 sec) #查看視圖記錄,發現改變了。 mysql> select * from teacher_view; +-----+-----------+ | tid | tname | +-----+-----------+ | 4 | 朱雲海 | | 5 | 李傑 | | 6 | 高圓圓 | | 7 | 董卿 | +-----+-----------+ 4 rows in set (0.00 sec)
View Code

7、刪除視圖

# 語法:DROP VIEW 視圖名稱 mysql> drop view teacher_view; Query OK, 0 rows affected (0.00 sec)
View Code

注意:視圖只能用到查詢併發

若是使用視圖查詢會很慢,不推薦使用。由於它會消費MySQL性能

2、觸發器

使用觸發器能夠定製用戶對錶進行【增、刪、改】操做時先後的行爲,注意:沒有查詢

1、建立觸發器

有6個動做,分別是增前,增後。刪前,刪後。改前,改後

# 插入前 CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW BEGIN ... END # 插入後 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
View Code

例子:用戶和日誌表。每次建立一個用戶以後,就在日誌布表中生成這條記錄

準備表:

# 建立用戶表 create table user( id int primary key auto_increment, name varchar(20) not null, reg_time datetime, # 註冊用戶的時間 affirm enum('yes','no') # no表示該用戶執行失敗 ); #建立日誌表 create table userLog( id int primary key auto_increment, u_name varchar(20) not null, u_reg_time datetime # 註冊用戶的時間 );
View Code

小知識點:

默認狀況下,mysql語句是以分號來做爲結束符的。經過delimiter命令,能夠修改結束符。

好比修改成//

#修改結束符爲// mysql> delimiter // #執行sql語句,以分號結尾 mysql> select * from course; #發現結束不了,必須用//才行 -> -> // +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 體育 | 3 | | 4 | 美術 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec)
View Code

delimiter修改是全局的,不要輕易修改。修改以後,要還原回來。

 

二 使用觸發器

觸發器沒法由用戶直接調用,而知因爲對錶的【增/刪/改】操做被動引起的。

# 建立觸發器 delimiter 默認狀況下,delimiter是分號 觸發器名稱應遵循命名約定[trigger time]_[table name]_[trigger event] delimiter // create trigger after_user_insert after insert on user for each row begin if new.affirm = 'yes' then insert into userLog(u_name,u_reg_time) values(new.name,new.reg_time); end if; end // delimiter ; #往用戶表中插入記錄,觸發觸發器,根據if的條件決定是否插入數據 mysql> insert into user(name,reg_time,affirm) values ('張三',now(),'yes'),('李四',now(),'yes'),('王五',now(),'no'); Query OK, 3 rows affected (0.13 sec) Records: 3 Duplicates: 0 Warnings: 0 # 查看日誌表,發現多了兩條記錄 ,你們應該看到for each row就明白了 mysql> select * from userlog; +----+--------+---------------------+ | id | u_name | u_reg_time | +----+--------+---------------------+ | 1 | 張三 | 2018-06-15 16:07:15 | | 2 | 李四 | 2018-06-15 16:07:15 | +----+--------+---------------------+ 2 rows in set (0.00 sec)
View Code

注意:請注意,在爲INSERT定義的觸發器中,能夠僅使用NEW關鍵字。不能使用OLD關鍵字。可是,在爲DELETE定義的觸發器中,沒有新行,所以您只能使用OLD關鍵字。在UPDATE觸發器中,OLD是指更新前的行,而NEW是更新後的行

new.name表示表裏面的字段name

當new.affirm爲yes才插入,不然不插入

三 刪除觸發器

語法: drop trigger 觸發器名; #刪除觸發器after_user_insert mysql> drop trigger after_user_insert; Query OK, 0 rows affected (0.00 sec)
View Code

注意:

不建議使用觸發器

trigger對之後的維護以及開發不怎麼好,後面可能忘了定義過trigger,致使一些莫名的bug
若是是面向用戶的高併發應用,都不要使用

在併發不高的項目,好比管理系統中,可使用觸發器

 

3、函數

 MySQL中提供了許多內置函數:

 內置函數

1、數學函數 ROUND(x,y) 返回參數x的四捨五入的有y位小數的值 RAND() 返回0到1內的隨機值,能夠經過提供一個參數(種子)使RAND()隨機數生成器生成一個指定的值。 2、聚合函數(經常使用於GROUP BY從句的SELECT查詢中) AVG(col)返回指定列的平均值 COUNT(col)返回指定列中非NULL值的個數 MIN(col)返回指定列的最小值 MAX(col)返回指定列的最大值 SUM(col)返回指定列的全部值之和 GROUP_CONCAT(col) 返回由屬於一組的列值鏈接組合而成的結果 3、字符串函數 CHAR_LENGTH(str) 返回值爲字符串str 的長度,長度的單位爲字符。一個多字節字符算做一個單字符。 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) 變大寫 REVERSE(str) 返回字符串 str ,順序和字符順序相反。 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' 4、日期和時間函數 CURDATE()或CURRENT_DATE() 返回當前的日期 CURTIME()或CURRENT_TIME() 返回當前的時間 DAYOFWEEK(date) 返回date所表明的一星期中的第幾天(1~7) DAYOFMONTH(date) 返回date是一個月的第幾天(1~31) DAYOFYEAR(date) 返回date是一年的第幾天(1~366) DAYNAME(date) 返回date的星期名,如:SELECT DAYNAME(CURRENT_DATE); FROM_UNIXTIME(ts,fmt) 根據指定的fmt格式,格式化UNIX時間戳ts HOUR(time) 返回time的小時值(0~23) MINUTE(time) 返回time的分鐘值(0~59) MONTH(date) 返回date的月份值(1~12) MONTHNAME(date) 返回date的月份名,如:SELECT MONTHNAME(CURRENT_DATE); NOW() 返回當前的日期和時間 QUARTER(date) 返回date在一年中的季度(1~4),如SELECT QUARTER(CURRENT_DATE); WEEK(date) 返回日期date爲一年中第幾周(0~53) YEAR(date) 返回日期date的年份(1000~9999) 重點: DATE_FORMAT(date,format) 根據format字符串格式化date值 mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y'); -> 'Sunday October 2009' mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s'); -> '22:23:00' mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00', -> '%D %y %a %d %m %b %j'); -> '4th 00 Thu 04 10 Oct 277' mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00', -> '%H %k %I %r %T %S %w'); -> '22 22 10 10:23:00 PM 22:23:00 00 6' mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V'); -> '1998 52' mysql> SELECT DATE_FORMAT('2006-06-00', '%d'); -> '00' 5、加密函數 MD5() 計算字符串str的MD5校驗和 PASSWORD(str) 返回字符串str的加密版本,這個加密過程是不可逆轉的,和UNIX密碼加密過程使用不一樣的算法。 6、控制流函數 CASE WHEN[test1] THEN [result1]...ELSE [default] END 若是testN是真,則返回resultN,不然返回default CASE [test] WHEN[val1] THEN [result]...ELSE [default]END 若是test和valN相等,則返回resultN,不然返回default IF(test,t,f) 若是test是真,返回t;不然返回f IFNULL(arg1,arg2) 若是arg1不是空,返回arg1,不然返回arg2 NULLIF(arg1,arg2) 若是arg1=arg2返回NULL;不然返回arg1 內置函數
View Code

更多函數:中文猛擊這裏 OR 官方猛擊這裏

打開官方連接,裏面有21個內置函數 

 

掌握內置函數中的時間格式化DATE_FORMAT()的用法

官網示例:

mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y'); +------------------------------------------------+ | DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y') | +------------------------------------------------+ | Sunday October 2009 | +------------------------------------------------+ 1 row in set (0.34 sec) mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s'); +------------------------------------------------+ | DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s') | +------------------------------------------------+ | 22:23:00 | +------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00','%D %y %a %d %m %b %j'); +-----------------------------------------------------------+ | DATE_FORMAT('1900-10-04 22:23:00','%D %y %a %d %m %b %j') | +-----------------------------------------------------------+ | 4th 00 Thu 04 10 Oct 277 | +-----------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00','%H %k %I %r %T %S %w'); +-----------------------------------------------------------+ | DATE_FORMAT('1997-10-04 22:23:00','%H %k %I %r %T %S %w') | +-----------------------------------------------------------+ | 22 22 10 10:23:00 PM 22:23:00 00 6 | +-----------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V'); +------------------------------------+ | DATE_FORMAT('1999-01-01', '%X %V') | +------------------------------------+ | 1998 52 | +------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT DATE_FORMAT('2006-06-00', '%d'); +---------------------------------+ | DATE_FORMAT('2006-06-00', '%d') | +---------------------------------+ | 00 | +---------------------------------+ 1 row in set (0.00 sec)
View Code

舉2個例子

#返回參數x的四捨五入的有y位小數的值 mysql> select round(113.34545,3); +--------------------+ | round(113.34545,3) | +--------------------+ | 113.345 | +--------------------+ 1 row in set (0.00 sec) #返回0到1內的隨機值,能夠經過提供一個參數(種子)使RAND()隨機數生成器生成一個指定的值。 mysql> select rand(); +--------------------+ | rand() | +--------------------+ | 0.8292567104373655 | +--------------------+ 1 row in set (0.33 sec)
View Code

 

舉例:博客園 時間處理

# 一、博客園 時間處理 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'); #3. 提取sub_time字段的值,按照格式後的結果即"年月"來分組 SELECT DATE_FORMAT(sub_time,'%Y-%m') as y_m,COUNT(1) as num FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m'); # 結果: +---------+-----+ | y_m | num | +---------+-----+ | 2015-03 | 2 | | 2016-07 | 4 | | 2017-03 | 3 | +---------+-----+ rows in set (0.00 sec)
View Code

關於更多時間處理,請參考官網連接

https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format

下圖只列舉了一部分

 

4、存儲過程

1、存儲過程的定義

存儲過程是存儲在數據庫目錄中的一些的聲明性SQL語句。 Java,Python,PHP等應用程序能夠調用存儲過程。

MySQL是最受歡迎的開源RDBMS,被社區和企業普遍使用。 然而,在它發佈的第一個十年期間,它不支持存儲過程,存儲函數觸發器事件。自從MySQL 5.0版本以來,這些功能被添加到MySQL數據庫引擎,使其更加靈活和強大。

 

2、存儲過程的優勢

#一、一般存儲過程有助於提升應用程序的性能。當建立,存儲過程被編譯以後,就存儲在數據庫中。 可是,MySQL實現的存儲過程略有不一樣。 MySQL存儲過程按需編譯。 在編譯存儲過程以後,MySQL將其放入緩存中。 MySQL爲每一個鏈接維護本身的存儲過程高速緩存。 若是應用程序在單個鏈接中屢次使用存儲過程,則使用編譯版本,不然存儲過程的工做方式相似於查詢。 # 二、存儲過程有助於減小應用程序和數據庫服務器之間的流量,由於應用程序沒必要發送多個冗長的SQL語句,而只能發送存儲過程的名稱和參數。 #三、存儲的程序對任何應用程序都是可重用的和透明的。 存儲過程將數據庫接口暴露給全部應用程序,以便開發人員沒必要開發存儲過程當中已支持的功能。 #四、存儲的程序是安全的。 數據庫管理員能夠向訪問數據庫中存儲過程的應用程序授予適當的權限,而不向基礎數據庫表提供任何權限。

除了這些優勢以外,存儲過程有其自身的缺點,在數據庫中使用它們以前,您應該注意這些缺點。

 

3、存儲過程的缺點

#一、若是使用大量存儲過程,那麼使用這些存儲過程的每一個鏈接的內存使用量將會大大增長。 此外,若是您在存儲過程當中過分使用大量邏輯操做,則CPU使用率也會增長,由於數據庫服務器的設計不當於邏輯運算。 #二、存儲過程的構造使得開發具備複雜業務邏輯的存儲過程變得更加困難。 #三、很難調試存儲過程。只有少數數據庫管理系統容許您調試存儲過程。不幸的是,MySQL不提供調試存儲過程的功能。 #四、開發和維護存儲過程並不容易。開發和維護存儲過程一般須要一個不是全部應用程序開發人員擁有的專業技能。這可能會致使應用程序開發和維護階段的問題。

MySQL存儲過程有本身的優勢和缺點。開發應用程序時,您應該決定是否應該或不該該根據業務需求使用存儲過程。

 

4、一個簡單的Mysql存儲過程示例

delimiter // create procedure b1() begin select * from blog; end // delimiter ;

解釋:

1.第一個命令是delimiter //,它與存儲過程語法無關。 delimter語句將標準分隔符 - 分號(;)更改成://。 在這種狀況下,分隔符從分號(;)更改成雙斜槓//。爲何咱們必須更改分隔符? 由於咱們想將存儲過程做爲總體傳遞給服務器,而不是讓mysql工具一次解釋每一個語句。 在END關鍵字以後,使用分隔符//來指示存儲過程的結束。 最後一個命令(DELIMITER;)將分隔符更改回分號(;)。

2.使用create procedure語句建立一個新的存儲過程。在create procedure語句以後指定存儲過程的名稱。在這個示例中,存儲過程的名稱爲:b1,並把括號放在存儲過程的名字以後。

3.begin和end之間的部分稱爲存儲過程的主體。將聲明性SQL語句放在主體中以處理業務邏輯。 在這個存儲過程當中,咱們使用一個簡單的select語句來查詢blog表中的數據。

 

mysql中調用存儲過程 

語法: call 存儲過程名(); #調用b1,返回的是begin和end之間的sql語句 mysql> call b1(); +----+---------+---------------------+ | id | NAME | sub_time | +----+---------+---------------------+ | 1 | 第1篇 | 2015-03-01 11:31:21 | | 2 | 第2篇 | 2015-03-11 16:31:21 | | 3 | 第3篇 | 2016-07-01 10:21:31 | | 4 | 第4篇 | 2016-07-22 09:23:21 | | 5 | 第5篇 | 2016-07-23 10:11:11 | | 6 | 第6篇 | 2016-07-25 11:21:31 | | 7 | 第7篇 | 2017-03-01 15:33:21 | | 8 | 第8篇 | 2017-03-01 17:32:21 | | 9 | 第9篇 | 2017-03-01 18:31:21 | +----+---------+---------------------+ 9 rows in set (0.00 sec) Query OK, 0 rows affected (0.02 sec)
View Code

 

在python中基於pymysql調用

#導入模塊 import pymysql # 1.鏈接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='', db='db1', charset='utf8') # 2.建立遊標 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 3.執行存儲過程b1 cursor.callproc('b1') #返回多個記錄(rows) result = cursor.fetchall() print('返回記錄數:',len(result)) # 關閉鏈接,遊標和鏈接都要關閉 cursor.close() conn.close()
View Code

執行py文件,執行輸出:

返回記錄數: 9

 

5、聲明變量

要在存儲過程當中聲明變量,可使用delclare語句,以下

DECLARE variable_name datatype(size) DEFAULT default_value;

下面來更詳細地解釋上面的語句:

首先,在DECLARE關鍵字後面要指定變量名。變量名必須遵循MySQL表列名稱的命名規則。
其次,指定變量的數據類型及其大小。變量能夠有任何MySQL數據類型,如INT,VARCHAR,DATETIME等。
第三,當聲明一個變量時,它的初始值爲NULL。可是可使用DEFAULT關鍵字爲變量分配默認值。

 

實現:

delimiter // create procedure b2() begin DECLARE n int DEFAULT 1; set n = 5; select * from blog where id = n; end // delimiter ; # mysql中調用存儲過程 mysql> call b2(); +----+---------+---------------------+ | id | NAME | sub_time | +----+---------+---------------------+ | 5 | 第5篇 | 2016-07-23 10:11:11 | +----+---------+---------------------+ 1 row in set (0.00 sec)
View Code

 

6、存儲過程傳參

在現實應用中,開發的存儲過程幾乎都須要參數。這些參數使存儲過程更加靈活和有用。 在MySQL中,參數有三種模式:IN,OUT或INOUT。
IN - 是默認模式。在存儲過程當中定義IN參數時,調用程序必須將參數傳遞給存儲過程。 另外,IN參數的值被保護。這意味着即便在存儲過程當中更改了IN參數的值,在存儲過程結束後仍保留其原始值。換句話說,存儲過程只使用IN參數的副本。
OUT - 能夠在存儲過程當中更改OUT參數的值,並將其更改後新值傳遞迴調用程序。請注意,存儲過程在啓動時沒法訪問OUT參數的初始值。
INOUT - INOUT參數是IN和OUT參數的組合。這意味着調用程序能夠傳遞參數,而且存儲過程能夠修改INOUT參數並將新值傳遞迴調用程序。

在存儲過程當中定義參數的語法以下:

MODE param_name param_type(param_size)

根據存儲過程當中參數的目的,MODE能夠是IN,OUT或INOUT。
param_name是參數的名稱。參數的名稱必須遵循MySQL中列名的命名規則。
在參數名以後是它的數據類型和大小。和變量同樣,參數的數據類型能夠是任何有效的MySQL數據類型

ps:若是存儲過程有多個參數,則每一個參數由逗號(,)分隔。

1. in

delimiter // create procedure b3( in blogName varchar(30) ) begin select * from blog where NAME = blogName; end // delimiter ;
View Code

mysql中調用存儲過程

mysql> call b3('第5篇'); +----+---------+---------------------+ | id | NAME | sub_time | +----+---------+---------------------+ | 5 | 第5篇 | 2016-07-23 10:11:11 | +----+---------+---------------------+ 1 row in set (0.00 sec)
View Code

python中調用存儲過程

#導入模塊 import pymysql # 1.鏈接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='', db='db1', charset='utf8') # 2.建立遊標 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 3.執行存儲過程b3,傳入參數。注意:參數結尾必須有一個逗號 cursor.callproc('b3',args=('第5篇',)) #返回多個記錄(rows) result = cursor.fetchall() print('返回記錄數:',len(result)) # 關閉鏈接,遊標和鏈接都要關閉 cursor.close() conn.close()
View Code

執行py文件,輸出:

返回記錄數: 1

 

2. out

delimiter // create procedure b4( in year int, out count int ) begin SELECT COUNT(1) into count FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y') having max(DATE_FORMAT(sub_time,'%Y')) = year ; set count = 6; end // delimiter ; #調用存儲過程 mysql> call b4(2016,@count); Query OK, 1 row affected (0.00 sec) #查看返回值,out只能返回值 mysql> select @count; +--------+ | @count | +--------+ | 6 | +--------+ 1 row in set (0.00 sec)
View Code

 

3. inout

inout:既能夠傳入又能夠返回

delimiter // create procedure b5( inout n1 int ) begin select * from blog where id > n1; end // delimiter ;
View Code

mysql中調用存儲過程(必須先定義一個用戶變量)

#必須先定義一個用戶變量 mysql> set @n1=5; Query OK, 0 rows affected (0.00 sec) #再調用,就能夠了 mysql> call b5(@n1); +----+---------+---------------------+ | id | NAME | sub_time | +----+---------+---------------------+ | 6 | 第6篇 | 2016-07-25 11:21:31 | | 7 | 第7篇 | 2017-03-01 15:33:21 | | 8 | 第8篇 | 2017-03-01 17:32:21 | | 9 | 第9篇 | 2017-03-01 18:31:21 | +----+---------+---------------------+ 4 rows in set (0.00 sec)
View Code

在python中基於pymysql調用

#!/usr/bin/env python # -*- coding: utf-8 -*- #導入模塊 import pymysql # 1.鏈接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='', db='db1', charset='utf8') # 2.建立遊標 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 3.執行存儲過程b3,傳入參數。注意:參數結尾必須有一個逗號 cursor.callproc('b5',args=('4',)) #返回多個記錄(rows) result = cursor.fetchall() print('返回記錄數:',len(result)) cursor.execute('select @n1') print(cursor.fetchall()) # 關閉鏈接,遊標和鏈接都要關閉 cursor.close() conn.close()
View Code

執行py文件,輸出:

返回記錄數: 5
[{'@n1': None}]

爲何n1輸出None呢?由於n1是一個用戶變量

用戶變量與數據庫鏈接有關, 這某個鏈接中聲明變量, 在斷開鏈接時這個變量就會消失, 且在這個鏈接中聲明的變量沒法在另外一個鏈接中使用

 

5、事務

事務用於將某些操做的多個SQL做爲原子性操做,一旦有某一個出現錯誤,便可回滾到原來的狀態,從而保證數據庫數據完整性。

 

舉例說明:

準備基礎數據

#建立用戶表 create table user2( id int primary key auto_increment, name char(32), balance int ); #插入數據 insert into user2(name,balance) values ('wsb',1000), ('egon',1000), ('ysb',1000);
View Code

模擬異常,並進行回滾

#先模擬出現異常,回滾到初始狀態 #原子操做 #表示開始事務 mysql> start transaction; Query OK, 0 rows affected (0.00 sec) #買支付100元 mysql> update user2 set balance=900 where name='wsb'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 #中介拿走10元 mysql> update user2 set balance=1010 where name='egon'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 #賣家拿到90元 mysql> update user2 set balance=1090 where name='ysb'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 #查看數據,發現不對 mysql> select * from user2; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 900 | | 2 | egon | 1010 | | 3 | ysb | 1090 | +----+------+---------+ 3 rows in set (0.00 sec) #回滾到初始狀態 mysql> rollback; Query OK, 0 rows affected (0.10 sec) mysql> select * from user2; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 1000 | | 2 | egon | 1000 | | 3 | ysb | 1000 | +----+------+---------+ 3 rows in set (0.00 sec)
View Code

注意:一旦執行commit,就沒法回滾了!

舉例:

#原子操做 start transaction; update user2 set balance=900 where name='wsb'; #買支付100元 update user2 set balance=1010 where name='egon'; #中介拿走10元 update user2 set balance=1090 where name='ysb'; #賣家拿到90元 commit; #查看錶記錄,發現不對 mysql> select * from user; +----+--------+---------------------+--------+ | id | name | reg_time | affirm | +----+--------+---------------------+--------+ | 1 | 張三 | 2018-06-15 16:07:15 | yes | | 2 | 李四 | 2018-06-15 16:07:15 | yes | | 3 | 王五 | 2018-06-15 16:07:15 | no | +----+--------+---------------------+--------+ 3 rows in set (0.00 sec) #執行回滾操做 mysql> rollback; Query OK, 0 rows affected (0.00 sec) #再查看錶記錄,發現回不來了... mysql> select * from user2; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 900 | | 2 | egon | 1010 | | 3 | ysb | 1090 | +----+------+---------+ 3 rows in set (0.00 sec)
View Code

 

下面是操做:當p_return_code爲1時,表示異常,立馬回滾。當爲2時,出現警告,立馬回滾原始狀態。0表示成功

delimiter // create PROCEDURE b6( OUT p_return_code tinyint ) BEGIN DECLARE exit handler for sqlexception BEGIN -- ERROR set p_return_code = 1; rollback; END; DECLARE exit handler for sqlwarning BEGIN -- WARNING set p_return_code = 2; rollback; END; START TRANSACTION; insert into blog(name,sub_time) values('ok',now()); COMMIT; -- SUCCESS set p_return_code = 0; #0表明執行成功 END // delimiter ; #設置局部變量 mysql> set @res=123; Query OK, 0 rows affected (0.00 sec) #調用事務 mysql> call b6(@res); Query OK, 0 rows affected (0.36 sec) #查看局部變量,發現歸0了。說明執行成功 mysql> select @res; +------+ | @res | +------+ | 0 | +------+ 1 row in set (0.00 sec) #查看錶,會發現有一條ok記錄 mysql> select * from blog; | 26 | ok | 2018-06-15 21:27:02 |
View Code

DECLARE exit handler for sqlexception
DECLARE exit handler for sqlwarning

這2個表示異常處理,因爲沒法模擬異常。只能簡單的執行存儲過程,就能夠了。

相關文章
相關標籤/搜索