MYSQL存儲過程梳理

目錄

事件調度器

事件任務

存儲過程

錯誤處理

MySQL  存儲過程 if語句

MySQL IF語句語法

MySQL IF ELSE語句

MySQL IF ELSEIF ELSE語句

MySQL IF語句示例

IF表達式

作爲表達式的if也可以用CASE when來實現:

IFNULL(expr1,expr2)

IF ELSE 做爲流程控制語句使用

MySql中創建定時任務

MySQL取得某一範圍隨機數

①直接取值

②創建函數


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

在兩種計劃任務中,時間戳可以是任意的TIMESTAMPDATETIME數據類型,時間戳需要大於當前時間。

在重複的計劃任務中,時間(單位)的數量可以是任意非空(NOT NULL)的整數形式,時間單位是關鍵詞:YEARMONTHDAYHOURMINUTESECOND...

 

[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,FALSENULL,這三個值之一。

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');
複製代碼

 

看看效果:

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

ok,表和數據做好了,我們說一下需求:

需求1:沒經過2秒管理員的積分加1,字段is_adm爲1的則是管理員

 

以上爲添加表和數據,並提出需求

--------------------------------分割線--------------------------------

以下內容,我們會提到如何啓動定時任務,來完成我們的需求

 

首先第一步,我們要檢查我們數據庫,是否開啓了event(事件)

SHOW VARIABLES LIKE 'event_scheduler' #查看狀態

 

運行以上代碼,如果你的結果,Value字段的值爲"OFF",代表你的事件屬於關閉狀態,那麼我們就要開啓它,運行以下代碼:

SET GLOBAL event_scheduler = ON;  #ON開啓,OFF關閉

運行之後,再返回上一個,再次檢查事件的狀態,當結果爲"ON",則代表你的事件已開啓,我們就繼續往下走。。

 

既然是定時任務,那麼我們就要封裝一個函數,流程是這樣的:

  1. 封裝一個函數
  2. 封裝一個事件
  3. 事件開始運行,滿足條件,調用 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));

②創建函數

複製代碼wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

CREATE FUNCTION rand_num (
    start_num INTEGER,
    end_num INTEGER
) RETURNS INTEGER
BEGIN
    RETURN FLOOR(start_num + RAND() * (end_num - start_num + 1));
END;

複製代碼wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

比如,要獲取1-9的隨機數,如此調用即可:

select rand_num(1, 9);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章