1. 程式人生 > 實用技巧 >06-Re: 檢視&過程&觸發器

06-Re: 檢視&過程&觸發器

檢視

檢視概述

檢視(View)是一種虛擬存在的表。檢視並不在資料庫中實際存在,行和列資料來自定義檢視的查詢中使用的表,並且是在使用檢視時動態生成的。通俗的講,檢視就是一條 SELECT 語句執行後返回的結果集。所以我們在建立檢視的時候,主要的工作就落在建立這條 SQL 查詢語句上。

檢視相對於普通的表的優勢主要包括以下幾項:

  • 簡單:使用檢視的使用者完全不需要關心後面對應的表的結構、關聯條件和篩選條件,對使用者來說已經是過濾好的複合條件的結果集。
  • 安全:使用檢視的使用者只能訪問他們被允許查詢的結果集,對錶的許可權管理並不能限制到某個行某個列,但是通過檢視就可以簡單的實現。
  • 資料獨立:一旦檢視的結構確定了,可以遮蔽表結構變化對使用者的影響,源表增加列對檢視沒有影響;源表修改列名,則可以通過修改檢視來解決,不會造成對訪問者的影響。

建立或修改檢視

建立檢視的語法為:

CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]

修改檢視的語法為:

ALTER [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]

選項:

WITH [CASCADED | LOCAL] CHECK OPTION 決定了是否允許更新資料使記錄不再滿足檢視的條件。
    LOCAL:只要滿足本檢視的條件就可以更新。
    CASCADED(預設值):必須滿足所有針對該檢視的所有檢視的條件才可以更新。

檢視檢視

從 MySQL 5.1 版本開始,使用 SHOW TABLES 命令的時候不僅顯示錶的名字,同時也會顯示檢視的名字,而不存在單獨顯示檢視的 SHOW VIEWS 命令。

同樣,在使用 SHOW TABLE STATUS\G 命令的時候,不但可以顯示錶的資訊,同時也可以顯示檢視的資訊。

如果需要查詢某個檢視的定義,可以使用 SHOW CREATE VIEW\G

命令進行檢視。

刪除檢視

語法

DROP VIEW [IF EXISTS] view_name [, view_name] ...[RESTRICT | CASCADE]

示例:刪除檢視 city_country_view

DROP VIEW city_country_view;

儲存過程和函式

概述

儲存過程和函式是事先經過編譯並存儲在資料庫中的一段 SQL 語句的集合,呼叫儲存過程和函式可以簡化應用開發人員的很多工作,減少資料在資料庫和應用伺服器之間的傳輸,對於提高資料處理的效率是有好處的。儲存過程和函式的區別在於函式必須有返回值,而儲存過程沒有

  • 函式:是一個有返回值的過程。
  • 過程:是一個沒有返回值的函式。

基本操作

建立儲存過程

語法:

CREATE PROCEDURE procedure_name ([proc_parameter[,...]])
BEGIN
-- SQL語句
END;

示例:

DELIMITER $

CREATE PROCEDURE pro_test1()
BEGIN
select 'Hello Mysql';
END$

DELIMITER ;

呼叫儲存過程

CALL procedure_name();

檢視儲存過程

-- 查詢db_name資料庫中的所有的儲存過程
select name from mysql.proc where db='db_name';

-- 查詢儲存過程的狀態資訊
show procedure status;

-- 查詢某個儲存過程的定義
show create procedure mydb.pro_test1\G;

刪除儲存過程

DROP PROCEDURE [IF EXISTS] sp_name;

語法

儲存過程是可以程式設計的,意味著可以使用變數、表示式、控制結構,來完成比較複雜的功能。

變數

  • DECLARE
    • 通過 DECLARE 可以定義一個區域性變數,該變數的作用範圍只能在 BEGIN...END 塊中。具體語法如下:
      DECLARE var_name[,...] type [DEFAULT value]
      
    • 示例
      DELIMITER $
      CREATE PROCEDURE pro_test2()
      BEGIN
          DECLARE num int default 5;
          SELECT num + 10;
      END$
      DELIMITER ;
      
  • SET
    • 直接賦值使用 SET,可以賦常量或者賦表示式,具體語法如下:
      SET var_name = expr [, var_name = expr] ...
      
    • 示例
      DELIMITER $
      CREATE PROCEDURE pro_test3()
      BEGIN
          DECLARE name varchar(20);
          SET name = 'MYSQL';
          SELECT name;
      END$
      DELIMITER ;
      
  • SELECT INTO
    DELIMITER $
    CREATE PROCEDURE pro_test5()
    BEGIN
        DECLARE countnum int;
        SELECT count(*) INTO countnum FROM city; # 進行賦值操作
        SELECT countnum;
    END$
    DELIMITER ;
    

if 條件判斷

語法結構

IF search_condition THEN statement_list
[ELSEIF search_condition THEN statement_list] ...
[ELSE statement_list]
END IF;

示例:根據定義的身高變數,判定當前身高的所屬的身材型別

DELIMITER $
create procedure pro_test6()
BEGIN
    declare height int default 175;
    declare description varchar(50);
    if height >= 180 then
        set description = '身材高挑';
    elseif height >= 170 and height < 180 then
        set description = '標準身材';
    else
        set description = '一般身材';
    end if;
    select description;
END$
DELIMITER ;

傳遞引數

語法格式

create procedure procedure_name([in/out/inout] 引數名 引數型別)
...

IN      預設,該引數可以作為輸入,也就是需要呼叫方傳入值
OUT     該引數作為輸出,也就是該引數可以作為返回值
INOUT   既可以作為輸入引數,也可以作為輸出引數

IN-輸入

# 根據入參的身高,判定當前身高的所屬的身材型別

DELIMITER $
create procedure pro_test5(in height int)
begin
    declare description varchar(50) default '';
    if height >= 180 then
        set description='身材高挑';
    elseif height >= 170 and height < 180 then
        set description='標準身材';
    else
        set description='一般身材';
    end if;
    select concat('身高 ', height , '對應的身材型別為:',description);
end$
DELIMITER ;

OUT-輸出

# 根據傳入的身高變數,獲取當前身高的所屬的身材型別
DELIMITER $
create procedure pro_test5(in height int , out description varchar(100))
begin
    if height >= 180 then
        set description='身材高挑';
    elseif height >= 170 and height < 180 then
        set description='標準身材';
    else
        set description='一般身材';
    end if;
end$
DELIMITER ;

CALL pro_test5(168, @description);
select @description;

補充:

  • @description : 這種變數要在變數名稱前面加上 '@' 符號,叫做使用者會話變數,代表整個會話過程它都是有作用的,這個類似於全域性變數一樣。
  • @@global.sort_buffer_size:這種在變數前加上 '@@' 符號, 叫做系統變數。

case 結構

語法結構

方式一:
CASE case_value
    WHEN when_value THEN statement_list
    [WHEN when_value THEN statement_list] ...
    [ELSE statement_list]
END CASE;

方式二:
CASE
    WHEN search_condition THEN statement_list
    [WHEN search_condition THEN statement_list] ...
    [ELSE statement_list]
END CASE;

示例:給定一個月份, 然後計算出所在的季度

DELIMITER $
create procedure pro_test9(month int)
begin
    declare result varchar(20);
    case
        when month >= 1 and month <=3 then
            set result = '第一季度';
        when month >= 4 and month <=6 then
            set result = '第二季度';
        when month >= 7 and month <=9 then
            set result = '第三季度';
        when month >= 10 and month <=12 then
            set result = '第四季度';
    end case;
    select concat('您輸入的月份為:', month , ' , 該月份為: ' , result) as content ;
end$
DELIMITER ;

while 迴圈

語法結構

WHILE search_condition DO
    statement_list
END WHILE;

示例:計算從 1 加到 n 的值

DELIMITER $
create procedure pro_test8(n int)
begin
    declare total int default 0;
    declare num int default 1;
    while num<=n do
        set total = total + num;
        set num = num + 1;
    end while;
    select total;
end$
DELIMITER ;

repeat 結構

有條件的迴圈控制語句, 當滿足條件的時候退出迴圈 。while 是滿足條件才執行,repeat 是滿足條件就退出迴圈。具體語法如下:

REPEAT
    statement_list
    UNTIL search_condition # 注意!這裡沒有分號
END REPEAT;

示例:計算從 1 加到 n 的值

DELIMITER $
create procedure pro_test10(n int)
begin
    declare total int default 0;
    repeat
        set total = total + n;
        set n = n - 1;
        until n=0
    end repeat;
    select total ;
end$
DELIMITER ;

loop & leave

LOOP 實現簡單的迴圈,退出迴圈的條件需要使用其他的語句定義,通常可以使用 LEAVE 語句實現,具體語法如下:

[begin_label:] LOOP
    statement_list
END LOOP [end_label]

如果不在 statement_list 中增加退出迴圈的語句,那麼 LOOP 語句可以用來實現簡單的死迴圈。

用來從標註的流程構造中退出,通常和 BEGIN ... END 或者迴圈一起使用。下面是一個使用 LOOP 和 LEAVE 的簡單例子,退出迴圈:

DELIMITER $
CREATE PROCEDURE pro_test11(n int)
BEGIN
    DECLARE total int DEFAULT 0;
    ins: LOOP
        IF n <= 0 then
            leave ins;
        END IF;
        SET total = total + n;
        SET n = n - 1;
    END LOOP ins;
    SELECT total;
END$
DELIMITER ;

遊標/游標

遊標是用來儲存查詢結果集的資料型別,在儲存過程和函式中可以使用游標對結果集進行迴圈的處理。游標的使用包括游標的宣告、OPEN、FETCH 和 CLOSE,其語法分別如下。

  • 宣告游標:DECLARE cursor_name CURSOR FOR select_statement;
  • OPEN 游標:OPEN cursor_name;
  • FETCH 游標:FETCH cursor_name INTO var_name [, var_name] ...;
  • CLOSE 游標:CLOSE cursor_name;

示例:遍歷 emp 表中的記錄。

初始化指令碼

create table emp(
    id int(11) not null auto_increment ,
    name varchar(50) not null comment '姓名',
    age int(11) comment '年齡',
    salary int(11) comment '薪水',
    primary key(`id`)
)engine=innodb default charset=utf8;

insert into emp(id,name,age,salary) values
(null,'金毛獅王',55,3800),
(null,'白眉鷹王',60,4000),
(null,'青翼蝠王',38,2800),
(null,'紫衫龍王',42,1800);

通過迴圈結構,獲取遊標中的資料:

DELIMITER $
CREATE PROCEDURE pro_test11()
BEGIN
    DECLARE e_id int(11);
    DECLARE e_name varchar(50);
    DECLARE e_age int(11);
    DECLARE e_salary int(11);

    DECLARE has_data int DEFAULT 1;

    DECLARE emp_result CURSOR FOR select * from emp;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET has_data=0;

    OPEN emp_result;

    REPEAT
        FETCH emp_result INTO e_id, e_name, e_age, e_salary;
        SELECT CONCAT('id=',e_id , ', name=',e_name, ', age=', e_age, ', 薪資為:',e_salary);
        UNTIL has_data = 0
    END REPEAT;
    CLOSE emp_result;
END$
DELIMITER ;

函式

語法結構

CREATE FUNCTION function_name([param type ... ])
RETURNS type
BEGIN
    ...
END;

示例:定義一個儲存過程,請求滿足條件的總記錄數。

DELIMITER $
create function count_city(countryId int)
returns int
BEGIN
    DECLARE cnum int;
    select count(*) into cnum from city where country_id = countryId;
    RETURN cnum;
END$
DELIMITER ;

呼叫

select count_city(1);
select count_city(2);

觸發器

簡述

觸發器是與表有關的資料庫物件,指在 insert/update/delete 之前或之後,觸發並執行觸發器中定義的 SQL 語句集合。觸發器的這種特性可以協助應用在資料庫端確保資料的完整性、日誌記錄、資料校驗等操作 。

使用別名 OLD 和 NEW (也稱為“行記錄變數”)來引用觸發器中發生變化的記錄內容,這與其他的資料庫是相似的。現在觸發器還只支援行級觸發,不支援語句級觸發。

建立觸發器

語法結構

CREATE TRIGGER trigger_name
BEFORE/AFTER insert/update/delete
ON tbl_name
[ FOR EACH ROW ] -- 行級觸發器
BEGIN
    trigger_stmt;
END;

示例:通過觸發器記錄 emp 表的資料變更日誌,包含增加、修改、刪除。

  1. 首先建立一張日誌表
    create table emp_logs(
        id int(11) not null auto_increment,
        operation varchar(20) not null comment '操作型別, insert/update/delete',
        operate_time datetime not null comment '操作時間',
        operate_id int(11) not null comment '操作表的ID',
        operate_params varchar(500) comment '操作引數',
        primary key(`id`)
    )engine=innodb default charset=utf8;
    
  2. 建立 insert 型觸發器,完成插入資料時的日誌記錄。
    DELIMITER $
    CREATE TRIGGER emp_logs_insert_trigger
    AFTER insert
    ON emp
    for each row
    BEGIN
        insert into emp_logs (id,operation,operate_time,operate_id,operate_params)
        values (null, 'insert', now(), new.id, concat('插入後(id:', new.id, ',name:',
        new.name, ',age:', new.age, ',salary:', new.salary, ')'));
    END $
    DELIMITER ;
    
  3. 建立 update 型觸發器,完成更新資料時的日誌記錄。
    DELIMITER $
    CREATE TRIGGER emp_logs_update_trigger
    AFTER update
    ON emp
    for each row
    BEGIN
        insert into emp_logs (id,operation,operate_time,operate_id,operate_params)
        values(null, 'update', now(), new.id, concat('修改前(id:', old.id, ',name:',
        old.name, ',age:', old.age, ',salary:', old.salary, '),修改後(id', new.id,
        'name:', new.name, ',age:', new.age, ',salary:', new.salary, ')'));
    END $
    DELIMITER ;
    
  4. 建立 delete 行的觸發器,完成刪除資料時的日誌記錄。
    DELIMITER $
    CREATE TRIGGER emp_logs_delete_trigger
    AFTER delete
    ON emp
    for each row
    BEGIN
        insert into emp_logs (id,operation,operate_time,operate_id,operate_params)
        values(null, 'delete', now(), old.id, concat('刪除前(id:', old.id, ',name:',
        old.name, ',age:', old.age, ',salary:', old.salary, ')'));
    END $
    DELIMITER ;
    

刪除觸發器

DROP TRIGGER [schema_name.]trigger_name

如果沒有指定 schema_name,預設為當前資料庫 。

檢視觸發器

可以通過執行 SHOW TRIGGERS 命令檢視觸發器的狀態、語法等資訊。

SHOW TRIGGERS\G;