MySql—檢視、函式、儲存過程、觸發器
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$