一、 視圖 二、 觸發器 三、 事務 四、 存儲過程 五、 函數 六、 流程式控制制 一 、視圖 視圖是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的數據集,併為其命名】,用戶使用時只需使用【名稱】即可獲取結果集,可以將該結果集當做表來使用。 使用視圖我們可以把查詢過程中的臨時表摘出來,用 ...
一 、視圖
視圖是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的數據集,併為其命名】,用戶使用時只需使用【名稱】即可獲取結果集,可以將該結果集當做表來使用。
使用視圖我們可以把查詢過程中的臨時表摘出來,用視圖去實現,這樣以後再想操作該臨時表的數據時就無需重寫複雜的sql了,直接去視圖中查找即可,但視圖有明顯地效率問題,並且視圖是存放在資料庫中的,如果我們程式中使用的sql過分依賴資料庫中的視圖,即強耦合,那就意味著擴展sql極為不便,因此並不推薦使用
#兩張有關係的表 mysql> select * from course; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 體育 | 3 | | 4 | 美術 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> select * from teacher; +-----+-----------------+ | tid | tname | +-----+-----------------+ | 1 | 大娃老師 | | 2 | 二娃老師 | | 3 | 三娃老師 | | 4 | 四娃老師 | | 5 | 五娃老師 | +-----+-----------------+ 5 rows in set (0.00 sec) #查詢二娃老師教授的課程名 mysql> select cname from course where teacher_id = (select tid from teacher where tname='二娃老師'); +--------+ | cname | +--------+ | 物理 | | 美術 | +--------+ 2 rows in set (0.00 sec) #子查詢出臨時表,作為teacher_id等判斷依據 select tid from teacher where tname='二娃老師'創建臨時表
一 、創建視圖
#語法:CREATE VIEW 視圖名稱 AS SQL語句 create view teacher_view as select tid from teacher where tname='二娃老師'; #於是查詢二娃老師教授的課程名的sql可以改寫為 mysql> select cname from course where teacher_id = (select tid from teacher_view); +--------+ | cname | +--------+ | 物理 | | 美術 | +--------+ 2 rows in set (0.00 sec) #!!!註意註意註意: #1. 使用視圖以後就無需每次都重寫子查詢的sql,但是這麼效率並不高,還不如我們寫子查詢的效率高 #2. 而且有一個致命的問題:視圖是存放到資料庫里的,如果我們程式中的sql過分依賴於資料庫中存放的視圖,那麼意味著,一旦sql需要修改且涉及到視圖的部分,則必須去資料庫中進行修改,而通常在公司中資料庫有專門的DBA負責,你要想完成修改,必須付出大量的溝通成本DBA可能才會幫你完成修改,極其地不方便如何創建視圖
二 、使用視圖
#修改視圖,原始表也跟著改 mysql> select * from course; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 體育 | 3 | | 4 | 美術 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> create view course_view as select * from course; #創建表course的視圖 Query OK, 0 rows affected (0.52 sec) mysql> select * from course_view; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 體育 | 3 | | 4 | 美術 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> update course_view set cname='xxx'; #更新視圖中的數據 Query OK, 4 rows affected (0.04 sec) Rows matched: 4 Changed: 4 Warnings: 0 mysql> insert into course_view values(5,'yyy',2); #往視圖中插入數據 Query OK, 1 row affected (0.03 sec) mysql> select * from course; #發現原始表的記錄也跟著修改了 +-----+-------+------------+ | cid | cname | teacher_id | +-----+-------+------------+ | 1 | xxx | 1 | | 2 | xxx | 2 | | 3 | xxx | 3 | | 4 | xxx | 2 | | 5 | yyy | 2 | +-----+-------+------------+ 5 rows in set (0.00 sec)強調:千萬不要修改視圖
強調:我們不應該修改視圖中的記錄,而且在涉及多個表的情況下是根本無法修改視圖中的記錄的
三、修改視圖
語法:ALTER VIEW 視圖名稱 AS SQL語句 mysql> alter view teacher_view as select * from course where cid>3; Query OK, 0 rows affected (0.04 sec) mysql> select * from teacher_view; +-----+-------+------------+ | cid | cname | teacher_id | +-----+-------+------------+ | 4 | xxx | 2 | | 5 | yyy | 2 | +-----+-------+------------+ 2 rows in set (0.00 sec)修改操作視圖
四、 刪除視圖
語法:DROP VIEW 視圖名稱
DROP VIEW teacher_view
刪除操作視圖
二 、觸發器
使用觸發器可以定製用戶對錶進行【增、刪、改】操作時前後的行為,註意:沒有查詢
一 、創建觸發器
# 插入前 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觸發器基本語法
#準備表 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 ); #創建觸發器 delimiter // CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW BEGIN IF NEW.success = 'no' THEN #等值判斷只有一個等號 INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ; #必須加分號 END IF ; #必須加分號 END// delimiter ; #往表cmd中插入記錄,觸發觸發器,根據IF的條件決定是否插入錯誤日誌 INSERT INTO cmd ( USER, priv, cmd, sub_time, success ) VALUES ('duoduo','0755','ls -l /etc',NOW(),'yes'), ('duoduo','0755','cat /etc/passwd',NOW(),'no'), ('duoduo','0755','useradd xxx',NOW(),'no'), ('duoduo','0755','ps aux',NOW(),'yes'); #查詢錯誤日誌,發現有兩條 mysql> select * from errlog; +----+-----------------+---------------------+ | id | err_cmd | err_time | +----+-----------------+---------------------+ | 1 | cat /etc/passwd | 2017-09-14 22:18:48 | | 2 | useradd xxx | 2017-09-14 22:18:48 | +----+-----------------+---------------------+ 2 rows in set (0.00 sec)觸發器舉例應用
特別的:NEW表示即將插入的數據行,OLD表示即將刪除的數據行。
二、使用觸發器
觸發器無法由用戶直接調用,而知由於對錶的【增/刪/改】操作被動引發的。
三、 刪除觸發器
刪除語法:
drop trigger tri_after_insert_cmd;
三 、事務
事務用於將某些操作的多個SQL作為原子性操作,一旦有某一個出現錯誤,即可回滾到原來的狀態,從而保證資料庫數據完整性。
create table user( id int primary key auto_increment, name char(32), balance int ); insert into user(name,balance) values ('大娃',1000), ('二娃',1000), ('三娃',1000); #原子操作 start transaction; update user set balance=900 where name='大娃'; #買支付100元 update user set balance=1010 where name='二娃'; #中介拿走10元 update user set balance=1090 where name='三娃'; #賣家拿到90元 commit; #出現異常,回滾到初始狀態 start transaction; update user set balance=900 where name='大娃'; #買支付100元 update user set balance=1010 where name='二娃'; #中介拿走10元 uppdate user set balance=1090 where name='三娃'; #賣家拿到90元,出現異常沒有拿到 rollback; #回到原來的狀態 commit; mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | 大娃 | 1000 | | 2 | 二娃 | 1000 | | 3 | 三娃 | 1000 | +----+------+---------+ 3 rows in set (0.00 sec)事務實例
四 、存儲過程
一、 介紹
存儲過程包含了一系列可執行的sql語句,存儲過程存放於MySQL中,通過調用它的名字可以執行其內部的一堆sql
使用存儲過程的優點:
1. 用於替代程式寫的SQL語句,實現程式與sql解耦
2. 基於網路傳輸,傳別名的數據量小,而直接傳sql數據量大
使用存儲過程的缺點:
程式員擴展功能不方便
補充:程式與資料庫結合使用的三種方式
方式一:
MySQL:存儲過程
程式:調用存儲過程
方式二:
MySQL:
程式:純SQL語句
方式三:
MySQL:
程式:類和對象,即ORM(本質還是純SQL語句)
二、 創建簡單存儲過程(無參)
delimiter // #定義sql的結束語句為// create procedure p1() BEGIN select * from blog; INSERT into blog(name,sub_time) values("xxx",now()); END // delimiter ; #定義sql的結束語句為; #在mysql中調用 call p1() #在python中基於pymysql調用 cursor.callproc('p1') print(cursor.fetchall())無參的例子
三、 創建存儲過程(有參)
對於存儲過程,可以接收參數,其參數有三類:
in 僅用於傳入參數用
out 僅用於返回值用
inout 既可以傳入又可以當作返回值
delimiter // create procedure p2( in n1 int, in n2 int ) BEGIN select * from blog where id > n1; END // delimiter ; #在mysql中調用 call p2(3,2) #在python中基於pymysql調用 cursor.callproc('p2',(3,2)) print(cursor.fetchall())in的運用實例
delimiter // create procedure p3( in n1 int, out res int ) BEGIN select * from blog where id > n1; set res = 1; END // delimiter ; #在mysql中調用 set @res=0; #0代表假(執行失敗),1代表真(執行成功) call p3(3,@res); select @res; #在python中基於pymysql調用 cursor.callproc('p3',(3,0)) #0相當於set @res=0 print(cursor.fetchall()) #查詢select的查詢結果 cursor.execute('select @_p3_0,@_p3_1;') #@_p3_0代表第一個參數,@_p3_1代表第二個參數,即返回值 print(cursor.fetchall())out的運用實例
delimiter // create procedure p4( inout n1 int ) BEGIN select * from blog where id > n1; set n1 = 1; END // delimiter ; #在mysql中調用 set @x=3; call p4(@x); select @x; #在python中基於pymysql調用 cursor.callproc('p4',(3,)) print(cursor.fetchall()) #查詢select的查詢結果 cursor.execute('select @_p4_0;') print(cursor.fetchall())inout的運用實例
#介紹 delimiter // create procedure p4( out status int ) BEGIN 1. 聲明如果出現異常則執行{ set status = 1; rollback; } 開始事務 -- 大娃賬戶減去100 -- 二娃賬戶加90 -- 三娃賬戶加10 commit; 結束 set status = 2; END // delimiter ; #實現 delimiter // create PROCEDURE p5( 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; DELETE from tb1; #執行失敗 insert into blog(name,sub_time) values('yyy',now()); COMMIT; -- SUCCESS set p_return_code = 0; #0代表執行成功 END // delimiter ; #在mysql中調用存儲過程 set @res=123; call p5(@res); select @res; #在python中基於pymysql調用存儲過程 cursor.callproc('p5',(123,)) print(cursor.fetchall()) #查詢select的查詢結果 cursor.execute('select @_p5_0;') print(cursor.fetchall())事務和存儲過程的實例
四、 執行存儲過程
-- 無參數 call proc_name() -- 有參數,全in call proc_name(1,2) -- 有參數,有in,out,inout set @t1=0; set @t2=3; call proc_name(1,2,@t1,@t2) 執行存儲過程在MySQL中執行存儲過程
import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123', db='t1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 執行存儲過程 cursor.callproc('p1', args=(1, 22, 3, 4)) # 獲取執行完存儲的參數 cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3") result = cursor.fetchall() conn.commit() cursor.close() conn.close() print(result)在python中基於pymysql執行存儲過程
五、 刪除存儲過程
刪除語法:
drop procedure proc_name;
五 、函數
MySQL中提供了許多內置函數,例如:
一、數學函數 ROUND(x,y) 返回參數x的四捨五入的有y位小數的值 RAND() 返回0到1內的隨機值,可以通過提供一個參數(種子)使RAND()隨機數生成器生成一個指定的值。 二、聚合函數(常用於GROUP BY從句的SELECT查詢中) AVG(col)返回指定列的平均值 COUNT(col)返回指定列中非NULL值的個數 MIN(col)返回指定列的最小值 MAX(col)返回指定列的最大值 SUM(col)返回指定列的所有值之和 GROUP_CONCAT(col) 返回由屬於一組的列值連接組合而成的結果 三、字元串函數 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' 四、日期和時間函數 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' 五、加密函數 MD5() 計算字元串str的MD5校驗和 PASSWORD(str) 返回字元串str的加密版本,這個加密過程是不可逆轉的,和UNIX密碼加密過程使用不同的演算法。 六、控制流函數 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;否則返回arg1sql內置函數
#1 基本使用 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' #2 準備表和記錄 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'),COUNT(1) FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m'); #結果 +-------------------------------+----------+ | DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) | +-------------------------------+----------+ | 2015-03 | 2 | | 2016-07 | 4 | | 2017-03 | 3 | +-------------------------------+----------+ 3 rows in set (0.00 sec)date_format的實例
一 、自定義函數
!!!註意!!! 函數中不要寫sql語句(否則會報錯),函數僅僅只是一個功能,是一個在sql中被應用的功能 若要想在begin...end...中寫sql,請用存儲過程
delimiter // create function f1( i1 int, i2 int) returns int BEGIN declare num int; set num = i1 + i2; return(num); END // delimiter ;自定義函數例子
二、 刪除函數
drop function func_name;
三、 執行函數
獲取返回值 select UPPER('egon') into @res; SELECT @res;
在查詢中使用 select f1(11,nid) ,name from tb2;
六 、流程式控制制
一、 條件語句
delimiter // CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT 1; ELSEIF i = 2 THEN SELECT 2; ELSE SELECT 7; END IF; END // delimiter ;if語句的實例
二、 迴圈語句
delimiter // CREATE PROCEDURE proc_while () BEGIN DECLARE num INT ; SET num = 0 ; WHILE num < 10 DO SELECT num ; SET num = num + 1 ; END WHILE ; END // delimiter ;while 語句的實例