任務需求:定時執行的任務,調用存儲過程,進行數據遷移。
存儲過程相關總結:(存儲過程的創建 ?不能伴隨有if exists ?需要提前判斷刪除同名的存儲過程)
存儲過程的查看:
select name from mysql.proc where db = your_db_name and type = PROCEDURE
show procedure status;
查看存儲過程或函數的創建代碼
show create procedure proc_name;
存儲過程的創建 語法以及遇到的問題
MySQL中,創建存儲過程的基本形式如下:
CREATE PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
其中,sp_name參數是存儲過程的名稱;proc_parameter表示存儲過程的參數列表; characteristic參數指定存儲過程的特性;routine_body參數是SQL代碼的內容,可以用BEGIN…END來標志SQL代碼的開始和結束。
proc_parameter中的每個參數由3部分組成。這3部分分別是輸入輸出類型、參數名稱和參數類型。其形式如下:
[ IN | OUT | INOUT ] param_name type
其中,IN表示輸入參數;OUT表示輸出參數; INOUT表示既可以是輸入,也可以是輸出; param_name參數是存儲過程的參數名稱;type參數指定存儲過程的參數類型,該類型可以是MySQL數據庫的任意數據類型。
characteristic參數有多個取值。其取值說明如下:
LANGUAGE SQL:說明routine_body部分是由SQL語言的語句組成,這也是數據庫系統默認的語言。
[NOT] DETERMINISTIC:指明存儲過程的執行結果是否是確定的。DETERMINISTIC表示結果是確定的。每次執行存儲過程時,相同的輸入會得到相同的輸出。NOT DETERMINISTIC表示結果是非確定的,相同的輸入可能得到不同的輸出。默認情況下,結果是非確定的。
{ CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }:指明子程序使用SQL語句的限制。CONTAINS SQL表示子程序包含SQL語句,但不包含讀或寫數據的語句;NO SQL表示子程序中不包含SQL語句;READS SQL DATA表示子程序中包含讀數據的語句;MODIFIES SQL DATA表示子程序中包含寫數據的語句。默認情況下,系統會指定為CONTAINS SQL。
SQL SECURITY { DEFINER | INVOKER }:指明誰有權限來執行。DEFINER表示只有定義者自己才能夠執行;INVOKER表示調用者可以執行。默認情況下,系統指定的權限是DEFINER。
COMMENT 'string':注釋信息。
技巧:創建存儲過程時,系統默認指定CONTAINS SQL,表示存儲過程中使用了SQL語句。但是,如果存儲過程中沒有使用SQL語句,最好設置為NO SQL。而且,存儲過程中最好在COMMENT部分對存儲過程進行簡單的注釋,以便以后在閱讀存儲過程的代碼時更加方便。
創建一個名為num_from_employee的存儲過程。代碼如下:
CREATE? PROCEDURE? num_from_employee (IN emp_id INT, OUT count_num INT )
READS SQL DATA
BEGIN
SELECT? COUNT(*)? INTO? count_num
FROM? employee
WHERE? d_id=emp_id ;
END
上述代碼中,存儲過程名稱為num_from_employee;輸入變量為emp_id;輸出變量為count_num。SELECT語句從employee表查詢d_id值等于emp_id的記錄,并用COUNT(*)計算d_id值相同的記錄的條數,最后將計算結果存入count_num中。代碼的執行結果如下:
mysql> DELIMITER &&
mysql> CREATE? PROCEDURE? num_from_employee
(IN emp_id INT, OUT count_num INT )
-> READS SQL DATA
-> BEGIN
-> SELECT? COUNT(*)? INTO? count_num
-> FROM? employee
-> WHERE? d_id=emp_id ;
-> END &&
Query OK, 0 rows affected (0.09 sec)
mysql> DELIMITER ;
代碼執行完畢后,沒有報出任何出錯信息就表示存儲函數已經創建成功。以后就可以調用這個存儲過程,數據庫中會執行存儲過程中的SQL語句。
說明:MySQL中默認的語句結束符為分號(;)。存儲過程中的SQL語句需要分號來? ? 結束。為了避免沖突,首先用"DELIMITER &&"將MySQL的結束符設置為&&。最后再用"DELIMITER ;"來將結束符恢復成分號。這與創建觸發器時是一樣的。
一、MySQL 創建存儲過程
“pr_add” 是個簡單的 MySQL 存儲過程,這個存儲過程有兩個 int 類型的輸入參數 “a”、“b”,返回這兩個參數的和。
drop procedure if exists pr_add;
-- 計算兩個數之和
create procedure pr_add
(
a int,
b int
)
begin
declare c int;
if a is null then
set a = 0;
end if;
if b is null then
set b = 0;
end if;
set c = a + b;
select c as sum;
/*
return c;- 不能在 MySQL 存儲過程中使用。return 只能出現在函數中。
/
end;
二、調用Mysql存儲過程
eg: call pr_add(10,20);
執行 MySQL 存儲過程,存儲過程參數為 MySQL 用戶變量。
set @a = 10;
set @b = 20;
call pr_add(@a, @b);
假定存儲過程如下:create procedure pr_return2Pr(a int,out b int,inout c int) begin set b =a; set c = a+ c; end
set @cc = 3;call pr_return2Pr(1,@bb,@cc);select @bb,@cc; 返回1,4
如果存儲過程中變量定義為out,可以不進行變量值定義,直接聲明即可。inout? 則需要提前set 變量,然后調用。
IN為默認類型,值必須在調用時指定,值不能返回(值傳遞)
OUT值可以返回(指針傳遞)
INOUT值必須在調用時指定,值可以返回
使用DECLARE來聲明,DEFAULT賦默認值,SET賦值
DECLARE counter INT DEFAULT 0;? - 默認為0
SET counter = counter+1;? ? ? ? ? ? ? - 自增+1
三、MySQL 存儲過程特點
創建 MySQL 存儲過程的簡單語法為:
create procedure 存儲過程名字()
(
[in|out|inout] 參數 datatype
)
begin
MySQL 語句;
end;
MySQL 存儲過程參數如果不顯式指定“in”、“out”、“inout”,則默認為“in”。習慣上,對于是“in” 的參數,我們都不會顯式指定。
如下,給出解釋
1. MySQL 存儲過程名字后面的“()”是必須的,即使沒有一個參數,也需要“()”
2. MySQL 存儲過程參數,不能在參數名稱前加“@”,如:“@a int”。下面的創建存儲過程語法在 MySQL 中是錯誤的(在 SQL Server 中是正確的)。 MySQL 存儲過程中的變量,不需要在變量名字前加“@”,雖然 MySQL 客戶端用戶變量要加個“@”。
create procedure pr_add
(
@a int,- 錯誤
b int? - 正確
)
3. MySQL 存儲過程的參數不能指定默認值。
4. MySQL 存儲過程不需要在 procedure body 前面加 “as”。而 SQL Server 存儲過程必須加 “as” 關鍵字。
create procedure pr_add
(
a int,
b int
)
as? ? ? ? ? ? - 錯誤,MySQL 不需要 “as”
begin
mysql statement ...;
end;
5. 如果 MySQL 存儲過程中包含單條或者多條 MySQL 語句,都需要 begin end 關鍵字。
create procedure pr_add
(
a int,
b int
)
begin
mysql statement 1 ...;
mysql statement 2 ...;
end;
6.MySQL 存儲過程中的每條語句的末尾,都要加上分號 “;”
...
declare c int;
if a is null then
set a = 0;
end if;
...
7. MySQL 存儲過程中的注釋。
/*
這是個
多行 MySQL 注釋。
/
declare c int;? ? - 這是單行 MySQL 注釋 (注意- 后至少要有一個空格)
if a is null then 這也是個單行 MySQL 注釋
set a = 0;
end if;
...
end;
8. 不能在 MySQL 存儲過程中使用 “return” 關鍵字。
set c = a + b;
select c as sum;
/*
return c;- 不能在 MySQL 存儲過程中使用。return 只能出現在函數中。
/
end;
9. 調用 MySQL 存儲過程時候,需要在過程名字后面加“()”,即使沒有一個參數,也需要“()”
call pr_no_param();
10. 因為 MySQL 存儲過程參數沒有默認值,所以在調用 MySQL 存儲過程時候,不能省略參數。可以用 null 來替代。
call pr_add(10, null);
mysql寫條件判斷時,注意時if? elseif? else? 其中elseif之間是不能夠有空格的。
命令行編寫存儲過程命令是一定注意提前delimiter。說明:MySQL中默認的語句結束符為分號(;)。存儲過程中的SQL語句需要分號來? ? 結束。為了避免沖突,首先用"DELIMITER &&"將MySQL的結束符設置為&&。最后再用"DELIMITER ;"來將結束符恢復成分號。這與創建觸發器時是一樣的。但是mysql 窗口就沒問題了。
存儲過程的刪除: 不能在一個存儲過程中刪除另一個存儲過程,只能調用另一個存儲過程
drop procedure if exists prName
11,存儲方法
存儲方法與存儲過程的區別
1,存儲方法的參數列表只允許IN類型的參數,而且沒必要也不允許指定IN關鍵字
2,存儲方法返回一個單一的值,值的類型在存儲方法的頭部定義
3,存儲方法可以在SQL語句內部調用
4,存儲方法不能返回結果集
mysql事件:創建存儲過程的時候,如果采用命令行的方式,需要先修改命令結束符,將分號改成其他的符號,注意delimiter時 不能在$$后添加;? 否則就以$$;? 作為結束標記了。
-- 設置分隔符為 '$$' ,mysql默認的語句分隔符為 ';' ,這樣在后續的 create 到 end 這段代碼都會看成是一條語句來執行
DELIMITER $$
//創建存儲過程或者事件語句
//結束
$$
- 將語句分割符設置回 ';'
DELIMITER ;
事件簡介
事件(event)是MySQL在相應的時刻調用的過程式數據庫對象。一個事件可調用一次,也可周期性的啟動,它由一個特定的線程來管理的,也就是所謂的“事件調度器”。
事件和觸發器類似,都是在某些事情發生的時候啟動。當數據庫上啟動一條語句的時候,觸發器就啟動了,而事件是根據調度事件來啟動的。由于他們彼此相似,所以事件也稱為臨時性觸發器。
事件取代了原先只能由操作系統的計劃任務來執行的工作,而且MySQL的事件調度器可以精確到每秒鐘執行一個任務,而操作系統的計劃任務(如:Linux下的CRON或Windows下的任務計劃)只能精確到每分鐘執行一次。
2 事件的優缺點
2.1 優點
一些對數據定時性操作不再依賴外部程序,而直接使用數據庫本身提供的功能。
可以實現每秒鐘執行一個任務,這在一些對實時性要求較高的環境下就非常實用了。
2.2 缺點
定時觸發,不可以調用。
3 創建事件
一條create event語句創建一個事件。每個事件由兩個主要部分組成,第一部分是事件調度(event schedule),表示事件何時啟動以及按什么頻率啟動,第二部分是事件動作(event action ),這是事件啟動時執行的代碼,事件的動作包含一條SQL語句,它可能是一個簡單地insert或者update語句,也可以使一個存儲過程或者benin...end語句塊,這兩種情況允許我們執行多條SQL。
一個事件可以是活動(打開)的或停止(關閉)的,活動意味著事件調度器檢查事件動作是否必須調用,停止意味著事件的聲明存儲在目錄中,但調度器不會檢查它是否應該調用。在一個事件創建之后,它立即變為活動的,一個活動的事件可以執行一次或者多次。
3.1 創建語法如下
CREATE
[DEFINER = { user | CURRENT_USER }]
EVENT
[IF NOT EXISTS]
event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO event_body;
schedule:
AT timestamp [+ INTERVAL interval] ...
| EVERY interval
[STARTS timestamp [+ INTERVAL interval] ...]
[ENDS timestamp [+ INTERVAL interval] ...]
interval:
quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |
WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |
DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}
名詞解釋:
event_name :創建的event名字(唯一確定的)。
ON SCHEDULE:計劃任務。
schedule: 決定event的執行時間和頻率(注意時間一定要是將來的時間,過去的時間會出錯),有兩種形式 AT和EVERY。
[ON COMPLETION [NOT] PRESERVE]: 可選項,默認是ON COMPLETION NOT PRESERVE 即計劃任務執行完畢后自動drop該事件;ON COMPLETION? PRESERVE則不會drop掉。
[COMMENT 'comment'] :可選項,comment 用來描述event;相當注釋,最大長度64個字節。
[ENABLE | DISABLE] :設定event的狀態,默認ENABLE:表示系統嘗試執行這個事件, DISABLE:關閉該事情,可以用alter修改
DO event_body: 需要執行的sql語句(可以是復合語句)。CREATE EVENT在存儲過程中使用時合法的。
定時器 -- 創建定時器后會在mysql 中創建一個線程用以執行該事件。 user就是event_scheduler command-daemon state-waiting for next activation
1.查看定時器-事件調度器 MySQL事件調度器event_scheduler負責調用事件,它默認是關閉的。這個調度器不斷地監視一個事件是否要調用, 要創建事件,必須打開調度器。
show variables like '%event_scheduler%';
2.開啟時間調度器
命令行:
SET GLOBAL event_scheduler = ON;
SET @@global.event_scheduler = ON;
SET GLOBAL event_scheduler = 1;
SET @@global.event_scheduler = 1;
配置文件:event_scheduler = 1 #或者ON
查看調度器線程
mysql> show processlist;
+----+-----------------+-----------+------+---------+------+------------------------+------------------+
| Id | User? ? ? ? ? ? | Host? ? ? | db? | Command | Time | State? ? ? ? ? ? ? ? ? | Info? ? ? ? ? ? |
+----+-----------------+-----------+------+---------+------+------------------------+------------------+
|? 2 | root? ? ? ? ? ? | localhost | NULL | Query? |? ? 0 | NULL? ? ? ? ? ? ? ? ? | show processlist |
|? 3 | event_scheduler | localhost | NULL | Daemon? |? ? 6 | Waiting on empty queue | NULL? ? ? ? ? ? |
+----+-----------------+-----------+------+---------+------+------------------------+------------------+
關閉事件調度器
通過命令行
可通過如下任何一個命令行
SET GLOBAL event_scheduler = OFF;
SET @@global.event_scheduler = OFF;
SET GLOBAL event_scheduler = 0;
SET @@global.event_scheduler = 0;
通過配置文件my.cnf
在[mysqld]下增加
event_scheduler = 0 #或者OFF,DISABLED
查看調度器線程
mysql> show processlist;
+----+------+-----------+------+---------+------+-------+------------------+
| Id | User | Host? ? ? | db? | Command | Time | State | Info? ? ? ? ? ? |
+----+------+-----------+------+---------+------+-------+------------------+
|? 2 | root | localhost | NULL | Query? |? ? 0 | NULL? | show processlist |
+----+------+-----------+------+---------+------+-------+------------------+
查看事件運行狀態:
(1)查詢mysql.event表;
(2)通過SHOW EVENTS命令;
(4)通過查詢information_schema.events表
(5)SHOW CREATE EVENT。 -- 查看創建目錄
總之,event的使用頻率較低建議使用root用戶進行創建和維護。
使用權限
單獨使用event調用SQL語句時,查看和創建需要用戶具有event權限,調用該SQL語句時,需要用戶具有執行該SQL的權限。Event權限的設置保存在mysql.user表和mysql.db表的Event_priv字段中。
當event和procedure配合使用的時候,查看和創建存儲過程需要用戶具有create routine權限,調用存儲過程執行時需要使用excute權限,存儲過程調用具體的SQL語句時,需要用戶具有執行該SQL的權限。
關于事件計劃的權限:
單獨使用event調用SQL語句時,查看和創建需要用戶具有event權限,調用該SQL語句時,需要用戶具有執行該SQL的權限。Event權限的設置保存在mysql.user表和mysql.db表的Event_priv字段中。(FLUSH PRIVILEGES;)
當event和procedure配合使用的時候,查看和創建存儲過程需要用戶具有create routine權限,調用存儲過程執行時需要使用excute權限,存儲過程調用具體的SQL語句時,需要用戶具有執行該SQL的權限。
SELECT HOST,USER,Event_priv FROM mysql.user;
(Figure1:user表的Event_priv權限)
獲取當前登陸的用戶和數據庫:SELECT CURRENT_USER(), SCHEMA();
從Figure1可以知道bfsql@%是沒有Event_priv權限的,在該用戶下創建事件的時候會出現下面的錯誤:
Error Code: 1044
Access denied for user 'bfsql'@'%' to database 'blog'
如果出現上面的錯誤,執行下面的SQL就可以給bfsql@%賦予創建Event的權限:
UPDATE mysql.user SET Event_priv = 'Y' WHERE HOST='%' AND USER='bfsql';
如果你這個時候再次執行創建Event的SQL,還是會出現上面的錯誤,因為你需要執行:
FLUSH PRIVILEGES;最后,你可以通過SHOW GRANTS FOR 'bfsql'@'%';查看所有權限;
創建event語句相關命令解釋:
ON SCHEDULE 計劃任務,有兩種設定計劃任務的方式:
1. AT 時間戳,用來完成單次的計劃任務。
2. EVERY 時間(單位)的數量時間單位[STARTS 時間戳] [ENDS時間戳],用來完成重復的計劃任務。
在兩種計劃任務中,時間戳可以是任意的TIMESTAMP 和DATETIME 數據類型,時間戳需要大于當前時間。
在重復的計劃任務中,時間(單位)的數量可以是任意非空(Not Null)的整數式,時間單位是關鍵詞:YEAR,MONTH,DAY,HOUR,MINUTE 或者SECOND。
提示: 其他的時間單位也是合法的如:QUARTER, WEEK, YEAR_MONTH,DAY_HOUR,DAY_MINUTE,DAY_SECOND,HOUR_MINUTE,HOUR_SECOND, MINUTE_SECOND,不建議使用這些不標準的時間單位。
標注4: [ON COMPLETION [NOT] PRESERVE]
ON COMPLETION參數表示"當這個事件不會再發生的時候",即當單次計劃任務執行完畢后或當重復性的計劃任務執行到了ENDS階段。而PRESERVE的作用是使事件在執行完畢后不會被Drop掉,建議使用該參數,以便于查看EVENT具體信息。
標注5:[ENABLE | DISABLE]
參數Enable和Disable表示設定事件的狀態。Enable表示系統將執行這個事件。Disable表示系統不執行該事件。
可以用如下命令關閉或開啟事件:
ALTER EVENT event_name? ENABLE/DISABLE
標注6:[COMMENT 'comment']
注釋會出現在元數據中,它存儲在information_schema表的COMMENT列,最大長度為64個字節。'comment'表示將注釋內容放在單引號之間,建議使用注釋以表達更全面的信息。
當然SQL語句是有限制的,對它的限制跟函數Function和觸發器Trigger 中對SQL語句的限制是一樣的,如果你在函數Function 和觸發器Trigger 中不能使用某些SQL,同樣的在EVENT中也不能使用。明確的來說有下面幾個:
LOCK TABLES
UNLOCK TABLES
CREATE EVENT
ALTER EVENT
LOAD DATA
修改事件 使用ALTER EVENT 來修改事件,具體的ALTER語法如下,與創建事件的語法類似:
刪除事件:EVENT使用DROP EVENT語句來刪除已經創建的事件,語法如下:DROP EVENT [IF EXISTS] event_name
但當一個事件正在運行中時,刪除該事件不會導致事件停止,事件會執行到完畢為止。使用DROP USER和DROP DATABASE 語句同時會將包含其中的事件刪除。
每隔一秒自動調用e_test()存儲過程:
CREATE EVENT IF NOT EXISTS e_test
ON SCHEDULE EVERY 1 SECOND
ON COMPLETION PRESERVE
DO CALL e_test();
每個月的一號凌晨1 點執行STAT()存儲過程:
CREATE? EVENT? NOT EXISTS? STAT
ON? SCHEDULE? EVERY? 1? MONTH? STARTS DATE_ADD(DATE_ADD(DATE_SUB(CURDATE(),INTERVAL DAY(CURDATE())-1 DAY), INTERVAL 1 MONTH),INTERVAL 1 HOUR)
ON? COMPLETION? PRESERVE? ENABLE
DO
BEGIN
CALL STAT();
END
創建一個每隔3秒往test表中插入一條數據的事件,代碼如下:
CREATE EVENT IF NOT EXISTS test ON SCHEDULE EVERY 3 SECOND
ON COMPLETION PRESERVE
DO INSERT INTO test(id,t1) VALUES('',NOW());
創建一個10分鐘后清空test表數據的事件
CREATE EVENT IF NOT EXISTS test
ON SCHEDULE
AT CURRENT_TIMESTAMP + INTERVAL 10 MINUTE
DO TRUNCATE TABLE test.aaa;
創建一個在2012-08-23 00:00:00時刻清空test表數據的事件
CREATE EVENT IF NOT EXISTS test
ON SCHEDULE
AT TIMESTAMP '2012-08-23 00:00:00'
DO TRUNCATE TABLE test;
創建一個從2012年8月22日21點45分開始到10分鐘后結束,運行每隔3秒往test表中插入一條數據的事件
CREATE EVENT IF NOT EXISTS test ON SCHEDULE EVERY 3 SECOND
STARTS '2012-08-22 21:49:00'
ENDS '2012-08-22 21:49:00'+ INTERVAL? 10 MINUTE
ON COMPLETION PRESERVE
DO INSERT INTO test(id,t1) VALUES('',NOW());
注意:
默認創建事件存儲在當前庫中,也可顯示指定事件創建在哪個庫中
通過show events只能查看當前庫中創建的事件
事件執行完即釋放,如立即執行事件,執行完后,事件便自動刪除,多次調用事件或等待執行事件可以查看到。
如果兩個事件需要在同一時刻調用,mysql會確定調用他們的順序,如果要指定順序,需要確保一個事件至少在另一個事件1秒后執行
對于遞歸調度的事件,結束日期不能在開始日期之前。
select可以包含在一個事件中,然而他的結果消失了,就好像沒執行過。
刪除事件:drop event 語句刪除它。使用這條語句我們不需要等到最后一次事件調用。DROP EVENT [IF EXISTS] event_name,drop event if exists event_second;
一個事件示例:每分鐘的零秒執行數據遷移
create event updateEachMinuteAt0Second
on schedule every 1 minute starts timestampadd(second,60 -second(current_timestamp()),current_timestamp())
on completion preserve enable
do
begin
call updatetestevent();
end
函數體內使用函數變量提前聲明 declare a int; ? 判斷是否為空 ? a is null; ?條件塊:if ?body then body; elseif body then body; end if; ? ? ? ? ? ? ? 存儲過程:begin ?body ?end;