目錄
MySQL5.1.6起增加了事件調度器(Event Scheduler),可用來做定時執行某些特定任務,用於取代原先只能由操作系統的計劃任務來執行的工作。MySQL的事件調度器可以精確到每秒執行一個任務,而操作系統的計劃任務只能精確到分鐘級別。對於對數據實時性要求比較高的應用非常合適。
事件調度器也稱爲臨時觸發器(Temporal Triggers),因爲事件調度器是基於特定時間週期觸發來執行某些任務,而觸發器(Triggers)是基於某個表所產生的事件觸發的。
MySQL定時任務的實現方式有兩種:
- 使用MySQL的
event
定時任務
使用MySQL的事件計劃,首先需要在服務器開啓event_scheduler
後才能處理。 - 使用Linux的定時任務
crontab
如何開啓事件計劃呢?
$ SHOW VARIABLES LIKE 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | OFF |
+-----------------+-------+
1 row in set
如果執行命令後返回值爲OFF
則表示目前事件計劃是處於關閉的狀態。
開啓的方式也分爲兩種,臨時方式使用命令行或腳本操作,永久修改則需要修改MySQL主配置文件my.ini
在其中添加event_schduler=1
的配置後重啓MySQL。
臨時性修改只要不重啓MySQL在當前運行狀態下會直接生效,一旦重啓後則失效。
$ SET GLOBAL event_scheduler = ON;
$ SET @@global.event_scheduler = ON;
$ SET GLOBAL event_scheduler = 1;
$ SET @@global.event_scheduler = 1;
事件調度器
要保證能夠執行事件,就必須保證事件計劃是開啓狀態,事件計劃默認爲關閉狀態。
# 查看MySQL版本
$ SELECT VERSION();
+------------+
| VERSION() |
+------------+
| 5.7.18-log |
+------------+
1 row in set
# 事件計劃是否開啓
$ SHOW VARIABLES LIKE 'event%'
$ SHOW VARIABLES LIKE 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set
# 查看事件任務是否開啓
$ SELECT @@event_scheduler;
+-------------------+
| @@event_scheduler |
+-------------------+
| ON |
+-------------------+
1 row in set
# 開啓事件計劃
$ SET GLOBAL event_scheduler=1
$ SET GLOBAL event_scheduler=ON
duler=1;
Query OK, 0 rows affected
# 關閉事件計劃
$ SET GLOBAL event_scheduler=0
在真實開發環境中會遇到MySQL服務重啓或斷電的情況,此時會出現事件調度器被關閉的情況。所有事件都不再起作用,解決的方式需要在MySQL的配置文件mysql.ini
中加入event_scheduler=ON
的配置。
事件任務
事件任務
# 查看事件任務
$ SHOW EVENTS;
Empty set
# 查看事件任務錯誤 - 權限不足
$ SELECT * FROM mysql.event
1142 - SELECT command denied to user 'username'@'127.0.0.1' for table 'event'
# 開啓事件任務
$ ALTER EVENT event_name ON COMPLETION PRESERVE ENABLE
# 關閉事件任務
$ ALTER EVENT event_name ON COMPLETION PRESERVE DISABLE
# 刪除事件
$ DROP EVENT [IF EXISTS] event_name
設置定時任務執行SQL語句
例如:從當日開始每天凌晨4點刪除fight表超過一個月的數據
計劃任務
DROP EVENT IF EXISTS event_fight_delete;
CREATE EVENT event_fight_delete
ON SCHEDULE EVERY 1 DAY STARTS DATE_ADD(DATE(CURDATE() + 1), INTERVAL 4 HOUR)
DO
BEGIN
DELETE FROM center_fight WHERE 1=1 AND createdate < DATE_ADD(CURDATE(), INTERVAL -1 MONTH)
END
設置定時任務調用存儲過程
# 若計劃任務存在則刪除
DROP EVENT IF EXISTS event_name
# 創建計劃任務
CREATE EVENT event_name
ON SCHEDULE EVERY 10 second
STARTS TIMESTAMP '2018-07-12 00:00:00'
ON COMPLETION PRESERVE
DO
BEGIN
CALL producer()
END
參數說明
ON SCHDULE schduler
定義執行的時間和時間間隔ON COMPLETION [NOT] PRESERVE
定義事件是一次性執行還是永久執行,默認爲一次性執行,即NOT PRESERVE
。
在事件中ON SCHEDULE
計劃任務中有2種設定的方式
- 用來完成單次計劃任務。
AT 時間戳
eg:5天后
AT CURRENT_TIMESTAMP + INTERVAL 5 DAY
eg:某時間點
AT TIMESTAMP '2018-07-12 12:00:00'
- 用來完成重複的計劃任務
EVERY 時間(單位)的數量 時間單位 [STARTS 時間戳] [ENDS時間戳]
eg:每隔1秒
EVERY 1 SECOND
eg:每隔10分鐘。
EVERY 10 MINUTE
eg:從2018-08-01 12:00:00開始每隔1天
EVERY 1 DAY STARTS '2018-08-01 12:00:00'
EVERY 10 second STARTS TIMESTAMP '2018-08-01 12:00:00'
eg:5天后開啓每天定時處理
EVERY 1 DAY START CURRENT_TIMESTAMP + INTERVAL 5 DAY
eg:每天定時處理5天后停止
EVERY 1 DAY ENDS CURRENT_TIMESTAMP + INTERVAL 5 DAY
在兩種計劃任務中,時間戳可以是任意的TIMESTAMP
和DATETIME
數據類型,時間戳需要大於當前時間。
在重複的計劃任務中,時間(單位)的數量可以是任意非空(NOT NULL)的整數形式,時間單位是關鍵詞:YEAR
、MONTH
、DAY
、HOUR
、MINUTE
、SECOND
...
[ON COMPLETION [NOT] PRESERVE]
ON COMPLETION
參數表示“當這個事件不會再發生的時候”,即當單次計劃任務執行完畢後或當重複性的計劃任務執行到了ENDS
階段。而PRESERVE
的作用是使事件在執行完畢後不會被DROP
掉,建議使用該參數,以便於查看EVENT
具體信息。
CREATE DEFINER=`root`@`localhost` EVENT `event_knapsacks_remember_expire`
ON SCHEDULE EVERY 1 MINUTE STARTS '2018-07-13 15:09:49'
ON COMPLETION PRESERVE ENABLE
COMMENT '每分鐘檢測揹包中換牌卡到期並每日自動減少'
DO
BEGIN
CALL produce_knapsacks_remember_expire();
END
存儲過程
DELIMITER $$
DROP PROCEDURE IF EXISTS procedure_name
CREATE PROCEDURE procedure_name()
BEGIN
INSERT INTO procedure_name(name, create_time) VALUES('name_value', now())
END $$
DELIMITER ;
-- 存儲過程 produce_knapsacks_remember_expire
-- 作用:判斷揹包中道具記牌卡,是否過期,且每日減一。
CREATE DEFINER=`root`@`localhost` PROCEDURE `produce_knapsacks_remember_expire`()
BEGIN
DECLARE pk INT DEFAULT 0;
DECLARE sec INT DEFAULT 0;
DECLARE days INT DEFAULT 0;
DECLARE expire INT DEFAULT 0;
DECLARE mc CURSOR FOR (SELECT id,TIMESTAMPDIFF(SECOND,effect_time,NOW()) AS diff,TIMESTAMPDIFF(SECOND,NOW(),expire_time) AS expire FROM knapsacks WHERE name='REMEBER' AND expired=0);
OPEN mc;
ml:LOOP
FETCH mc INTO pk,sec,expire;
IF(expire <= 0) THEN
UPDATE `knapsacks` SET `expired`=1 WHERE `id`=pk;
ELSE
IF(sec>0 && sec<=86400) THEN
SET days = 1;
ELSEIF(sec>86400) THEN
SET days=CEILING(sec/86400);
END IF;
UPDATE `knapsacks` SET `quantity`=`purchase`-days,`consume`=days WHERE `id`=pk;
END IF;
COMMIT;
END LOOP ml;
CLOSE mc;
END
錯誤處理
出現錯誤
[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'information_schema.PROFILING.SEQ' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
解決方案
$ SELECT VERSION();
$ @@sql_mode;
$ SET sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY' , ''));
出現提示
ERROR
# 在SQL中查詢計劃事件的狀態
$ SHOW VARIABLES LIKE 'event_scheduler'
# 在mysql程序的目錄下找到my.ini文件添加
$ vim my.ini
event_scheduler = 1
# 保存後重啓mysql服務
# 用腳本來實現
# 開啓event_scheduler sql指令:
SET GLOBAL event_scheduler = ON;
SET @@global.event_scheduler = ON;
SET GLOBAL event_scheduler = 1;
SET @@global.event_scheduler = 1;
$ 關閉event_scheduler指令:
SET GLOBAL event_scheduler = OFF;
SET @@global.event_scheduler = OFF;
SET GLOBAL event_scheduler = 0;
SET @@global.event_scheduler = 0;
MySQL 存儲過程 if語句
MySQL IF
語句允許您根據表達式的某個條件或值結果來執行一組SQL語句。 要在MySQL中形成一個表達式,可以結合文字,變量,運算符,甚至函數來組合。表達式可以返回TRUE
,FALSE
或NULL
,這三個值之一。
MySQL IF語句語法
下面說明了IF
語句的語法:
IF expression THEN statements; END IF;
如果表達式(expression
)計算結果爲TRUE
,那麼將執行statements
語句,否則控制流將傳遞到END IF
之後的下一個語句。
以下流程圖演示了IF
語句的執行過程:
MySQL IF ELSE語句
如果表達式計算結果爲FALSE
時執行語句,請使用IF ELSE
語句,如下所示:
IF expression THEN statements; ELSE else-statements; END IF;
以下流程圖說明了IF ELSE
語句的執行過程:
MySQL IF ELSEIF ELSE語句
如果要基於多個表達式有條件地執行語句,則使用IF ELSEIF ELSE
語句如下:
IF expression THEN statements; ELSEIF elseif-expression THEN elseif-statements; ... ELSE else-statements; END IF;
如果表達式(expression
)求值爲TRUE
,則IF
分支中的語句(statements
)將執行;如果表達式求值爲FALSE
,則如果elseif_expression
的計算結果爲TRUE
,MySQL將執行elseif-expression
,否則執行ELSE
分支中的else-statements
語句。具體流程如下
MySQL IF語句示例
以下示例說明如何使用IF ESLEIF ELSE
語句,GetCustomerLevel()
存儲過程接受客戶編號和客戶級別的兩個參數。
首先,它從customers
表中獲得信用額度
然後,根據信用額度,它決定客戶級別:PLATINUM
, GOLD
和 SILVER
。
參數p_customerlevel
存儲客戶的級別,並由調用程序使用。
USE yiibaidb; DELIMITER $$ CREATE PROCEDURE GetCustomerLevel( in p_customerNumber int(11), out p_customerLevel varchar(10)) BEGIN DECLARE creditlim double; SELECT creditlimit INTO creditlim FROM customers WHERE customerNumber = p_customerNumber; IF creditlim > 50000 THEN SET p_customerLevel = 'PLATINUM'; ELSEIF (creditlim <= 50000 AND creditlim >= 10000) THEN SET p_customerLevel = 'GOLD'; ELSEIF creditlim < 10000 THEN SET p_customerLevel = 'SILVER'; END IF; END$$
以下流程圖演示了確定客戶級別的邏輯
IF表達式
IF(expr1,expr2,expr3)
如果 expr1 是TRUE (expr1 <> 0 and expr1 <> NULL),則 IF()的返回值爲expr2; 否則返回值則爲 expr3。IF() 的返回值爲數字值或字符串值,具體情況視其所在語境而定。
select *,if(sva=1,"男","女") as ssva from taname where sva != ""
作爲表達式的if也可以用CASE when來實現:
select CASE sva WHEN 1 THEN '男' ELSE '女' END as ssva from taname where sva != ''
在第一個方案的返回結果中, value=compare-value。而第二個方案的返回結果是第一種情況的真實結果。如果沒有匹配的結果值,則返回結果爲ELSE後的結果,如果沒有ELSE 部分,則返回值爲 NULL。
例如:
SELECT CASE 1 WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'more' END as testCol
將輸出one
IFNULL(expr1,expr2)
假如expr1 不爲 NULL,則 IFNULL() 的返回值爲 expr1; 否則其返回值爲 expr2。IFNULL()的返回值是數字或是字符串,具體情況取決於其所使用的語境。
mysql> SELECT IFNULL(1,0); -> 1 mysql> SELECT IFNULL(NULL,10); -> 10 mysql> SELECT IFNULL(1/0,10); -> 10 mysql> SELECT IFNULL(1/0,'yes'); -> 'yes'
IFNULL(expr1,expr2)
的默認結果值爲兩個表達式中更加“通用”的一個,順序爲STRING、 REAL或 INTEGER。
IF ELSE 做爲流程控制語句使用
if實現條件判斷,滿足不同條件執行不同的操作,這個我們只要學編程的都知道if的作用了,下面我們來看看mysql 存儲過程中的if是如何使用的吧。
IF search_condition THEN statement_list [ELSEIF search_condition THEN] statement_list ... [ELSE statement_list] END IF
與PHP中的IF語句類似,當IF中條件search_condition成立時,執行THEN後的statement_list語句,否則判斷ELSEIF中的條件,成立則執行其後的statement_list語句,否則繼續判斷其他分支。當所有分支的條件均不成立時,執行ELSE分支。search_condition是一個條件表達式,可以由“=、<、<=、>、>=、!=”等條件運算符組成,並且可以使用AND、OR、NOT對多個表達式進行組合。
例如,建立一個存儲過程,該存儲過程通過學生學號(student_no)和課程編號(course_no)查詢其成績(grade),返回成績和成績的等級,成績大於90分的爲A級,小於90分大於等於80分的爲B級,小於80分大於等於70分的爲C級,依次到E級。那麼,創建存儲過程的代碼如下:
create procedure dbname.proc_getGrade (stu_no varchar(20),cour_no varchar(10)) BEGIN declare stu_grade float; select grade into stu_grade from grade where student_no=stu_no and course_no=cour_no; if stu_grade>=90 then select stu_grade,'A'; elseif stu_grade<90 and stu_grade>=80 then select stu_grade,'B'; elseif stu_grade<80 and stu_grade>=70 then select stu_grade,'C'; elseif stu_grade70 and stu_grade>=60 then select stu_grade,'D'; else select stu_grade,'E'; end if; END
注意:IF作爲一條語句,在END IF後需要加上分號“;”以表示語句結束,其他語句如CASE、LOOP等也是相同的。
MySql中創建定時任務
1.需求描述
每天0時定時調用存儲過程
2.創建定時任務
方式一
SELECT @@global.sql_mode;
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); ##當前session變量生效
-- 創建每天0時執行一次的定時任務,調用存儲過程
CREATE DEFINER=`root`@`localhost` EVENT IF NOT EXISTS event_call_proc_modify_primary_key_value
-- 每天0時執行一次
-- ON SCHEDULE EVERY 1 DAY STARTS TIMESTAMP(CURRENT_DATE,'00:00:00')
-- 每3秒執行一次
ON SCHEDULE EVERY 3 SECOND STARTS TIMESTAMP(CURRENT_DATE,'00:00:00')
ON COMPLETION PRESERVE ENABLE
DO
CALL proc_modify_primary_key_value('primary_key_generator_table','value','1','name','PK_CUSTOMER_ID')
-----------------------------------------------------------------------------------------------------------
方式二
-- 創建每天0時執行一次的定時任務,調用存儲過程
CREATE DEFINER=`root`@`localhost` EVENT IF NOT EXISTS event_call_proc_modify_primary_key_value
-- 每天0時執行一次
-- ON SCHEDULE EVERY 1 DAY STARTS TIMESTAMP(CURRENT_DATE,'00:00:00')
-- 每3秒執行一次
ON SCHEDULE EVERY 3 SECOND STARTS TIMESTAMP(CURRENT_DATE,'00:00:00')
ON COMPLETION PRESERVE
DO
CALL proc_modify_primary_key_value('primary_key_generator_table','value','1','name','PK_CUSTOMER_ID')
3.開啓定時任務
方式一
-- 開啓定時器
SET GLOBAL event_scheduler=1;
-----------------------------------------------------------------------------------------------------------
方式二
-- 開啓定時器
SET GLOBAL event_scheduler=1;
-- 開啓事件任務
ALTER EVENT event_call_proc_modify_primary_key_value ENABLE;
4.查看定時任務
-- 查看定時任務
SELECT * FROM information_schema.`EVENTS`;
5.刪除定時任務
-- 刪除定時任務
DROP EVENT event_call_proc_modify_primary_key_value;
這裏我們要完成的定時任務,比較簡單
效果:每過X秒 某一些用戶積分 加X
第一步:我們先創建一個表和插入數據
/* 創建一個表 */ CREATE TABLE `test_name` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵,自增' , `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '用戶名' , `integral` int(11) NULL COMMENT '積分' , `is_adm` tinyint(1) NULL COMMENT '管理員,1-是,0-不是' , PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci CHECKSUM=0 DELAY_KEY_WRITE=0 ; /* 添加數據 */ INSERT INTO `test_name` VALUES ('1', '會員1', '0', '1'); INSERT INTO `test_name` VALUES ('2', '會員2', '0', '0'); INSERT INTO `test_name` VALUES ('3', '會員3', '0', '0');
看看效果:
ok,表和數據做好了,我們說一下需求:
需求1:沒經過2秒管理員的積分加1,字段is_adm爲1的則是管理員
以上爲添加表和數據,並提出需求
--------------------------------分割線--------------------------------
以下內容,我們會提到如何啓動定時任務,來完成我們的需求
首先第一步,我們要檢查我們數據庫,是否開啓了event(事件)
SHOW VARIABLES LIKE 'event_scheduler' #查看狀態
運行以上代碼,如果你的結果,Value字段的值爲"OFF",代表你的事件屬於關閉狀態,那麼我們就要開啓它,運行以下代碼:
SET GLOBAL event_scheduler = ON; #ON開啓,OFF關閉
運行之後,再返回上一個,再次檢查事件的狀態,當結果爲"ON",則代表你的事件已開啓,我們就繼續往下走。。
既然是定時任務,那麼我們就要封裝一個函數,流程是這樣的:
- 封裝一個函數
- 封裝一個事件
- 事件開始運行,滿足條件,調用 1(封裝的函數)
那麼我們先來封裝一個函數:
#寫一個函數 begin------------ CREATE PROCEDURE test() BEGIN update test_name SET integral= integral + 1 WHERE is_adm = 1; END; #寫一個函數 end------------
運行以上代碼成功之後,大家不用提心吊膽,不會立刻修改你的數據,因爲這裏只是封裝了一個函數,我們並沒有調用它,它僅僅是存在。所以需要寫一個事件,來調用它
#寫一個事件 begin------------ create event if not exists e_test on schedule every 2 second on completion preserve do call test(); #寫一個事件 end------------
運行以上代碼,成功之後,大家也彆着急,還不會立刻運行此事件。雖然在上面,我們已經開啓了event。但是我們還要單個來運行,指定一個事件來運行,就跟訪問接口一樣,指定一個接口。。。
#開啓事件 alter event e_test ON COMPLETION PRESERVE ENABLE;
這裏能看見,開啓事件,指定的是 e_test 這個事件名字,跟我們上面創建的是一樣的,那麼運行這一段代碼之後,你會發現,你的表 test_name 裏,is_adm = 1 的行,字段爲 integral(積分) 的會每 2秒 加1
那麼一個簡單的定時任務,我們完成了。
噢,對了,關閉單個事件,使用以下的代碼:
#關閉事件 alter event e_test ON COMPLETION PRESERVE DISABLE;
運行之後,就會停止對 e_test 事件的使用
以上需要注意:
- event默認情況下,都是OFF關閉狀態,在MYSQL配置裏可以找到,如果你在配置裏修改成ON,就會默認成ON了
- 關閉數據庫之後,如果你的event還是OFF,也會因爲配置裏默認的OFF因爲,而停止
- 當event關閉之後,你的單個事件屬於開啓狀態,那麼當你開啓event之後,單個事件狀態依舊是開啓的
- 如果你要關閉某個事件,請使用最後一個“關閉事件”的代碼來關閉,不要使用event關閉,會影響到其他的事件
- event如同是總閘,關閉之後,所有定義的事件斷電
MySQL取得某一範圍隨機數
①直接取值
若要在i ≤ R ≤ j 這個範圍得到一個隨機整數R ,需要用到表達式 FLOOR(i + RAND() * (j – i + 1))。
例如, 若要在7 到 12 的範圍(包括7和12)內得到一個隨機整數, 可使用以下語句:
SELECT FLOOR(7 + (RAND() * 6));
②創建函數
CREATE FUNCTION rand_num ( start_num INTEGER, end_num INTEGER ) RETURNS INTEGER BEGIN RETURN FLOOR(start_num + RAND() * (end_num - start_num + 1)); END;
比如,要獲取1-9的隨機數,如此調用即可:
select rand_num(1, 9);