30.4. MySQL函數,存儲過程,觸發器,視圖

函數

函數:系統函數和自定義函數
系統函數:https://dev.mysql.com/doc/refman/8.0/en/func-op-summary-ref.html
函數也可以進入數據庫之後利用help命令來查看幫助,比如help max
自定義函數 (user-defined function UDF)

  • 注自定義函數定義之後保存在mysql.proc(mysql.func)表中
  • 創建UDF
    CREATE [AGGREGATE] FUNCTION function_name(parameter_name type,[parameter_name type,...]) RETURNS {STRING|INTEGER|REAL}
    runtime_body
  • 說明:
    參數可以有多個,也可以沒有參數
    必須得有,且只能有一個返回值

創建函數

示例1:無參UDF
CREATE FUNCTION simpleFun() RETURNS VARCHAR(20)
RETURN "Hello World!”;

  • 查看自定義的函數列表:
    SHOW FUNCTION STATUS;
    select * from mysql.proc(mysql.func)\G;
  • 查看函數定義
    SHOW CREATE FUNCTION function_name;
  • 查看函數的code
    SHWO FUNCTION CODE;
  • 刪除UDF:
    DROP FUNCTION function_name;
  • 調用自定義函數語法:
    SELECT function_name(parameter_value,...);

示例2:有參數UDF
DELIMITER //
CREATE FUNCTION deleteById(uid SMALLINT UNSIGNED) RETURNS
VARCHAR(20)
BEGIN
DELETE FROM students WHERE stuid = uid;
RETURN (SELECT COUNT(stuid) FROM students);
END//
DELIMITER ;

  • 其中的delimiter代表分隔符,先切換成//(也可以寫其他的符號),然後函數定義完了之後再切換回;
  • 這樣做用於避免函數的的內容被執行,因爲函數體內的命令有分號作爲結尾,不先修改分隔符的話會被當做命令來執行。

自定義函數中定義局部變量語法

DECLARE 變量1[,變量2,... ] 變量類型 [DEFAULT 默認值]

  • 說明:局部變量的作用範圍是在BEGIN...END程序中,而且定義局部變量語句必須在BEGIN...END的第一行定義

示例3:
DELIMITER //
CREATE FUNCTION addTwoNumber(x SMALLINT UNSIGNED, Y SMALLINT
UNSIGNED) RETURNS SMALLINT
BEGIN
DECLARE a, b SMALLINT UNSIGNED;
SET a = x, b = y;
RETURN a+b;
END//
DELIMITER ;

  • 在這裏的函數中定義本地變量的時候,要使用declare命令(主義和set區別),一行中的變量的類型只需要寫一次(寫在最後)即可,它代表把前面所有的變量都定義爲這種類型的
  • 寫在begin和and之間進行定義的變量都是局部變量,在這裏類似C語言,且變量前面不用加任何符號直接定義和使用即可

爲變量賦值語法

SET @parameter_name = value[,parameter_name = value...]
SELECT ... INTO parameter_name
示例4:
...
DECLARE x int;
SELECT COUNT(id) FROM tdb_name INTO x;
RETURN x;
END//

存儲過程

存儲過程優勢

  • 存儲過程把經常使用的SQL語句或業務邏輯封裝起來,預編譯保存在數據庫中,當需要時從數據庫中直接調用,省去了編譯的過程
  • 提高了運行速度
  • 同時降低網絡數據傳輸量
    存儲過程與自定義函數的區別
  • 存儲過程實現的過程要複雜一些,而函數的針對性較強
  • 存儲過程可以有多個返回值,而自定義函數只有一個返回值
  • 存儲過程一般可獨立執行,而函數往往是作爲其他SQL語句的一部分來使用

存儲過程:存儲過程保存在mysql.proc表中

  • 創建存儲過程
    CREATE PROCEDURE sp_name ([ proc_parameter [,proc_parameter ...]]) routime_body
    proc_parameter : [IN|OUT|INOUT] parameter_name type
    其中IN表示輸入參數,OUT表示輸出參數,INOUT表示既可以輸入也可以輸出;param_name表示參數名稱;type表示參數的類型
  • 查看存儲過程列表
    SHOW PROCEDURE STATUS;
    select * from mysql.proc\G;
  • 查看存儲過程定義
    SHOW CREATE PROCEDURE sp_name
  • 調用存儲過程
    CALL sp_name ([ proc_parameter [,proc_parameter ...]])
    CALL sp_name
    說明:當無參時,可以省略"()",當有參數時,不可省略"()”
  • 存儲過程修改
    ALTER語句修改存儲過程只能修改存儲過程的註釋等無關緊要的東西,不能修改存儲過程體,所以要修改存儲過程,方法就是刪除重建
  • 刪除存儲過程
    DROP PROCEDURE [IF EXISTS] sp_name

存儲過程示例

  1. 創建無參存儲過程

delimiter //
CREATE PROCEDURE showTime()
BEGIN
SELECT now();
END//
delimiter ;

CALL showTime;

  1. 創建含參存儲過程:只有一個IN參數

delimiter //
CREATE PROCEDURE selectById(IN uid SMALLINT UNSIGNED)
BEGIN
SELECT * FROM students WHERE stuid = uid;
END//
delimiter ;

call selectById(2);

  1. 創建包含有會話級全局變量的存儲過程

delimiter //
CREATE PROCEDURE dorepeat(n INT)
BEGIN
SET @i = 0;
SET @sum = 0;
REPEAT SET @sum = @sum+@i; SET @i = @i + 1;
UNTIL @i > n END REPEAT;
END//
delimiter ;

CALL dorepeat(100);
SELECT @sum;

  • 注意這裏用到了循環體repeat ... until ... END repeat;
  • 因爲是用set命令定義的自定義的變量,所以就算在存儲過程外也能夠直接使用它。
  • 在這裏用了set定義會話級全局變量的方式把過程內的結果傳入到外面,更簡單的方法就是下面4中的定義一個OUT參數,用它把結果傳到外面(不過也得利用外部的會話級自定義全局變量接收)。
  1. 創建含參存儲過程:包含IN參數和OUT參數

delimiter //
CREATE PROCEDURE deleteById(IN uid SMALLINT UNSIGNED, OUT num SMALLINT UNSIGNED)
BEGIN
DELETE FROM students WHERE stuid >= uid;
SELECT row_count() into num; #注意這裏的形參並沒有用@,看注意點7
END//
delimiter ;

call deleteById(2,@Line);
SELECT @Line;

  • 說明:創建存儲過程deleteById,包含一個IN參數和一個OUT參數.調用時,傳入刪除的ID和保存被修改的行的數值的用戶變量@Line
  • 然後用select @Line;輸出被修改後的行的值(這裏也就指的是被刪除掉了多少行的行數值,刪除這些行的判斷條件由傳入的參數uid指定)。
  • 可用help row_count 查看此函數的解釋

流程控制

存儲過程和函數中可以使用流程控制來控制語句的執行
流程控制:

  • IF:用來進行條件判斷。根據是否滿足條件,執行不同語句
  • CASE:用來進行條件判斷,可實現比IF語句更復雜的條件判斷
  • LOOP:重複執行特定的語句,實現一個簡單的循環
  • LEAVE:用於跳出循環控制 (類似break)
  • ITERATE:跳出本次循環,然後直接進入下一次循環 (類似continue)
  • REPEAT:有條件控制的循環語句。當滿足特定條件時,就會跳出循環語句
  • WHILE:有條件控制的循環語句

trigger觸發器(類似ansible中的handler)

觸發器的執行不是由程序調用,也不是由手工啓動,而是由事件來觸發、激活從而實現執行
創建觸發器
CREATE
[DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
trigger_body
說明:
trigger_name:觸發器的名稱
trigger_time:{ BEFORE | AFTER },表示在事件之前或之後觸發
trigger_event::{ INSERT |UPDATE | DELETE },觸發的具體事件
tbl_name:該觸發器作用在表名

查看觸發器

  1. SHOW TRIGGERS
  2. 查詢系統表information_schema.triggers的方式指定查詢條件,查看指定的觸發器信息:
    mysql> USE information_schema;
    Database changed
    mysql> SELECT * FROM triggers WHERE
    trigger_name='trigger_student_count_insert';
    刪除觸發器
    DROP TRIGGER trigger_name;

觸發器示例

=================先定義一張表:
CREATE TABLE student_info (
stu_id INT(11) NOT NULL AUTO_INCREMENT,
stu_name VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (stu_id)
);
CREATE TABLE student_count (
student_count INT(11) DEFAULT 0
);
INSERT INTO student_count VALUES(0);

================然後創建觸發器,在向學生表INSERT數據時,學生數增加,DELETE學生時,學生數減少
CREATE TRIGGER trigger_student_count_insert
AFTER INSERT
ON student_info FOR EACH ROW  :這裏要指定有事件變化的表(info表),別指定成了count表
UPDATE student_count SET student_count=student_count+1;
CREATE TRIGGER trigger_student_count_delete
AFTER DELETE
ON student_info FOR EACH ROW
UPDATE student_count SET student_count=student_count-1;

注意這裏的ON TABLE_NAME中的table_name指定的是變化的表和事件,它和trigger_body中的命令並無直接聯繫
body中的SQL語句可以對任何表進行任何操作,和上面ON後面的表並無直接聯繫,那個表只適用於判斷事件的發生,事件發生後就和body無關了(也就是說並非只能針對ON後面的這個表進行操作,body命令沒有限制)

注意點:

  1. 自定義函數存儲的位置是在mysql.proc表中,這是一個表。
  2. 函數和存儲過程(本質也是一種函數)的定義可以用交互式方式進行,也可以用非交互式方式先寫到文件內,然後導入即可(數據庫外直接重定向,連接到數據庫內則用source命令)。
    • 注意這裏只是定義了函數和存儲過程,而並沒有調用或者執行它們。
  3. 注意,與示例3中函數體中的局部變量相對應的會話級的全局變量可以在mysql的交互式命令行中用set命令來進行定義賦值;set命令不僅可以定義並賦值用戶自己定義的變量,還可以用來修改系統自身存在的變量的值。
    • 不過系統自身的變量的值是否能夠直接在mysql命令行中修改,以及系統變量生效的範圍(global,session也就是這個端口)等等,會在下一章進行詳細介紹。
    • 這裏需要注意的就是如果是global類型的變量,只有在mysql服務重啓的時候,它纔會重新恢復到默認值(假設沒寫入配置文件且用set修改了它的值);而session級別的,只要退出了mysql的連接,下次客戶端重新連接mysql服務的時候它都會恢復到默認值(包括另外的終端上連接的也是)。除非將它寫入配置文件中(有相對應的選項)並且重啓mysql服務。
    • 另外一點要注意的就是用mysqladmin能夠修改的變量會在重啓mysql服務後才恢復默認值,而不是客戶端mysql連接到服務中後。因爲mysqladmin修改的就是服務器端的配置,如果沒有寫入配置文件,則服務器端重啓之後會恢復到默認值。它和mysql客戶端命令是沒有關係的。
  4. 注意用set命令定義和賦值自定義變量的時候要在變量名字前面加上@符號,不然會被當做系統變量。這個變量雖然是全局可用的,但它也是出於會話級別的全局,新開一個會話(終端端口)就不能再用了。
    • 顯示這個變量的時候要用select @變量名 ;
    • 注意show variables [like '系統變量名'] 命令只能顯示系統定義的變量
  5. 還可以用into命令把聚合函數的結果賦值到自定義變量中:
    • select avg(age) form students into @avg_age; select @avg_age;
  6. 存儲過程就相當於是一部分SQL語句和操作的合集的封裝,和函數很類似(其實本質就是一種特殊的function)
  7. 函數和存儲過程中的變量使用的的時候(不論是定義的時候用的形參,還是說內部定義的本地變量)都不需要在變量或參數前面加上@符號,只有用到了set命令自定義變量賦值或者外部的自定義變量的時候,才需要在參數名前面加上@符號。
    • 注意一點就是表中的字段用set修改賦值的時候也不需要加上@符號,這裏的字段雖然也能夠進行各種計算和條件判斷(where)等,但是字段和變量並不一樣。(參考上面trigger的示例中的set命令,由此想起來這一點的)

視圖

視圖:VIEW,虛表,保存有實表的查詢結果

  • 創建方法:
    CREATE VIEW view_name [(column_list)]
    AS select_statement
    [WITH [CASCADED | LOCAL] CHECK OPTION]
  • 查看視圖定義:SHOW CREATE VIEW view_name
  • 刪除視圖:
    DROP VIEW [IF EXISTS]
    view_name [, view_name] ...
    [RESTRICT | CASCADE]
    注意:視圖中的數據事實上存儲於“基表”中,因此,其修改操作也會針對基表實現;其修改操作受基表限制

視圖注意點:

  1. 因爲視圖創建完之後在數據庫中就像真的表一般無法區分,(此時當然也不可能用show create view view_name的方式來查看,因爲不知道是view 還是 table)
    這時候只可以用 show table status like 'view_name'\G; 的方式來判斷這個表到底是視圖還是表格了。
  2. 視圖可以當做是一個複雜的查詢語句的別名,可以把內連接的查詢結果創建一個視圖,這樣就不用每次輸入很長的的命令來進行內連接查詢了。
  3. 對視圖的修改如果修改成功了,則對應的原表也會相應的修改;不過對視圖的修改有很多要求,比如必須要是單表,不能有聚合函數等等限制,詳細更多要求以後再研究。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章