12、mysql之視圖,觸發器,事務等

1、視圖

視圖是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的數據集,併爲其命名】,用戶使用時只需使用【名稱】便可獲取結果集,能夠將該結果集當作表來使用。html

使用視圖咱們能夠把查詢過程當中的臨時表摘出來,用視圖去實現,這樣之後再想操做該臨時表的數據時就無需重寫複雜的sql了,直接去視圖中查找便可,但視圖有明顯地效率問題,而且視圖是存放在數據庫中的,若是咱們程序中使用的sql過度依賴數據庫中的視圖,即強耦合,那就意味着擴展sql極爲不便,所以並不推薦使用python

 

-- 1.視圖是一個虛擬表(非正式存在),其本質是其本質是
-- 【根據SQL語句獲取動態的數據集,併爲其命名】,
-- 用戶使用時只需使用【名稱】便可獲取結果集,
-- 能夠將該結果集當作表來使用。
-- 2.
-- 有了視圖之後你是否是以爲寫sql語句就很簡單了,可是你儘可能不要這樣作
-- 由於mysql是DBA管着呢,那麼你告訴DBA建一堆視圖,你寫程序的時候是方便了,
-- 可是你要是修改呢,那麼你就得修改視圖了,你就得找到DBA修改你的視圖了,
-- 那麼這樣聯繫別人會很麻煩的。說不定人家還很忙呢。仍是推薦本身去寫sql語句。

-- #注意:
-- 若是是一個單表的就能夠修改或者刪除或者插入
-- 若是是幾個表關聯的時候是不能夠修改或者刪除或者插入的(這點也是不肯定的,有的能夠改,有的不能夠改)

準備表mysql

========================
建立部門表
create table dep(
id int primary key auto_increment,
name char(32)
);
建立用戶表
create table user(
id int primary key auto_increment,
name char(32),
dep_id int,
foreign key(dep_id) references dep(id)
);
插數據
insert into dep(name) values('外交部'),('銷售'),('財經部');
insert into user(name,dep_id) values ('egon',1),
                                       ('alex',2),
                                       ('haiyan',3);

1.建立視圖sql

建立視圖語法數據庫

CREATE VIEW 視圖名稱 AS SQL語句網絡

create view teacher_view as select tid from teacher where tname='李平老師';ide

#連表
select * from dep left join user on dep.id = user.dep_id;
#建立一個視圖
create view user_dep_view as select dep.id depid ,user.id uid ,user.name uname,dep.name depname from dep left join user
on dep.id = user.dep_id;
-- 這樣建立一個視圖之後就能夠吧一個虛擬表保存下來,就能夠查看了。
select uname from user_dep_view where depid = 3;
-- 個人電腦上不能夠增刪改,只可查看。可是有的電腦上又能夠增刪改,多是跟版本有關吧
#測試
insert into user_dep_view VALUES (1,2,'egon','人文部'); #會報錯
DELETE from  user_dep_view where uid = 1;  #會報錯
update user_dep_view set uname = '海燕' where depid = 2; #會報錯
-- 對於單表來講是能夠修改的,而且原來表的也就更改了。
-- 可是通常仍是不要這樣改。視圖大多數是用來查看的
#建表
CREATE TABLE t1(
id int PRIMARY KEY auto_increment,
name CHAR(10)
);
#插入數據
insert into t1 VALUES (1,'egon'),
                        (2,'daa'),
                        (3,'eef');
#建立視圖
CREATE view t1_view as select * from t1;
#測試建立視圖之後還能不能增刪改查
select * from t1_view;
update t1_view set name = '海燕' where id = 2; #能夠修改(並且原來表的記錄也修改了)
INSERT into t1_view values(4,'aaa'); #能夠插入(同上)
delete from t1_view where id=3;#同上

2.修改視圖函數

語法:ALTER VIEW 視圖名稱 AS SQL語句

3.刪除視圖工具

語法:DROP VIEW 視圖名稱

2、觸發器

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

-- 觸發器:某種程序觸發了工具的運行
-- 觸發器不能主動調用,只有觸發了某種行爲纔會調用觸發器的執行
-- 插入一條記錄就觸發一次
-- 仍是建議不要用觸發器,由於這是BDA管理的,仍是不如你在程序裏面直接寫比較方便

1.建立觸發器的語法

create
   trigger trigger_name
   trigger_time trigger_event
   on tbl_name for each row
   triggrr_body  #主體,就是在觸發器裏幹什麼事
trigger_time:{before | after}
trigger_event:{insert | update |detele}

準備表

-- # 2.準備表
-- #第一步:準備表
create table cmd_log(
id int primary key auto_increment,
cmd_name char(64), #命令的名字
sub_time datetime, #提交時間
user_name char(32), #是哪一個用戶過來執行這個命令
is_success enum('yes','no')  #命令是否執行成功
);

create table err_log(
id int primary key auto_increment,
cname char(64), #命令的名字
stime datetime #提交時間
);

建立觸發器

-- #建立觸發器(向err_log表裏插入最新的記錄)
delimiter //
create
  trigger tri_after_inser_cmd_log
  after insert
  on cmd_log for  each row
BEGIN
  if new.is_success = 'no' then
    insert into err_log(cname,stime) VALUES(new.cmd_name,new.sub_time);
  end if;  #記得加分號,mysql一加分號表明結束,那麼就得聲明一下
END //
delimiter ;  #還原的最原始的狀態


-- #建立觸發器(向err_log表裏插入最舊的記錄)
delimiter //
create
  trigger tri_after_inser_cmd_log1
  after delete
  on cmd_log for  each row
BEGIN
  if old.is_success = 'no' then
    insert into err_log(cname,stime) VALUES(old.cmd_name,old.sub_time);
  end if;  #記得加分號,mysql一加分號表明結束,那麼就得聲明一下
END //
delimiter ;  #還原的最原始的狀態
DELETE from cmd_log where id=1;

 

-- 觸發器的兩個關鍵字:new ,old
-- new :表示新的記錄
-- old:表示舊的那條記錄
-- 什麼狀況下才往裏面插記錄
--       當命令輸入錯誤的時候就把錯誤的記錄插入到err_log表中

測試

# 4.測試
insert into cmd_log(cmd_name,sub_time,user_name,is_success) values
('ls -l /etc | grep *.conf',now(),'root','no'), #NEW.id,NEW.cmd_name,NEW.sub_time
('ps aux |grep mysqld',now(),'root','yes'),
('cat /etc/passwd |grep root',now(),'root','yes'),
('netstat -tunalp |grep 3306',now(),'egon','no');

3、事務

-- 事務用於將某些操做的多個SQL做爲原子性操做,一旦有某一個出現錯誤,
-- 便可回滾到原來的狀態,從而保證數據庫數據完整性。
-- 事務也就是要麼都成功,要麼都不成功
-- 事務就是由一堆sql語句組成的
create table user(
id int primary key auto_increment,
name char(32),
balance int  #用戶餘額
);

insert into user(name,balance) values('海燕',200),
                                       ('哪吒',200),
                                       ('小哈',200);

-- 若是都成功就執行commit,,,若是不成功就執行rollback。
start transaction #開啓事務
update user set balance = 100 where name = '海燕';
update user set balance = 210 where name = '哪吒';
update user set balance = 290 where name = '小哈';  #sql語句錯誤就會報錯了
commit; #若是全部的sql語句都沒有出現異常,應該執行commit

start transaction
update user set balance = 100 where name = '海燕';
update user set balance = 210 where name = '哪吒';
updatezzzz user set balance = 290 where name = '小哈';  #sql語句錯誤就會報錯了
rollback; #若是任意一條sql出現異常,都應該回歸到初始狀態
上面的兩種狀況咱們能夠用異常處理捕捉一下
 1 delimiter //
 2 create PROCEDURE p6(
 3     OUT p_return_code tinyint
 4 )
 5 BEGIN
 6     DECLARE exit handler for sqlexception
 7     BEGIN
 8         -- ERROR
 9         set p_return_code = 1;
10         rollback;
11     END;
12     DECLARE exit handler for sqlwarning
13     BEGIN
14         -- WARNING
15         set p_return_code = 2;
16         rollback;
17     END;
18     START TRANSACTION;
19        update user set balance = 100 where name = '海燕';
20        update user set balance = 210 where name = '哪吒';
21        update user11 set balance = 290 where name = '小哈';
22     COMMIT;
23     -- SUCCESS
24     set p_return_code = 0; #0表明執行成功
25 END //
26 delimiter ;
捕捉異常+事務1
 1 ============捕捉異常+事務==============
 2 delimiter //
 3 create PROCEDURE p6(
 4     OUT p_return_code tinyint
 5 )
 6 BEGIN
 7     DECLARE exit handler for sqlexception
 8     BEGIN
 9         -- ERROR
10         set p_return_code = 1;
11         rollback;
12     END;
13     DECLARE exit handler for sqlwarning
14     BEGIN
15         -- WARNING
16         set p_return_code = 2;
17         rollback;
18     END;
19     START TRANSACTION;
20         insert into test(username,dep_id) values('egon',1);
21         DELETE from tb1111111; #若是執行失敗,就不會執行commit了
22     COMMIT;
23     -- SUCCESS
24     set p_return_code = 0; #0表明執行成功
25 END //
26 delimiter ;
27 #調用
28 set @res = 111  #至關於定義一個全局變量
29 call p6(@res)
30 select * from test;
31 select @res
捕捉異常+事務
其實也就至關於python中的try.....except
 1 #用python模擬
 2 try:
 3     START TRANSACTION;
 4         DELETE from tb1; #執行失敗
 5         insert into blog(name,sub_time) values('yyy',now());
 6     COMMIT;
 7     set p_return_code = 0; #0表明執行成功
 8 except sqlexception:
 9     set p_return_code = 1;
10     rollback;
11 except sqlwaring:
12     set p_return_code = 2;
13     rollback;
用python模擬

4、存儲過程

存儲過程包含了一系列可執行的sql語句,存儲過程存放於MySQL中,經過調用它的名字能夠執行其內部的一堆sql

-- 存儲過程的優勢:
--   1.程序與數據實現解耦
--   2.減小網絡傳輸的數據量
-- 可是看似很完美,仍是不推薦你使用
 1 ===========建立無參的存儲過程===============
 2 delimiter //
 3 create procedure p1()
 4 begin
 5     select * from test;
 6     insert into test(username,dep_id) VALUES('egon',1);
 7 end //
 8 delimiter ;
 9 
10 #調用存儲過程
11 #在mysql中調用
12 call p1();
13 #在python程序中調用
14 cursor.callproc('p1')
建立無參的存儲過程

對於存儲過程,能夠接收參數,其參數有三類:

  #in 僅用於傳入參數用

  #out 僅用於返回值用

   #inout 既能夠傳入又能夠看成返回值

 1 ==========建立有參的存儲過程(in)===============
 2 delimiter //
 3 create procedure p2(
 4     in m int, #從外部傳進來的值
 5     in n int
 6 )
 7 begin
 8     insert into test(username,dep_id) VALUES ('haha',2),('xixi',3),('sasa',1),('yanyan',2);
 9     select * from test where id between m and n;
10 end //
11 delimiter ;
12 
13 #調用存儲過程
14 call p2(3,7); #在mysql中執行
15 #在python程序中調用
16 cursor.callproc('p2',arg(3,7))
建立有參的存儲過程(in)
 1 ===========建立有參的存儲過程(out)===============
 2 delimiter //
 3 create procedure p3(
 4     in m int, #從外部傳進來的值
 5     in n int,
 6     out res int
 7 )
 8 begin
 9     select * from test where id between m and n;
10     set res = 1;#若是不設置,則res返回null
11 end //
12 delimiter ;
13 
14 #調用存儲過程
15 set @res = 11111;
16 call p3(3,7,@res);
17 select @res; #在mysql中執行
18 
19 #在python中
20 res=cursor.callproc('p3',args=(3,7,123)) #@_p3_0=3,@_p3_1=7,@_p3_2=123
21 print(cursor.fetchall()) #只是拿到存儲過程當中select的查詢結果
22 cursor.execute('select @_p3_0,@_p3_1,@_p3_2')
23 print(cursor.fetchall()) #能夠拿到的是返回值
建立有參的存儲過程(out)
 1 =============建立有參存儲過程之inout的使用==========
 2 delimiter //
 3 create procedure p4(
 4     inout m int
 5 )
 6 begin
 7     select * from test where id > m;
 8     set m=1;
 9 end //
10 delimiter ;
11 
12 #在mysql中
13 set @x=2;
14 call p4(@x);
15 select @x;
16 
17 ===========================
18 delimiter //
19 create procedure p5(
20     inout m int
21 )
22 begin
23     select * from test11111 where id > m;
24     set m=1;
25 end //
26 delimiter ;
27 
28 #在mysql中
29 set @x=2;
30 call p5(@x);
31 select @x;  #這時因爲不存在那個表就會報錯,查看的結果就成2了。
建立有參存儲過程之inout的使用
-- 無參數
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中執行存儲過程

補充:程序與數據庫結合使用的三種方式

#方式一:
    MySQL:存儲過程
    程序:調用存儲過程

#方式二:
    MySQL:
    程序:純SQL語句

#方式三:
    MySQL:
    程序:類和對象,即ORM(本質仍是純SQL語句)

 

import pymysql
conn = pymysql.connect(host = 'localhost',user = 'root',password='123456',database = 'lianxi',charset = 'utf8')
cursor = conn.cursor(pymysql.cursors.DictCursor)  #以字典的形式輸出
# rows = cursor.callproc('p1')  #1.調用存儲過程的方法 ,沒參數時
# rows = cursor.callproc('p2',args=(3,7))  #有參數時
rows = cursor.callproc('p3', args=(3,7,123))  #@_p3_0=3,@_p3_1=7 ,@_p3_2=123  #有參數時
conn.commit()  #執行
print(cursor.fetchall())
cursor.execute('select @_p3_0,@_p3_1,@_p3_2')
print(cursor.fetchall())
cursor.close()
conn.close()

刪除存儲過程

drop procedure proc_name;

5、函數

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

  1 CHAR_LENGTH(str)
  2         返回值爲字符串str 的長度,長度的單位爲字符。一個多字節字符算做一個單字符。
  3         對於一個包含五個二字節字符集, LENGTH()返回值爲 10, 而CHAR_LENGTH()的返回值爲5。
  4 
  5     CONCAT(str1,str2,...)
  6         字符串拼接
  7         若有任何一個參數爲NULL ,則返回值爲 NULL。
  8     CONCAT_WS(separator,str1,str2,...)
  9         字符串拼接(自定義鏈接符)
 10         CONCAT_WS()不會忽略任何空字符串。 (然而會忽略全部的 NULL)。
 11 
 12     CONV(N,from_base,to_base)
 13         進制轉換
 14         例如:
 15             SELECT CONV('a',16,2); 表示將 a 由16進制轉換爲2進制字符串表示
 16 
 17     FORMAT(X,D)
 18         將數字X 的格式寫爲'#,###,###.##',以四捨五入的方式保留小數點後 D 位, 並將結果以字符串的形式返回。若  D 爲 0, 則返回結果不帶有小數點,或不含小數部分。
 19         例如:
 20             SELECT FORMAT(12332.1,4); 結果爲: '12,332.1000'
 21     INSERT(str,pos,len,newstr)
 22         在str的指定位置插入字符串
 23             pos:要替換位置其實位置
 24             len:替換的長度
 25             newstr:新字符串
 26         特別的:
 27             若是pos超過原字符串長度,則返回原字符串
 28             若是len超過原字符串長度,則由新字符串徹底替換
 29     INSTR(str,substr)
 30         返回字符串 str 中子字符串的第一個出現位置。
 31 
 32     LEFT(str,len)
 33         返回字符串str 從開始的len位置的子序列字符。
 34 
 35     LOWER(str)
 36         變小寫
 37 
 38     UPPER(str)
 39         變大寫
 40 
 41     LTRIM(str)
 42         返回字符串 str ,其引導空格字符被刪除。
 43     RTRIM(str)
 44         返回字符串 str ,結尾空格字符被刪去。
 45     SUBSTRING(str,pos,len)
 46         獲取字符串子序列
 47 
 48     LOCATE(substr,str,pos)
 49         獲取子序列索引位置
 50 
 51     REPEAT(str,count)
 52         返回一個由重複的字符串str 組成的字符串,字符串str的數目等於count 。
 53         若 count <= 0,則返回一個空字符串。
 54         若str 或 count 爲 NULL,則返回 NULL 。
 55     REPLACE(str,from_str,to_str)
 56         返回字符串str 以及全部被字符串to_str替代的字符串from_str 。
 57     REVERSE(str)
 58         返回字符串 str ,順序和字符順序相反。
 59     RIGHT(str,len)
 60         從字符串str 開始,返回從後邊開始len個字符組成的子序列
 61 
 62     SPACE(N)
 63         返回一個由N空格組成的字符串。
 64 
 65     SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
 66         不帶有len 參數的格式從字符串str返回一個子字符串,起始於位置 pos。帶有len參數的格式從字符串str返回一個長度同len字符相同的子字符串,起始於位置 pos。 使用 FROM的格式爲標準 SQL 語法。也可能對pos使用一個負值。倘若這樣,則子字符串的位置起始於字符串結尾的pos 字符,而不是字符串的開頭位置。在如下格式的函數中能夠對pos 使用一個負值。
 67 
 68         mysql> SELECT SUBSTRING('Quadratically',5);
 69             -> 'ratically'
 70 
 71         mysql> SELECT SUBSTRING('foobarbar' FROM 4);
 72             -> 'barbar'
 73 
 74         mysql> SELECT SUBSTRING('Quadratically',5,6);
 75             -> 'ratica'
 76 
 77         mysql> SELECT SUBSTRING('Sakila', -3);
 78             -> 'ila'
 79 
 80         mysql> SELECT SUBSTRING('Sakila', -5, 3);
 81             -> 'aki'
 82 
 83         mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
 84             -> 'ki'
 85 
 86     TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str)
 87         返回字符串 str , 其中全部remstr 前綴和/或後綴都已被刪除。若分類符BOTH、LEADIN或TRAILING中沒有一個是給定的,則假設爲BOTH 。 remstr 爲可選項,在未指定狀況下,可刪除空格。
 88 
 89         mysql> SELECT TRIM('  bar   ');
 90                 -> 'bar'
 91 
 92         mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');
 93                 -> 'barxxx'
 94 
 95         mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');
 96                 -> 'bar'
 97 
 98         mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');
 99                 -> 'barx'
100 
101 部份內置函數
部份內置函數

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

1.自定義函數

#!!!注意!!!
#函數中不要寫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 ;
delimiter //
create function f5(
    i int
)
returns int
begin
    declare res int default 0;
    if i = 10 then
        set res=100;
    elseif i = 20 then
        set res=200;
    elseif i = 30 then
        set res=300;
    else
        set res=400;
    end if;
    return res;
end //
delimiter ;

2.刪除函數

drop function func_name;

3.執行函數

# 獲取返回值
select UPPER('egon') into @res;
SELECT @res;
select f1(1,2) into @res;
select @res
# 在查詢中使用

select f1(11,nid) ,name from tb2;
 
   
 1 #1 基本使用
 2 mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
 3         -> 'Sunday October 2009'
 4 mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
 5         -> '22:23:00'
 6 mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
 7     ->                 '%D %y %a %d %m %b %j');
 8         -> '4th 00 Thu 04 10 Oct 277'
 9 mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
10     ->                 '%H %k %I %r %T %S %w');
11         -> '22 22 10 10:23:00 PM 22:23:00 00 6'
12 mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
13         -> '1998 52'
14 mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
15         -> '00'
16 
17 
18 #2 準備表和記錄
19 CREATE TABLE blog (
20     id INT PRIMARY KEY auto_increment,
21     NAME CHAR (32),
22     sub_time datetime
23 );
24 
25 INSERT INTO blog (NAME, sub_time)
26 VALUES
27     ('第1篇','2015-03-01 11:31:21'),
28     ('第2篇','2015-03-11 16:31:21'),
29     ('第3篇','2016-07-01 10:21:31'),
30     ('第4篇','2016-07-22 09:23:21'),
31     ('第5篇','2016-07-23 10:11:11'),
32     ('第6篇','2016-07-25 11:21:31'),
33     ('第7篇','2017-03-01 15:33:21'),
34     ('第8篇','2017-03-01 17:32:21'),
35     ('第9篇','2017-03-01 18:31:21');
36 
37 #3. 提取sub_time字段的值,按照格式後的結果即"年月"來分組
38 SELECT DATE_FORMAT(sub_time,'%Y-%m'),COUNT(1) FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m');
39 
40 #結果
41 +-------------------------------+----------+
42 | DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) |
43 +-------------------------------+----------+
44 | 2015-03                       |        2 |
45 | 2016-07                       |        4 |
46 | 2017-03                       |        3 |
47 +-------------------------------+----------+
48 rows in set (0.00 sec)
DATE_FORMAT須要掌握

6、流程控制

1.條件語句

舉例一

 

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 ;

 

舉例二 

#函數中不要寫sql語句,它僅僅只是一個功能,是一個在sql中被應用的功能
#若要想在begin...end...中寫sql,請用存儲過程
delimiter //
create function f5(
    i int
)
returns int
begin
    declare res int default 0;
    if i = 10 then
        set res=100;
    elseif i = 20 then
        set res=200;
    elseif i = 30 then
        set res=300;
    else
        set res=400;
    end if;
    return res;
end //
delimiter ;

 

2.循環語句

 1 delimiter //
 2 CREATE PROCEDURE proc_while ()
 3 BEGIN
 4 
 5     DECLARE num INT ;
 6     SET num = 0 ;
 7     WHILE num < 10 DO
 8         SELECT
 9             num ;
10         SET num = num + 1 ;
11     END WHILE ;
12 
13 END //
14 delimiter ;
while
 1 delimiter //
 2 CREATE PROCEDURE proc_repeat ()
 3 BEGIN
 4 
 5     DECLARE i INT ;
 6     SET i = 0 ;
 7     repeat
 8         select i;
 9         set i = i + 1;
10         until i >= 5
11     end repeat;
12 
13 END //
14 delimiter ;
repeat
 1 BEGIN
 2     
 3     declare i int default 0;
 4     loop_label: loop
 5         
 6         set i=i+1;
 7         if i<8 then
 8             iterate loop_label;
 9         end if;
10         if i>=10 then
11             leave loop_label;
12         end if;
13         select i;
14     end loop loop_label;
15 
16 END
loop
相關文章
相關標籤/搜索