MySql高級—視圖、函數、存儲過程、觸發器
目錄
一、視圖
1、視圖的定義
視圖的定義:
視圖是由查詢結果形成的一張虛擬表,是表通過某種運算得到的一個投影。
同一張表可以創建多個視圖
創建視圖的語法:
create view view_name as select 語句
說明:
(1)視圖名跟表名是一個級別的名字,隸屬於數據庫;
(2)該語句的含義可以理解為:就是將該select命名為該名字(視圖名);
(3)視圖也可以設定自己的字段名,而不是select語句本身的字段名——通常不設置。
(4)視圖的使用,幾乎跟表一樣!
2、視圖的作用
(1)可以簡化查詢。
案例1:查詢平均價格前3高的欄目。
傳統的sql語句寫法
select cat_id,avg(shop_price) as pj from ecs_goods group by cat_id order by pj desc limit 3;
創建一個視圖,簡化查詢。
語法:
create view ecs_goods_v1 as select cat_id,avg(shop_price) pj from ecs_goods group by cat_id;
查詢平均價格前3高的欄目,我們只需查詢視圖即可。
案例2:查詢出商品表,以及所在的欄目名稱;
傳統寫法:
select goods_id,goods_name,b.cat_name,shop_price from ecs_goods a left join ecs_category b on a.cat_id=b.cat_id;
創建一個視圖
create view ecs_goods_v2 as select goods_id,goods_name,b.cat_name,shop_price from ecs_goods a left join ecs_category b on a.cat_id=b.cat_id;
查詢視圖;
select * from ecs_goods_v2;
(2)可以進行權限控制
把表的權限封閉,但是開放相應的視圖權限,視圖里只開放部分數據,比如某張表,用戶表為例,2個網站搞合作,可以查詢對方網站的用戶,需要向對方開放用戶表的權限,但是呢,又不想開放用戶表中的密碼字段。
再比如一個goods表,兩個網站搞合作,可以相互查詢對方的商品表,比如進貨價格字段不能讓對方查看。
第一步:創建一個視圖,視圖中不能包含in_price字段。
語法: create view goods_v1 as select id,goods_name,shop_price from goods;
第二步:創建一個用戶,授予查詢權限,只能操作goods_v1表(視圖)
語法:grant select on php.goods_v1 to 'xiaotian'@'%' identified by '1234456';
第三步:我們就測試一下,
3、查詢視圖
語法:select * from 視圖名 [where 條件]
視圖和表一樣,可以添加where 條件
4、修改視圖
alter view view_name as select XXXX
5、刪除視圖
drop view 視圖名稱
6、查看視圖結構
和表一樣的,語法,desc 視圖名稱
7、查看所有視圖
和表一樣,語法:show tables;
注意:沒有show views語句;
8、視圖與表的關系
視圖是表的查詢結果,自然表的數據改變了,影響視圖的結果。
(1)視圖的數據與表的數據一一對應時,可以修改。
對視圖的修改,也會影響到數據庫表的修改。
(2)視圖增刪改也會影響表,但是視圖並不是總是能增刪改的。
create view lmj as select cat_id,max(shop_price) as lmj from goods group by cat_id;
mysql> update lmj set lmj=1000 where cat_id=4;
ERROR 1288 (HY000): The target table lmj of the UPDATE is not updatable
注意:視圖的數據與表的數據一一對應時,可以修改,
(3)對於視圖insert還應注意,視圖必須包含表中沒有默認值的列。
以上對user_v1的操作,就類似於,如下sql語句的操作,原因就是age字段沒有默認值,我們里面,沒有包含age字段,
create table user(
id int not null,
name varchar(32) not null,
age tinyint not null
)engine myisam charset utf8;
insert into user values(12,'xiaofan');
注意:一般來說,視圖只是用來查詢的,不應該執行增刪改的操作。
9、視圖算法
algorithm= merge/temptable/undefined
merge:當引用視圖時,引用視圖的語句與定義視圖的語句合並(默認)。
temptable:當引用視圖時,根據視圖的創建語句建立一個臨時表。
undefined:未定義,自動讓系統幫你選。
merge:意味着,視圖只是一個語句規則,當查詢視圖時,把查詢視圖的語句(比如where那些)與創建時的語句where子句等合並,
分析,形成一條 select語句。
temptable:是根據創建語句瞬間創建一張臨時表,然后查詢視圖的語句,從該臨時表查數據。
比如如下視圖的算法為merge
#在創建視圖時的語句:where shop_price>1000;
#查詢視圖時,where shop_price<3000;
#那么查此視圖時,真正發生的是where (select where) and (view where)
#where shop_price <3000 and shop_price >1000;
#分析出最終語句還是去查goods表
二、SQL 編程
1、變量聲明
(1)會話變量
定義形式:
set @變量名 = 值;
說明:
1,跟php類似,第一次給其賦值,就算定義了
2,它可以在編程環境和非編程環境中使用!
3,使用的任何場合也都帶該"@"符號。
(2)普通變量
定義形式:
declare 變量名 類型 【default 默認值】;
說明:
1、它必須先聲明(即定義),此時也可以賦值;
2、賦值跟會話變量一樣: set 變量名 = 值;
3、它只能在編程環境中使用!!!
說明:什么是編程環境?
存儲過程,函數,觸發器就是編程環境
(3)變量賦值形式
語法1:
set 變量名 = 表達式;#此語法中的變量必須先使用declare聲明
語法2:
set @變量名=表達式;
#此方式可以無需declare語法聲明,而是直接賦值,類似php定義變量並賦值。
語法3:
select @變量名:=表達式;
#此語句會給該變量賦值,同時還會作為一個select語句輸出'結果集'。
語法4:
select 表達式 into @變量名;#此語句雖然看起來是select語句,但其實並不輸出'結果集',而是給變量賦值。
2、運算符
(1)算術運算符
+、-、*、/、%
注意:mysql沒有++和—運算符
(2)關系運算符
>、>=、<、<=、=(等於)、<>(不等於)!=(不等於)
(3)邏輯運算符
and(與)、or(或)、not(非)
3、語句塊包含符
所謂語句塊包含符,在js或php中,以及絕大部分的其他語言中,都是大括號:{}
它用在很多場合:if, switch, for, function
而mysql編程中的語句塊包含符是。
4、if判斷
MySQL支持兩種判斷,第一個是if判斷,第二個 case判斷
if語法
單分支
if 條件 then
//代碼
end if;
雙分支
if 條件 then
代碼1
else
代碼2
end if;
多分支
if 條件 then
代碼1
elseif 條件 then
代碼2
else
代碼3
end if;
案例:接收4個數字,
如果輸入1則輸出春天,2=》夏天 3=》秋天 4 =》冬天 其他數字=》出錯
我們使用存儲過程來體驗if語句的用法,
create procedure 存儲過程名(參數,參數,…)
begin
//代碼
end
注意:通常情況下,";"表示SQL語句結束,同時向服務器提交並執行。但是存儲過程中有很多SQL語句,每一句都要以分號隔開,這時候我們就需要使用其他符號來代替向服務器提交的命令。通過delimiter命令更改語句結束符。
具體的語句;
create procedure p1 (n int)
begin
if n=1 then
select '春天' as '季節';
elseif n=2 then
select '夏天' as '季節';
elseif n=3 then
select '秋天' as '季節';
elseif n=4 then
select '冬天' as '季節';
else
select '無法無天' as '季節';
end if;
end$
調用:語法:call 存儲過程的名稱(參數)
5、case判斷
case 變量
when值 then 語句;
when值 then 語句;
else 語句;
end case ;
案例:接收4個數字,
如果輸入1則輸出春天,2=》夏天 3=》秋天 4 =》冬天 其他數字=》出錯
create procedure p2 (n int)
begin
case n
when 1 then select '春天天' as '季節';
when 2 then select '夏天天' as '季節';
when 3 then select '秋天天' as '季節';
when 4 then select '冬天天' as '季節';
else select '無法無天' as '季節';
end case;
end$
6、循環
MySQL支持的循環有loop、while、repeat循環
(1)loop循環
標簽名:loop
leave 標簽名 --退出循環
end loop;
(2)while 循環
[標簽:]while 條件 do
//代碼
end while;
(3) repeat 循環
repeat
//代碼
until 條件 end repeat;
三、存儲過程
1、概念
存儲過程(procedure)
概念類似於函數,就是把一段代碼封裝起來,當要執行這一段代碼的時候,可以通過調用該存儲過程來實現。在封裝的語句體里面,
可以同if/else ,case,while等控制結構。
可以進行sql編程。
查看現有的存儲過程。
show procedure status
2、存儲過程的優點
存儲過程(Stored Procedure)是在大型數據庫系統中,一組為了完成特定功能的SQL 語句集,存儲在數據庫中,經過第一次編譯后再次調用不需要再次編譯,用戶通過指定存儲過程的名字並給出參數(如果該存儲過程帶有參數)來執行它。存儲過程是數據庫中的一個重要對象,任何一個設計良好的數據庫應用程序都應該用到存儲過程。
(1)存儲過程只在創造時進行編譯,以后每次執行存儲過程都不需再重新編譯,而一般SQL語句每執行一次就編譯一次,所以使用存儲過程可提高數據庫執行速度。
(2)當對數據庫進行復雜操作時(如對多個表進行Update,Insert,Query,Delete時),可將此復雜操作用存儲過程封裝起來與數據庫提供的事務處理結合一起使用。
(3)存儲過程可以重復使用,可減少數據庫開發人員的工作量
(4)安全性高,可設定只有某些用戶才具有對指定存儲過程的使用權
3、創建存儲過程
create procedure 存儲過程名(參數,參數,…)
begin
//代碼
end
存儲過程的參數分為輸入參數(in)、輸出參數(out)、輸入輸出參數(inout),默認是輸入參數。
如果存儲過程中就一條語句,begin和end是可以省略的。
說明:
(1)存儲過程中,可有各種編程元素:變量,流程控制,函數調用;
(2)還可以有:增刪改查等各種mysql語句;
(3)其中select(或show,或desc)會作為存儲過程執行后的"結果集"返回;
(4)形參可以設定數據的"進出方向":
案例1:查詢一個表里面某些語句
調用結果:
案例2:第二個存儲過程體會參數
輸入一個字符串,如果等於h則取出價格大於1000的商品,輸入其他的值,則輸出小於1000的商品。
create procedure p11(str char(1))
begin
if str='h' then
select goods_id,goods_name,shop_price from ecs_goods where shop_price>1000;
else
select goods_id,goods_name,shop_price from ecs_goods where shop_price<=1000;
end if;
end$
4、調用存儲過程
語法:call 存儲過程()
mysql_query("call存儲過程名稱()")
5、刪除存儲過程
語法:drop procedure [if exists] 存儲過程名
6、創建復雜的存儲過程
案例1:體會循環,計算1到n的和:
案例2:帶輸出參數的存儲過程
案例3:帶有輸入輸出參數的存儲過程
7、declare聲明局部變量
在編程環境中使用。
用戶變量只要在前面加一個@符即可
set @name='李白';
select @name;
8、系統變量
MySQL啟動的時候就存在的變量,以@@開頭的都是系統變量
select @@version$
四、函數
1、自定義函數
它跟php或js中的函數幾乎一樣:需要先定義,然后調用(使用)。
只是規定,這個函數,必須要返回數據——要有返回值
(1)定義語法:
create function 函數名(參數,參數的類型) returns 返回值類型
begin
//代碼
end
說明:
(1)函數內部可以有各種編程語言的元素:變量,流程控制,函數調用;
(2)函數內部可以有增刪改等語句!
(3)但:函數內部不可以有select(或show或desc)這種返回結果集的語句!
(2)調用
跟系統函數調用一樣:任何需要數據的位置,都可以調用該函數。
案例1:返回兩個數的和
案例2:定義一個函數,返回1到n的和。
注意:創建的函數,是隸屬於數據庫的,只能在創建函數的數據庫中使用。
2、系統函數
(1)數字類
mysql> select rand();//返回0到1間的隨機數
mysql>select * from it_goods order by rand() limit 2;//隨機取出2件商品
mysql>select floor(3.9)//輸出3
mysql>select ceil(3.1)//輸出4
mysql>select round(3.5)//輸出4四舍五入
(2)大小寫轉換
mysql> select ucase('I am a boy!') // --轉成大寫
mysql> select lcase('I am a boy!') // --轉成小寫
(3)截取字符串
mysql> select left('abcde',3)// --從左邊截取
mysql> select right('abcde',3) // --從右邊截取
mysql> select substring('abcde',2,3)// --從第二個位置開始,截取3個,注意:位置從1開始
mysql> select concat(10,':鋤禾日當午')// --字符串相連
mysql> select coalesce(null,123);
coalesce(str1,str2):如果第str1為null,就顯示str2
mysql> select stuname,stusex,coalesce(writtenexam,'缺考'), coalesce(labexam,'缺考') from stuinfo natural left join stumarks//
mysql> select length('鋤禾日當午') // 輸出10 顯示字節的個數
mysql> select char_length('鋤禾日當午') // 輸出5 顯示字符的個數
mysql> select length(trim(' abc ')) // trim用來去字符串兩邊空格
mysql> select replace('abc','bc','pache')// 將bc替換成pache
(4)時間類
mysql> select unix_timestamp()// --時間戳
mysql> select from_unixtime(unix_timestamp()) // --將時間戳轉成日期格式
curdate();返回今天的時間日期:
mysql> select now()// --取出當前時間
mysql>select year(now()) 年,month(now()) 月 ,day(now()) 日, hour(now()) 小時,minute(now()) 分鍾,second(now()) 秒//
mysql>select datediff(now(),'1997-7-1') // 兩個日期相距多少天
if(表達式,值1,值2):類似於三元運算符
mysql> select concat(10,if(10%2=0,'偶數','奇數'))//
案例1:比如一個電影網站,求出今天添加的電影;在添加電影時,有一個添加的時間戳。
//curdate()求出今天的日期
//把添加的時間戳,轉換成日期
select title from dede_archives where curdate()=from_unixtime(senddate,'%Y-%m-%d')$
案例2:比如一個電影網站,求出昨天添加的電影;在添加電影時,有一個添加的時間戳。
思路:
把添加的時間戳,轉換成日期,和昨天的日期比較,
問題?如何求出昨天的日期,
擴展,如何取出昨天或者指定某個時間的電影:
date_sub和date_add函數:
基本用法:
date_sub(時間日期時間,interval 數字 時間單位)
說明:
(1)時間單位:可以是year month day hour minute second
(2)數字:可以是正數和負數。
比如:取出昨天的日期:
select date_sub(curdate(),interval 1 day)
或
select date_add(curdate(),interval -1 day)
比如:取出前天的日期:
select date_sub(curdate(),interval 2 day)
或
select date_add(curdate(),interval -2 day)
比如:取出后天的日期:
select date_sub(curdate(),interval -1 day)
或
select date_add(curdate(),interval 1 day)
回歸案例:
比如一個電影網站,求出昨天添加的電影;在添加電影時,有一個添加的時間戳。
五、觸發器
1、簡介
(1)觸發器是一個特殊的存儲過程,它是MySQL在insert、update、delete的時候自動執行的代碼塊。
(2)觸發器必須定義在特定的表上。
(3)自動執行,不能直接調用,
作用:監視某種情況並觸發某種操作。
觸發器的思路:
監視it_order表,如果it_order表里面有增刪改的操作,則自動觸發it_goods里面里面增刪該的操作。
比如新添加一個訂單,則it_goods表,就自東減少對應商品的庫存。
比如取消一個訂單,則it_goods表,就自動增加對應商品的庫存減少的庫存。
2、觸發器四要素
3、創建觸發器
創建觸發器的語法:
create trigger trigger_name
after/before insert /update/delete on 表名
for each row
begin
sql語句:(觸發的語句一句或多句)
end
案例1:第一個觸發器,購買一個饅頭,減少1個庫存。
分析:
監視的地點: it_order表
監視的事件: it_order表的inset操作。
觸發的時間: it_order表的inset之后
觸發的事件 it_goods表減少庫存的操作。
create trigger t1
after insert on it_order
for each row
begin
update it_goods set goods_number=goods_number-1 where id=2;
end$
測試效果如下:
上面我們創建的觸發器t1是有問題,我們購買任何商品都是減少饅頭的庫存。
案例2:購買商品,減少對應庫存 ,
create trigger t2
after insert on it_order
for each row
begin
update it_goods set goods_number=goods_number-new.much where id=new.goods_id;
end$
注意:如果在觸發器中引用行的值。
對於insert 而言,新增的行用new來表示,行中的每一列的值,用 new.列名 來表示。
測試效果如下:
案例3:取消訂單時,減掉的庫存要添加回來。
分析:
監視的地點: it_order表
監視的事件: it_order表的delete操作
觸發的時間: it_order表的delete操作之后
觸發的事件: it_goods表,把減掉的庫存恢復過來。
create trigger t3
after delete on it_order
for each row
begin
update it_goods set goods_number=goods_number+old.much where id=old.goods_id;
end$
注意:對應it_order表,刪除的行我們使用old來表示,如果要引用里面的數據,則使用
old.列名 來表示。
案例4:修改訂單時,庫存也要做對應修改.(可以修改購買數量,也可以修改類型)
監視的地點: it_order表
監視的事件: it_order表的update操作
觸發的時間: it_order表的update操作之后
觸發的事件: it_goods表,要修改對應的庫存。
create trigger t4
after update on it_order
for each row
begin
update it_goods set goods_number=goods_number+old.much where id=old.goods_id;
update it_goods set goods_number=goods_number-new.much where id=new.goods_id;
end$
完成修改的思路:
(1)撤銷訂單,it_goods表里面的庫存要恢復。
(2)重新下訂單,it_goods表里面的庫存要減少
注意:如果是修改操作,要引用it_order表里面的值,
修改前的數據,用old來表示,old.列名 引用被修改之前行中的值。
修改后的數據,用new來表示,new.列名 引用被修改之后行中的值。
4、刪除觸發器
語法:drop trigger 觸發器的名稱
5、查看觸發器
語法: show triggers
6、before與after的區別
after是先完成數據的增刪改,再觸發,觸發器中的語句晚於監視的增刪改,無法影響前面的增刪該動作。
就類似於先吃飯,再付錢。
before是先完成觸發,再增刪改,觸發的語句先於監視的增刪改發生,我們有機會判斷修改即將發生的操作。
就類似於先付錢,再吃飯
典型案例:對於已下的訂單,進行判斷,如果訂單的數量>5,就認為是惡意訂單,強制把所定的商品數量改成5
分析:
監視的地點:it_order表
監視的事件:it_order表的insert操作
觸發的時間:it_order表的insert操作之前。
觸發的事件:如果購買數量大於5,把購買數量改成5。
create trigger t5
before insert on it_order
for each row
begin
if new.much>5 then
set new.much=5;
end if;
end$
目前mysql不支持多個具有同一個動作,同一時間,同一事件,同一地點,的觸發器
create trigger t10
after delete on it_order
for each row
begin
update it_goods set goods_number=goods_number+old.much where id=old.goods_id;
end$
