MySQL存储过程+游标+触发器
生活随笔
收集整理的這篇文章主要介紹了
MySQL存储过程+游标+触发器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【0】README
0.1)本文旨在 arrange mysql?存儲過程及如何在存儲中使用游標 ?的相關知識;
0.2)delimieter的用法:參見?http://blog.csdn.net/pacosonswjtu/article/details/51407756;
【1】存儲過程基礎 1)intro to procedure:簡單來說,存儲過程就是為以后的使用而保存的一條或多條MySQL 語句的集合。可將其視為 批文件; 2)使用存儲過程的理由(reasons): r1)通過把處理封裝在容易使用的單元中,簡化復雜的操作 ; r2)提高性能:因為使用存儲過程比使用單獨的SQL語句要快; r3)安全性:通過存儲過程限制對基礎數據的訪問減少了數據訛誤(無意識的或別的原因所導致的數據訛誤)的機會; r4)存在一些只能用在單個請求中的MySQL 元素和特性,存儲過程可以使用它們來編寫 功能更強大更靈活的代碼; 3)存儲過程帶來的缺陷(defects) d1)存儲過程的編寫比基本SQL語句更加復雜,編寫存儲過程需要更高的技能,更更豐富的經驗; d2)你可能沒有創建存儲過程的安全訪問權限。許多 admin 限制存儲過程的創建 權限,允許用戶使用存儲過程,但不允許他們創建存儲過程 ; Attention)MySQL 將編寫存儲過程的安全和訪問權限 和 執行存儲過程的安全和訪問權限區分開來。所以,即使你不能編寫自己的存儲過程,與可以在適當的時候執行別的存儲過程;
【2】存儲過程 1)執行存儲過程:執行過程稱為存儲過程的調用; 2)創建存儲過程:
problem+solution) problem:默認的MySQL 語句分隔符為;(分號),MySQL 命令行實用程序(mysql.exe)也使用;作為分隔符;如果命令行實用程序要解釋存儲過程自身內的; 字符,則它們最終不會稱為存儲過程 的一部分,這會使得存儲過程中的SQL 出現句法錯誤; solution:解決方法是 臨時更改命令行實用程序的語句分隔符,如下所示: delimiter // drop procedure if exists avg_price // create procedure avg_price() beginselect avg(price) as avg_price from product; end // delimiter ;
對以上代碼的分析(Analysis): A1)delimiter // :告訴命令行實用程序使用 // 作為新的語句結束分隔符,可以看到標志存儲過程結束的end 定義為 end // 而不是 end; A2)這樣在存儲過程體內的; 保持不動,并且正確地傳遞給數據庫引擎。最后為恢復為原來的 分隔符,可以使用 delimieter; A3)除了 '\' 符號外,任何字符都可以用作語句分隔符; 3)如何使用這個存儲過程? call procedure_name;
【2.1】在存儲過程中使用參數 1)一般存儲過程并不顯示結果,而是吧結果返回給你指定的變量; 2)變量:內存中一個特定的位置,用來臨時存儲數據; 3)看個荔枝
delimiter // drop procedure if exists min_avg_max_price // create procedure min_avg_max_price(out p1 decimal(8,2),out p2 decimal(8,2),out p3 decimal(8,2) ) beginselect min(price) into p1from product;select avg(price) into p2from product;select max(price) into p3from product; end // delimiter ; 對以上代碼的分析(Analysis): A1)該存儲過程接收3個參數: p1, p2, p3; A2)關鍵字out:指出了相應的參數用來從存儲過程中傳入一個值(返回給調用者);(也即是看做 參數的數據類型) A3)MySQL 支持3中參數類型: in(傳遞給存儲過程),out(從存儲過程傳遞給調用者) 和 inout (對存儲過程傳入和傳出); A4)存儲過程的執行代碼: 位于 begin 和 end 語句內;
4)再看個荔枝(in 和 out 參數并用) delimiter // drop procedure if exists order_sum // create procedure order_sum(in p_name varchar(30),in p_num int,out p_sum decimal(8,2) ) beginselect p_num*p.price into p_sum from product p where p.name=p_name; end // delimiter ; 對以上代碼的分析(Analysis):注意它們的in 和 out 參數類型就是;
【2.2】建立智能存儲過程 1)只有在存儲過程內包含業務規則和智能處理時,存儲過程 的 威力才真正顯現出來; 2)任務需求(requirements):返回合計(帶稅和不帶稅); delimiter // drop procedure if exists tax_order_sum // create procedure tax_order_sum(in taxable boolean,in p_name varchar(30),in p_num int,out p_sum decimal(8,2) )comment 'return order sum with and without tax' begin-- declare variable for tax_order_sum.-- attention for the difference between variable and parameter.declare taxrate int default 6;select p_num*p.price into p_sum from product p where p.name=p_name;if taxable thenselect p_sum + p_sum*taxrate/100 into p_sum;end if; end // delimiter ; 對上述代碼的分析(Analysis): A1)增加了注釋 comment; A2)添加了另外一個參數 taxable,是否交稅;(繳稅) A3)declare用于聲明局部變量;(干貨——注意參數和變量的區別); A4)上述代碼還使用到了 if end if 語句; A5)boolean 類型的參數:0表假,1表真; supplement)檢查存儲過程: show create procedure tax_order_sum;
【3】使用游標 1)mysql 檢索操作返回一組稱為結果集的行;有時候需要在檢索出來的行中前進或后退一行或多行,這就是使用游標的原因; 2)intro to cursor:游標是一個存儲在mysql 服務器上的數據庫查詢,他不是一條select語句, 而是被該語句檢索出來的結果集; 3)在存儲了游標之后,應用程序可以根據需要 滾動或瀏覽 其中的數據; Attention)游標只能用于存儲過程;(干貨——游標只能用于存儲過程)
【3.1】使用游標 1)使用游標涉及幾個steps: step1)在能夠使用游標前,必須聲明(定義)它; step2)一旦聲明后,必須打開游標以供使用; step3)對于填有數據的游標,根據需要取出(檢索)各行; step4)在結束游標使用時,必須關閉游標; Attention)在聲明游標后, 可根據需要 頻繁地打開和關閉游標。 在游標打開后, 可根據需要頻繁地執行取操作; 2)創建游標 -- create a temp table.drop table if exists temp_sum_pricecreate table temp_sum_price(id int,sum_price decimal(8,2));3)打開游標, 使用游標數據和關閉游標 -- open the cursor.open mycursor; -- declare variables.declare vendor_id int;declare sum_price decimal(8,2);-- use cursor data. fetch mycursor into vendor_id,sum_price;-- close the cursor.close mycursor; 4)看個荔枝:利用游標統計每個vendor供應商的產品單價的總和; delimiter // drop procedure if exists total_price_with_cursor;create procedure total_price_with_cursor( ) comment 'computing sum of price grouped by vendor' begin-- declare variables.declare vendor_id int;declare sum_price decimal(8,2);declare done boolean; -- declare the cursor.(highlight line)declare mycursor cursor forselect v.id, sum(price) from product p,vendor v where p.vendor=v.id group by vendor;-- declare continue handler.declare continue handler for sqlstate '02000' set done=1; -- create a temp table.drop table if exists temp_sum_price;create table temp_sum_price(vendor_id int ,sum_price decimal(8,2)); -- open the cursor.open mycursor; -- repeat to call fetch clauserepeatfetch mycursor into vendor_id,sum_price; -- (highlight line)insert into temp_sum_price values(vendor_id,sum_price);until done end repeat;-- close the cursor.close mycursor; end // delimiter ; 對以上代碼的分析(Analysis): A1)declare continue handler for sqlstate '02000' set done=1;這條語句定義了一個 continue handler, 它是在條件出現時被執行的代碼。 A2)上述代碼指出當,SQLSTATE ‘02000’出現時,set done=1。SQLSTATE ‘02000’ 是一個未找到的條件,當repeat 由于沒有更多的行提供循環而不能繼續時,出現這個條件; A3)declare 語句的次序:用declare 語句定義的局部變量必須在定義任意游標或句柄前定義,而句柄必須在游標后定義,不遵循此順序將產生錯誤消息;
【4】使用觸發器 1)intro to trigger:觸發器是mysql 響應以下任意語句而自動執行的一條mysql語句(或位于begin 和 end之間的一組語句); 2)語句有:delete,insert,update; 【4.1】創建觸發器 1)創建觸發器時,需要給出4條信息(information): i1)唯一的觸發器名; i2)觸發器關聯的表; i3)觸發器應該響應的活動(delete,insert或update); i4)觸發器何時執行(處理前或處理后); Attention)只有表才有觸發器,視圖不支持; 2)看個荔枝 drop trigger if exists t_insert_product; delimiter // create trigger t_insert_product after insert on productfor each row begininsert into temp_auto_increment values(null,new.id); end // delimiter ; 對以上代碼的分析(Analysis): A1)create trigger 用來創建名為 product的新觸發器;觸發器可以在一個操作發生之前或之后執行;這里給出了 after insert,所以此觸發器將在insert 語句成功執行后執行; A2)這個觸發器還指定了 for each row,因此代碼對每個插入行執行; Attention) A1)觸發器按照每個表每個事件每次地定義,每個表每個事件每次只允許一個觸發器。因此,每個表最多支持6個觸發器(每條insert, update,deleter的之前和之后); A2)單一觸發器不能與多個事件或多個表關聯,所以,如果你需要一個對 insert 和 update 操作執行的觸發器,則應該定義兩個觸發器;
【4.2】刪除觸發器 drop trigger if exists t_insert_product; 【4.3】使用觸發器
【4.3.1】insert觸發器 1)insert觸發器的使用需要知道以下幾點(points): p1)在insert觸發器代碼內,可以引用一個名為 new 的虛擬表,訪問被插入的行; p2)在 before insert 觸發器內,new 中的值也可以被更新(允許更改被插入的值); p3)對于 auto_increment列,new 在 insert執行之前包含0,在insert執行后包含新的自動生成值; 2)看個荔枝:在插入到 product后,執行 begin 和 end 里面的語句(在after insert ,向 temp_auto_increment表插入數據) drop trigger if exists after_insert_product; delimiter // create trigger after_insert_product after insert on productfor each row begininsert into temp_auto_increment values(null,new.id); end // delimiter ;
【4.3.2】delete 觸發器 1)delete 觸發器的使用需要知道以下幾點(points): p1)在delete觸發器代碼內,你可以引用一個名為 old 的虛擬表,訪問被刪除的行; p2)old中的值全部是只讀的,不能更新; drop trigger if exists after_delete_product; delimiter // create trigger after_delete_product after delete on productfor each row begininsert into temp_auto_increment values(null,old.id); end // delimiter ; 對以上代碼的分析(Analysis): A1)上面的代碼定義了一個觸發器 after_delete_trigger,用于在 delete 后觸發; A2)觸發事件的工作是向 temp_auto_increment 表中插入 刪除行的id;
【4.3.3】update觸發器 1)update 觸發器的使用需要知道以下幾點(points): p1)在update 觸發器代碼內,你可以引用一個名為 old的虛擬表訪問以前(update之前)的值,引用一個名為 new的虛擬表訪問新更新的值; p2)在before update 觸發器內,new 中的值可能也被更新(允許更改將要用于 update 語句中的值); p3)old中的值 全都是只讀的,不能更新; drop trigger if exists before_update_product; delimiter // create trigger before_update_product before update on productfor each row beginset new.name=upper(new.name); end // delimiter ; 對以上代碼的分析(Analysis): A1)以上代碼的定義了一個觸發器before_update_product?,在update 前觸發; A2)觸發事件的工作是 將update的所在行的name設置為 大寫;
Attention)intro 一些使用觸發器需要記住的重點; A1)與其他DBMS相比,MySQL5 中支持的觸發器相當初級; A2)創建觸發器可能需要特殊的安全訪問權限,但是,觸發器的執行是自動的。如果insert,update和delete語句能夠執行,則相關的觸發器也能執行; A3)應該用觸發器來保證數據的一致性(大小寫,格式等)。在觸發器中執行這些類型的處理的優點是它總是進行這種處理,而且是透明地進行,與client 引用無關; A4)觸發器的一種非常有意義的 使用是創建審計跟蹤。使用觸發器,把更改記錄到另一個表非常容易; A5)MySQL觸發器不支持call 語句,表明 不能從觸發器內調用存儲過程;(干貨——MySQL觸發器內不能調用存儲過程)
【1】存儲過程基礎 1)intro to procedure:簡單來說,存儲過程就是為以后的使用而保存的一條或多條MySQL 語句的集合。可將其視為 批文件; 2)使用存儲過程的理由(reasons): r1)通過把處理封裝在容易使用的單元中,簡化復雜的操作 ; r2)提高性能:因為使用存儲過程比使用單獨的SQL語句要快; r3)安全性:通過存儲過程限制對基礎數據的訪問減少了數據訛誤(無意識的或別的原因所導致的數據訛誤)的機會; r4)存在一些只能用在單個請求中的MySQL 元素和特性,存儲過程可以使用它們來編寫 功能更強大更靈活的代碼; 3)存儲過程帶來的缺陷(defects) d1)存儲過程的編寫比基本SQL語句更加復雜,編寫存儲過程需要更高的技能,更更豐富的經驗; d2)你可能沒有創建存儲過程的安全訪問權限。許多 admin 限制存儲過程的創建 權限,允許用戶使用存儲過程,但不允許他們創建存儲過程 ; Attention)MySQL 將編寫存儲過程的安全和訪問權限 和 執行存儲過程的安全和訪問權限區分開來。所以,即使你不能編寫自己的存儲過程,與可以在適當的時候執行別的存儲過程;
【2】存儲過程 1)執行存儲過程:執行過程稱為存儲過程的調用; 2)創建存儲過程:
problem+solution) problem:默認的MySQL 語句分隔符為;(分號),MySQL 命令行實用程序(mysql.exe)也使用;作為分隔符;如果命令行實用程序要解釋存儲過程自身內的; 字符,則它們最終不會稱為存儲過程 的一部分,這會使得存儲過程中的SQL 出現句法錯誤; solution:解決方法是 臨時更改命令行實用程序的語句分隔符,如下所示: delimiter // drop procedure if exists avg_price // create procedure avg_price() beginselect avg(price) as avg_price from product; end // delimiter ;
對以上代碼的分析(Analysis): A1)delimiter // :告訴命令行實用程序使用 // 作為新的語句結束分隔符,可以看到標志存儲過程結束的end 定義為 end // 而不是 end; A2)這樣在存儲過程體內的; 保持不動,并且正確地傳遞給數據庫引擎。最后為恢復為原來的 分隔符,可以使用 delimieter; A3)除了 '\' 符號外,任何字符都可以用作語句分隔符; 3)如何使用這個存儲過程? call procedure_name;
【2.1】在存儲過程中使用參數 1)一般存儲過程并不顯示結果,而是吧結果返回給你指定的變量; 2)變量:內存中一個特定的位置,用來臨時存儲數據; 3)看個荔枝
delimiter // drop procedure if exists min_avg_max_price // create procedure min_avg_max_price(out p1 decimal(8,2),out p2 decimal(8,2),out p3 decimal(8,2) ) beginselect min(price) into p1from product;select avg(price) into p2from product;select max(price) into p3from product; end // delimiter ; 對以上代碼的分析(Analysis): A1)該存儲過程接收3個參數: p1, p2, p3; A2)關鍵字out:指出了相應的參數用來從存儲過程中傳入一個值(返回給調用者);(也即是看做 參數的數據類型) A3)MySQL 支持3中參數類型: in(傳遞給存儲過程),out(從存儲過程傳遞給調用者) 和 inout (對存儲過程傳入和傳出); A4)存儲過程的執行代碼: 位于 begin 和 end 語句內;
4)再看個荔枝(in 和 out 參數并用) delimiter // drop procedure if exists order_sum // create procedure order_sum(in p_name varchar(30),in p_num int,out p_sum decimal(8,2) ) beginselect p_num*p.price into p_sum from product p where p.name=p_name; end // delimiter ; 對以上代碼的分析(Analysis):注意它們的in 和 out 參數類型就是;
【2.2】建立智能存儲過程 1)只有在存儲過程內包含業務規則和智能處理時,存儲過程 的 威力才真正顯現出來; 2)任務需求(requirements):返回合計(帶稅和不帶稅); delimiter // drop procedure if exists tax_order_sum // create procedure tax_order_sum(in taxable boolean,in p_name varchar(30),in p_num int,out p_sum decimal(8,2) )comment 'return order sum with and without tax' begin-- declare variable for tax_order_sum.-- attention for the difference between variable and parameter.declare taxrate int default 6;select p_num*p.price into p_sum from product p where p.name=p_name;if taxable thenselect p_sum + p_sum*taxrate/100 into p_sum;end if; end // delimiter ; 對上述代碼的分析(Analysis): A1)增加了注釋 comment; A2)添加了另外一個參數 taxable,是否交稅;(繳稅) A3)declare用于聲明局部變量;(干貨——注意參數和變量的區別); A4)上述代碼還使用到了 if end if 語句; A5)boolean 類型的參數:0表假,1表真; supplement)檢查存儲過程: show create procedure tax_order_sum;
【3】使用游標 1)mysql 檢索操作返回一組稱為結果集的行;有時候需要在檢索出來的行中前進或后退一行或多行,這就是使用游標的原因; 2)intro to cursor:游標是一個存儲在mysql 服務器上的數據庫查詢,他不是一條select語句, 而是被該語句檢索出來的結果集; 3)在存儲了游標之后,應用程序可以根據需要 滾動或瀏覽 其中的數據; Attention)游標只能用于存儲過程;(干貨——游標只能用于存儲過程)
【3.1】使用游標 1)使用游標涉及幾個steps: step1)在能夠使用游標前,必須聲明(定義)它; step2)一旦聲明后,必須打開游標以供使用; step3)對于填有數據的游標,根據需要取出(檢索)各行; step4)在結束游標使用時,必須關閉游標; Attention)在聲明游標后, 可根據需要 頻繁地打開和關閉游標。 在游標打開后, 可根據需要頻繁地執行取操作; 2)創建游標 -- create a temp table.drop table if exists temp_sum_pricecreate table temp_sum_price(id int,sum_price decimal(8,2));3)打開游標, 使用游標數據和關閉游標 -- open the cursor.open mycursor; -- declare variables.declare vendor_id int;declare sum_price decimal(8,2);-- use cursor data. fetch mycursor into vendor_id,sum_price;-- close the cursor.close mycursor; 4)看個荔枝:利用游標統計每個vendor供應商的產品單價的總和; delimiter // drop procedure if exists total_price_with_cursor;create procedure total_price_with_cursor( ) comment 'computing sum of price grouped by vendor' begin-- declare variables.declare vendor_id int;declare sum_price decimal(8,2);declare done boolean; -- declare the cursor.(highlight line)declare mycursor cursor forselect v.id, sum(price) from product p,vendor v where p.vendor=v.id group by vendor;-- declare continue handler.declare continue handler for sqlstate '02000' set done=1; -- create a temp table.drop table if exists temp_sum_price;create table temp_sum_price(vendor_id int ,sum_price decimal(8,2)); -- open the cursor.open mycursor; -- repeat to call fetch clauserepeatfetch mycursor into vendor_id,sum_price; -- (highlight line)insert into temp_sum_price values(vendor_id,sum_price);until done end repeat;-- close the cursor.close mycursor; end // delimiter ; 對以上代碼的分析(Analysis): A1)declare continue handler for sqlstate '02000' set done=1;這條語句定義了一個 continue handler, 它是在條件出現時被執行的代碼。 A2)上述代碼指出當,SQLSTATE ‘02000’出現時,set done=1。SQLSTATE ‘02000’ 是一個未找到的條件,當repeat 由于沒有更多的行提供循環而不能繼續時,出現這個條件; A3)declare 語句的次序:用declare 語句定義的局部變量必須在定義任意游標或句柄前定義,而句柄必須在游標后定義,不遵循此順序將產生錯誤消息;
【4】使用觸發器 1)intro to trigger:觸發器是mysql 響應以下任意語句而自動執行的一條mysql語句(或位于begin 和 end之間的一組語句); 2)語句有:delete,insert,update; 【4.1】創建觸發器 1)創建觸發器時,需要給出4條信息(information): i1)唯一的觸發器名; i2)觸發器關聯的表; i3)觸發器應該響應的活動(delete,insert或update); i4)觸發器何時執行(處理前或處理后); Attention)只有表才有觸發器,視圖不支持; 2)看個荔枝 drop trigger if exists t_insert_product; delimiter // create trigger t_insert_product after insert on productfor each row begininsert into temp_auto_increment values(null,new.id); end // delimiter ; 對以上代碼的分析(Analysis): A1)create trigger 用來創建名為 product的新觸發器;觸發器可以在一個操作發生之前或之后執行;這里給出了 after insert,所以此觸發器將在insert 語句成功執行后執行; A2)這個觸發器還指定了 for each row,因此代碼對每個插入行執行; Attention) A1)觸發器按照每個表每個事件每次地定義,每個表每個事件每次只允許一個觸發器。因此,每個表最多支持6個觸發器(每條insert, update,deleter的之前和之后); A2)單一觸發器不能與多個事件或多個表關聯,所以,如果你需要一個對 insert 和 update 操作執行的觸發器,則應該定義兩個觸發器;
【4.2】刪除觸發器 drop trigger if exists t_insert_product; 【4.3】使用觸發器
【4.3.1】insert觸發器 1)insert觸發器的使用需要知道以下幾點(points): p1)在insert觸發器代碼內,可以引用一個名為 new 的虛擬表,訪問被插入的行; p2)在 before insert 觸發器內,new 中的值也可以被更新(允許更改被插入的值); p3)對于 auto_increment列,new 在 insert執行之前包含0,在insert執行后包含新的自動生成值; 2)看個荔枝:在插入到 product后,執行 begin 和 end 里面的語句(在after insert ,向 temp_auto_increment表插入數據) drop trigger if exists after_insert_product; delimiter // create trigger after_insert_product after insert on productfor each row begininsert into temp_auto_increment values(null,new.id); end // delimiter ;
【4.3.2】delete 觸發器 1)delete 觸發器的使用需要知道以下幾點(points): p1)在delete觸發器代碼內,你可以引用一個名為 old 的虛擬表,訪問被刪除的行; p2)old中的值全部是只讀的,不能更新; drop trigger if exists after_delete_product; delimiter // create trigger after_delete_product after delete on productfor each row begininsert into temp_auto_increment values(null,old.id); end // delimiter ; 對以上代碼的分析(Analysis): A1)上面的代碼定義了一個觸發器 after_delete_trigger,用于在 delete 后觸發; A2)觸發事件的工作是向 temp_auto_increment 表中插入 刪除行的id;
【4.3.3】update觸發器 1)update 觸發器的使用需要知道以下幾點(points): p1)在update 觸發器代碼內,你可以引用一個名為 old的虛擬表訪問以前(update之前)的值,引用一個名為 new的虛擬表訪問新更新的值; p2)在before update 觸發器內,new 中的值可能也被更新(允許更改將要用于 update 語句中的值); p3)old中的值 全都是只讀的,不能更新; drop trigger if exists before_update_product; delimiter // create trigger before_update_product before update on productfor each row beginset new.name=upper(new.name); end // delimiter ; 對以上代碼的分析(Analysis): A1)以上代碼的定義了一個觸發器before_update_product?,在update 前觸發; A2)觸發事件的工作是 將update的所在行的name設置為 大寫;
Attention)intro 一些使用觸發器需要記住的重點; A1)與其他DBMS相比,MySQL5 中支持的觸發器相當初級; A2)創建觸發器可能需要特殊的安全訪問權限,但是,觸發器的執行是自動的。如果insert,update和delete語句能夠執行,則相關的觸發器也能執行; A3)應該用觸發器來保證數據的一致性(大小寫,格式等)。在觸發器中執行這些類型的處理的優點是它總是進行這種處理,而且是透明地進行,與client 引用無關; A4)觸發器的一種非常有意義的 使用是創建審計跟蹤。使用觸發器,把更改記錄到另一個表非常容易; A5)MySQL觸發器不支持call 語句,表明 不能從觸發器內調用存儲過程;(干貨——MySQL觸發器內不能調用存儲過程)
總結
以上是生活随笔為你收集整理的MySQL存储过程+游标+触发器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网件设置向导(网络设置向导)
- 下一篇: MySQL检索数据(过滤+通配符+正则表