MySQL 視圖、觸發器、函數、存儲過程

1. 視圖

1.1 什麼是視圖mysql

通俗來說,視圖就是一條 select 語句執行後返回的結果集。全部咱們在建立視圖的時候,主要的工做就落在建立這條SQL查詢語句上。算法

 

1.2 視圖的特性sql

視圖是對若干張基本表的引用,一張虛表,查詢語句的執行結果,不存儲具體的數據(基本表數據發生了改變,視圖也會跟着改變)數據庫

 

1.3 視圖的做用編程

方便操做,特別是查詢操做,減小複雜的SQL語句,加強可讀性;更加安全,數據庫受權命令不能限定到特定的行和特定的列,但經過合理建立視圖,能夠把權限限定到行列級別;安全

 

1.4 使用場合oracle

權限控制的時候,不但願用戶訪問表中某些敏感信息的列,好比 salary… 關鍵信息來源於多個複雜關聯表,能夠建立視圖提取咱們須要的信息,簡化操做;ide

 

1.5 視圖的使用函數

視圖實例1-建立視圖及查詢數據操做fetch

現有三張表:用戶(user)、課程(course)、用戶課程中間表(user_course),表結構及數據以下:

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for course
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
  `sid` int(11) NOT NULL AUTO_INCREMENT,
  `sname` varchar(32) NOT NULL,
  PRIMARY KEY (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES ('1', '語文');
INSERT INTO `course` VALUES ('2', '數學');
INSERT INTO `course` VALUES ('3', '英語');
INSERT INTO `course` VALUES ('4', '物理');
INSERT INTO `course` VALUES ('5', '');

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `course_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_student_course` (`course_id`),
  CONSTRAINT `fk_student_course` FOREIGN KEY (`course_id`) REFERENCES `course` (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', '小飛', '1');
INSERT INTO `student` VALUES ('2', 'hukey', '2');
INSERT INTO `student` VALUES ('3', '小王', '3');
INSERT INTO `student` VALUES ('4', '阿狗', '4');
表定義及數據

這時,當咱們想要查詢小飛上的因此課程相關信息的時候,須要這樣寫一條長長的SQL語句,以下:

SELECT sid, sname, student.name from course 
LEFT JOIN student on course.sid = student.course_id
where student.name = '小飛';

可是咱們能夠經過視圖簡化操做,例如咱們建立視圖 view_student_course 以下:

create ALGORITHM = UNDEFINED
DEFINER = 'root'@'%'
SQL SECURITY DEFINER
VIEW view_student_course AS (
SELECT sid, sname, student.name from course 
LEFT JOIN student on course.sid = student.course_id
);

幾點說明(MySQL中的視圖在標準SQL的基礎之上作了擴展):

ALGORITHM=UNDEFINED:指定視圖的處理算法;
DEFINER=`root`@`localhost`:指定視圖建立者;
SQL SECURITY DEFINER:指定視圖查詢數據時的安全驗證方式;

 

建立好視圖以後,咱們能夠直接用如下SQL語句在視圖上查詢小飛上的因此課程相關信息,一樣能夠獲得所需結果:

SELECT * from view_student_course where name = '小飛';

 

能夠嘗試對視圖進行增刪改操做,這裏總結以下:

  (1)視圖與表是一對一關係狀況:若是沒有其它約束(如視圖中沒有的字段,在基本表中是必填字段狀況),是能夠進行增刪改數據操做;
  (2)視圖與表是一對多關係狀況:若是隻修改一張表的數據,且沒有其它約束(如視圖中沒有的字段,在基本表中是必填字段狀況),是能夠進行改數據操做;

 

除了以上兩條外都是沒法進行增刪改,可是強烈不建議直接對視圖進行增刪改操做,可能不經意就修改了真實表中的多條數據

 

查看庫中的視圖:

show table status where comment = 'view';

 

 

 

2. 觸發器

 

2.1 什麼是觸發器

觸發器是與表有關的數據庫對象,在知足定義條件時觸發,並執行觸發器中定義的語句集合。

觸發器的特性:

  1. 在 begin end體, begin … end; 之間的語句能夠寫的簡單或者複雜
  2. 什麼條件觸發:insert、update、delete
  3. 何時觸發:在增刪改前或者後
  4. 觸發頻率: 針對每一行執行
  5. 觸發器定義在表上,附着在表上

 

也就是由事件來觸發某個操做,事件包括INSERT語句,UPDATE語句和DELETE語句;能夠協助應用在數據庫端確保數據的完整性。

 

儘可能少使用觸發器、不建議使用

假設觸發器觸發每次執行1s,insert table 500條數據,那麼就須要觸發500次觸發器,光是觸發器執行的時間就花費了500s,而insert 500條數據一共是1s,那麼這個insert的效率就很是低了。所以咱們特別須要注意的一點是觸發器的begin end;之間的語句的執行效率必定要高,資源消耗要小。

 

2.2 觸發器的建立

CREATE
    [DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
[trigger_order]
trigger_body

trigger_time: { BEFORE | AFTER }

trigger_event: { INSERT | UPDATE | DELETE }

trigger_order: { FOLLOWS | PRECEDES } other_trigger_name

 

trigger_time: { BEFORE | AFTER }
    BEFORE 和 AFTER 參數指定了觸發的時間,在事件以前或以後

FOR EACH ROW
    表示任何一條記錄上的操做知足觸發事件都會觸發該觸發器,也就是說觸發器的觸發頻率是針對每一行數據觸發一次。
    
trigger_event: { INSERT | UPDATE | DELETE }
    (1)INSERT型觸發器:插入某一行時激活觸發器,可能經過INSERT、LOAD DATA、REPLACE 語句觸發(LOAD DAT語句用於將一個文件裝入到一個數據表中,至關與一系列的INSERT操做);
    (2)UPDATE型觸發器:更改某一行時激活觸發器,可能經過UPDATE語句觸發;
    (3)DELETE型觸發器:刪除某一行時激活觸發器,可能經過DELETE、REPLACE語句觸發。

 

2.3 建立只有一個執行語句的觸發器

CREATE TRIGGER 觸發器名 BEFORE|AFTER 觸發事件 ON 表名 FOR EACH ROW 執行語句;

例1:建立了一個名爲trig1的觸發器,一旦在work表中有插入動做,就會自動往time表裏插入當前時間

mysql> CREATE TRIGGER trig1 AFTER INSERT
    -> ON work FOR EACH ROW
    -> INSERT INTO time VALUES(NOW());

 

2.4 建立有多個執行語句的觸發器

CREATE TRIGGER 觸發器名 BEFORE|AFTER 觸發事件
ON 表名 FOR EACH ROW
BEGIN
        執行語句列表
END;
mysql> DELIMITER ||
mysql> CREATE TRIGGER trig2 BEFORE DELETE
    -> ON work FOR EACH ROW
    -> BEGIN
    -> INSERT INTO time VALUES(NOW());
    -> INSERT INTO time VALUES(NOW());
    -> END||
mysql> DELIMITER ;

 

 

2.5 NEW 和 OLD 詳解

MySQL 中定義了 NEW 和 OLD,用來表示觸發器的所在表中,觸發了觸發器的那一行數據,來引用觸發器中發生變化的記錄內容,具體地:
    (1)在INSERT型觸發器中,NEW用來表示將要(BEFORE)或已經(AFTER)插入的新數據;
    (2)在UPDATE型觸發器中,OLD用來表示將要或已經被修改的原數據,NEW用來表示將要或已經修改成的新數據;
    (3)在DELETE型觸發器中,OLD用來表示將要或已經被刪除的原數據;

 

使用方法:

  NEW.columnName (columnName爲相應數據表某一列名)

另外,OLD是隻讀的,而NEW則能夠在觸發器中使用 SET 賦值,這樣不會再次觸發觸發器,形成循環調用(如每插入一個學生前,都在其學號前加「2013」)。

mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);

mysql> delimiter $$
mysql> CREATE TRIGGER upd_check BEFORE UPDATE ON account
    -> FOR EACH ROW
    -> BEGIN
    -> IF NEW.amount < 0 THEN
    -> SET NEW.amount = 0;
    -> ELSEIF NEW.amount > 100 THEN
    -> SET NEW.amount = 100;
    -> END IF;
    -> END$$
mysql> delimiter ;

mysql> update account set amount=-10 where acct_num=137;

mysql> select * from account;
+----------+---------+
| acct_num | amount  |
+----------+---------+
|      137 |    0.00 |
|      141 | 1937.50 |
|       97 | -100.00 |
+----------+---------+

mysql> update account set amount=200 where acct_num=137;

mysql> select * from account;
+----------+---------+
| acct_num | amount  |
+----------+---------+
|      137 |  100.00 |
|      141 | 1937.50 |
|       97 | -100.00 |
+----------+---------+

 

 

2.6 查看觸發器

mysql> SHOW TRIGGERS\G;
……

結果,顯示全部觸發器的基本信息;沒法查詢指定的觸發器。


在information_schema.triggers表中查看觸發器信息
mysql> SELECT * FROM information_schema.triggers\G
……

結果,顯示全部觸發器的詳細信息;同時,該方法能夠查詢制定觸發器的詳細信息。
mysql> select * from information_schema.triggers 
    -> where trigger_name='upd_check'\G;

 

 

刪除觸發器

DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name

 

刪除觸發器以後最好使用上面的方法查看一遍;同時,也可使用database.trig來指定某個數據庫中的觸發器。

 

注意:
若是不須要某個觸發器時必定要將這個觸發器刪除,以避免形成意外操做,這很關鍵。

 

3. 函數

 

3.1 什麼是函數

函數存儲着一系列sql語句,調用函數就是一次性執行這些語句。因此函數能夠下降語句重複。但要注意的是函數注重返回值,不注重執行過程,因此一些語句沒法執行。因此函數並非單純的sql語句集合。mysql有內置函數,也可以自定義函數

補充函數與存儲過程的區別:函數只會返回一個值,不容許返回一個結果集。函數強調返回值,因此函數不容許返回多個值的狀況,即便是查詢語句。

 

3.2 函數的建立

語法:

Create function function_name(參數列表)
returns 返回值類型
BEGIN
函數體內容
END

相關說明:

        函數名:應該合法的標識符,而且不該該與已有的關鍵字衝突。一個函數應該屬於某數據庫,可使用db_name.funciton_name的形式執行當前函數所屬數據庫,不然默認爲當前數據庫。
        參數列表:能夠有一個或多個函數參數,甚至是沒有參數也是能夠的。對於每一個參數,由參數名和參數類型組成。
        返回值: 指明返回值類型
        函數體:自定義函數的函數體由多條可用的MySQL語句,流程控制,變量申明等語句構成。須要指明的是函數體中必定要含有return 返回語句。

 

3.3 自定義示例

  (1)無參數函數定義

delimiter $$

CREATE FUNCTION hello()
RETURNS VARCHAR(255)
BEGIN
RETURN 'Hello world, i am mysql';
END $$

delimiter ;

 

調用函數:

MariaDB [db1]> select hello();
+-------------------------+
| hello()                 |
+-------------------------+
| Hello world, i am mysql |
+-------------------------+

 

  (2)含有參數的自定義函數

delimiter $$
CREATE FUNCTION f1(
    t1 int,
    t2 int)
RETURNS INT
BEGIN
    DECLARE num int;
  set num = t1 + t2;
  RETURN(num);
END $$
delimiter ;

 

調用函數:

MariaDB [db1]> select f1(1, 100);
+------------+
| f1(1, 100) |
+------------+
|        101 |
+------------+

 

3.4 查看庫中的函數

-- 查看函數
show FUNCTION status;

-- 查看函數的建立過程:
show create function func_name;

 

 

4. 存儲過程

 

4.1 什麼是存儲過程

一組可編程的函數,是爲了完成特定功能的SQL語句集,經編譯建立並保存在數據庫中,用戶可經過指定存儲過程的名字並給定參數(須要時)來調用執行。

優勢:
    (1)將重複性很高的一些操做,封裝到一個存儲過程當中,簡化了對這些SQL的調用;
    (2)批量處理:SQL+循環,減小流量,也就是「跑批」;
    (3)統一接口,確保數據的安全

 

相對於oracle數據庫來講,MySQL的存儲過程相對功能較弱,使用較少。

 

4.2 存儲過程的建立和調用

  存儲過程就是具備名字的一段代碼,用來完成一個特定的功能,建立的存儲過程保存在數據庫的數據字典中。

 

建立存儲過程

 

CREATE
    [DEFINER = { user | CURRENT_USER }]
PROCEDURE sp_name ([proc_parameter[,...]])
    [characteristic ...] routine_body

proc_parameter:
    [ IN | OUT | INOUT ] param_name type

characteristic:
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }

routine_body:
Valid SQL routine statement

[begin_label:] BEGIN
[statement_list]
……
END [end_label]

 

 

 現有兩張表(userinfo)和(teacher)表,表結構及數據:

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
  `tid` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  PRIMARY KEY (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('1', '周杰倫');
INSERT INTO `teacher` VALUES ('2', '那英');
INSERT INTO `teacher` VALUES ('3', '汪峯');
INSERT INTO `teacher` VALUES ('4', '哈林');

-- ----------------------------
-- Table structure for userinfo
-- ----------------------------
DROP TABLE IF EXISTS `userinfo`;
CREATE TABLE `userinfo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(10) NOT NULL,
  `password` varchar(32) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of userinfo
-- ----------------------------
INSERT INTO `userinfo` VALUES ('1', 'admin', 'admin');
INSERT INTO `userinfo` VALUES ('2', 'superman', '123456');
INSERT INTO `userinfo` VALUES ('3', 'batman', '666');
表結構及數據

 

 

建立一個存儲過程:

delimiter $$
CREATE PROCEDURE p1()
BEGIN
    select * from teacher;
    insert into userinfo(username, password) VALUES ('xiaoA', '123');
END $$
delimiter ;

 

 

執行存儲過程:

MariaDB [db1]> call p1;
+-----+-----------+
| tid | name      |
+-----+-----------+
|   1 | 周杰倫    |
|   2 | 那英      |
|   3 | 汪峯      |
|   4 | 哈林      |
+-----+-----------+
4 rows in set (0.00 sec)

Query OK, 1 row affected (0.00 sec)

解析:
    這個存儲過程作了兩件事,一個是查詢全部的teacher,另外一個就是向student表中插入一條數據

 

 4.3 存儲過程的參數

   存儲過程能夠有 0 個或多個參數,用於存儲過程的定義。

  3 種參數類型:

    (1)IN 輸入參數:表示調用者向過程傳入值(傳入值能夠是字面量或變量);

    (2)OUT輸出參數:表示過程向調用者傳出值(能夠返回多個值)(傳出值只能是變量);

    (3)INOUT輸入輸出參數:既表示調用者向過程傳入值,又表示過程向調用者傳出值(值只能是變量)

 

IN輸入參數的使用

delimiter $$
CREATE PROCEDURE p2(in t1 int)
BEGIN
    SELECT t1;
  set t1 = 2;
    SELECT t1;
END $$
delimiter ;

 

 調用存儲過程:

MariaDB [db1]> set @t1 = 1;
Query OK, 0 rows affected (0.00 sec)

MariaDB [db1]> call p2(@t1);
+------+
| t1   |
+------+
|    1 |
+------+


+------+
| t1   |
+------+
|    2 |
+------+

MariaDB [db1]> select @t1;
+------+
| @t1  |
+------+
|    1 |
+------+

以上能夠看出,t1 在存儲過程當中被修改,但並不影響@t1 的值,由於前者爲局部變量、後者爲全局變量。

 

OUT 輸出參數

delimiter $$
CREATE PROCEDURE p3(out t_out int)
BEGIN
    SELECT t_out;
  set t_out = 2;
    SELECT t_out;
END $$
delimiter ;

調用存儲過程:

MariaDB [db1]> set @t_out =1 ;

MariaDB [db1]> call p3(@t_out);
+-------+
| t_out |
+-------+
|  NULL |
+-------+
# 由於out是向調用者輸出參數,不接收輸入的參數,因此存儲過程裏的p_out爲null

+-------+
| t_out |
+-------+
|     2 |
+-------+

MariaDB [db1]> select @t_out;
+--------+
| @t_out |
+--------+
|      2 |
+--------+
# 調用了 p3 存儲過程,輸出參數,改變了 t_out 變量的值

 

 

inout輸入參數

 

delimiter $$
CREATE PROCEDURE p4(inout t_inout int)
BEGIN
    SELECT t_inout;
  set t_inout = 2;
    SELECT t_inout;
END $$
delimiter ;

 

 調用存儲過程:

MariaDB [db1]> set @t_inout = 1;

MariaDB [db1]> call p4(@t_inout);
+---------+
| t_inout |
+---------+
|       1 |
+---------+

+---------+
| t_inout |
+---------+
|       2 |
+---------+

MariaDB [db1]> select @t_inout;
+----------+
| @t_inout |
+----------+
|        2 |
+----------+

 

 

調用了 p4 存儲過程,接受了輸入的參數,也輸出參數,改變了變量

 

注意:

   (1)若是過程沒有參數,也必須在過程名後面寫上小括號

   (2)確保參數的名字不等於列的名字,不然在過程體中,參數名被當作列名來處理

 

建議使用:

  輸入值使用 in 參數;

  輸入值使用 in 參數;

  inout參數就儘可能少用

 

4.4 存儲過程-事務

  在執行一個存儲過程當中,咱們沒法肯定這個存儲過程是否執行成功,若是執行失敗,咱們是否要考慮回滾的問題。這裏就須要存儲過程對於事務的支持:

delimiter //
create procedure p4(
    out status int
)
BEGIN
    1. 聲明若是出現異常則執行{
        set status = 1;
        rollback;
    }
       
    開始事務
        -- 由秦兵帳戶減去100
        -- 方少偉帳戶加90
        -- 張根帳戶加10
        commit;
    結束
    
    set status = 2;
    
    
END //
delimiter ;

存儲過程支持事務以下:

delimiter $$
CREATE PROCEDURE p5(out p_return_code tinyint)
BEGIN
    DECLARE exit HANDLER for SQLEXCEPTION
    BEGIN
    -- 執行失敗,則返回 1
        set p_return_code = 1;
        ROLLBACK; -- 若是出錯,則回滾
    END;
    START TRANSACTION;
    INSERT into userinfo(username, password) VALUES ('xiaoB', '222');
    COMMIT;
    -- 執行成功,則返回 2
    set p_return_code = 2;
END $$
delimiter ;

執行:
MariaDB [db1]> set @p_return_code=0;

MariaDB [db1]> call p5(@p_return_code);

MariaDB [db1]> select @p_return_code;
+----------------+
| @p_return_code |
+----------------+
|              2 |
+----------------+

變量 p_return_code = 2 說明存儲過程執行成功。

 

4.5 使用 pymysql 模塊調用存儲過程

import  pymysql

config = {
    'host': '192.168.118.11',
    'user': 'root',
    'password': '123456',
    'database': 'db1'
}

db = pymysql.connect(**config)
with db.cursor(cursor=pymysql.cursors.DictCursor) as cursor:
    cursor.callproc('p3', (0,))   # 使用 callproc 調用存儲過程
    cursor.execute('select @_p3_0') # 查詢 out 參數的返回值
    r2 = cursor.fetchall()  # 獲取返回值
    print(r2)

執行結果:
[{'@_p3_0': 2}]

 

 

 4.6 查看存儲過程

-- 查看存儲過程:
show procedure status; 

-- 查看存儲過程建立的過程:
show create procedure proc_name;
相關文章
相關標籤/搜索