接上篇基礎操作,本文進入 MySQL 進階核心特性:視圖、存儲過程、自定義函數、事務控制、觸發器、動態 SQL 執行。並附 Python (PyMySQL) 調用示例,幫助開發者從理論落地到工程實踐。
視圖是虛擬表,本質是預定義的 SELECT 語句。不存儲數據,查詢時動態執行。主要用途:
| 操作 | 語法 | 示例 |
|---|---|---|
| 創建 | CREATE VIEW view_name AS SELECT ... | CREATE VIEW v1 AS SELECT nid, name FROM tab1 WHERE nid > 4; |
| 修改 | ALTER VIEW view_name AS SELECT ... | ALTER VIEW v1 AS SELECT A.nid, B.name FROM tab1 LEFT JOIN B ON A.id = B.nid WHERE tab1.id > 2; |
| 刪除 | DROP VIEW view_name; | DROP VIEW v1; |
| 使用 | 同普通表 SELECT * FROM v1; | 僅支持查詢,不可直接 INSERT/UPDATE/DELETE (除非滿足可更新視圖嚴格條件) |
EXECUTE 權限,不給表直接操作權DELIMITER //
CREATE PROCEDURE proc_name (
IN param1 INT, -- 僅輸入
OUT param2 INT, -- 僅輸出
INOUT param3 INT -- 輸入輸出雙向
)
BEGIN
DECLARE temp_var INT DEFAULT 0;
-- 邏輯代碼
SET param2 = param1 + param3;
SET param3 = param3 + 100;
END//
DELIMITER ;
-- MySQL 客戶端 SET @t1 = 3; SET @t2 = 0; CALL proc_name(1, 2, @t1, @t2); SELECT @t1, @t2; -- 查看 OUT/INOUT 返回值
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pymysql
conn = pymysql.connect(
host='127.0.0.1', port=3306,
user='root', passwd='', db='test_db',
cursorclass=pymysql.cursors.DictCursor
)
try:
with conn.cursor() as cursor:
# 調用存儲過程
cursor.callproc('proc_name', (1, 2, 3)) # 傳入參數
# 獲取結果集 (若存儲過程有 SELECT)
result_set = cursor.fetchall()
print('Result set:', result_set)
# 獲取 OUT/INOUT 參數
cursor.execute('SELECT @_proc_name_0, @_proc_name_1, @_proc_name_2')
out_params = cursor.fetchone()
print('Out params:', out_params)
conn.commit()
finally:
conn.close()
關鍵點: PyMySQL 中 OUT/INOUT 參數需通過 SELECT @_proc_name_N 獲取,且必須 conn.commit() 才能持久化修改。
| 特性 | 函數 (FUNCTION) | 存儲過程 (PROCEDURE) |
|---|---|---|
| 返回值 | 必須有單一返回值 (RETURNS) | 可無返回值,通過 OUT 參數返回多值 |
| 調用方式 | SELECT func(...) 在 SQL 中直接用 | CALL proc(...) 獨立調用 |
| 事務控制 | 不支持 | 支持 START TRANSACTION/COMMIT/ROLLBACK |
| 動態 SQL | 不支持 | 支持 PREPARE/EXECUTE |
DELIMITER //
CREATE FUNCTION f_add(i1 INT, i2 INT) RETURNS INT
BEGIN
DECLARE sum INT;
SET sum = i1 + i2;
RETURN sum;
END//
DELIMITER ;
-- 刪除
DROP FUNCTION f_add;
-- 在查詢中使用
SELECT f_add(11, nid), name FROM tb2;
-- 賦值給變量
SET @res = f_add(5, 6);
SELECT @res;
轉賬業務:A 扣款、B 入賬,必須同成同敗。若扣款成功、入賬前網絡中斷,事務回滾保證數據一致性。
DELIMITER //
CREATE PROCEDURE sp_transfer(OUT p_code TINYINT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SET p_code = 1; -- 錯誤
ROLLBACK;
END;
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
SET p_code = 2; -- 警告
ROLLBACK;
END;
START TRANSACTION;
DELETE FROM tb1; -- 業務 SQL 1
INSERT INTO tb2(name) VALUES('seven'); -- 業務 SQL 2
COMMIT;
SET p_code = 0; -- 成功
END//
DELIMITER ;
-- 調用
SET @ret = 0;
CALL sp_transfer(@ret);
SELECT @ret; -- 0 成功,1 異常回滾,2 警告回滾
| 事件 | BEFORE | AFTER |
|---|---|---|
| INSERT | BEFORE INSERT | AFTER INSERT |
| DELETE | BEFORE DELETE | AFTER DELETE |
| UPDATE | BEFORE UPDATE | AFTER UPDATE |
DELIMITER //
CREATE TRIGGER trg_name
BEFORE INSERT ON tb1
FOR EACH ROW
BEGIN
-- NEW 代表新插入行,OLD 代表舊行 (UPDATE/DELETE 可用)
IF NEW.name = 'aylin' THEN
INSERT INTO tb2(name) VALUES ('張巖林');
END IF;
END//
DELIMITER ;
DELIMITER //
CREATE TRIGGER trg_after_ins_tb1
AFTER INSERT ON tb1
FOR EACH ROW
BEGIN
IF NEW.num = 666 THEN
INSERT INTO tb2(name) VALUES ('張巖林'), ('很帥');
ELSEIF NEW.num = 555 THEN
INSERT INTO tb2(name) VALUES ('aylin'), ('非常帥');
END IF;
END//
DELIMITER ;
-- 刪除
DROP TRIGGER trg_after_ins_tb1;
-- 觸發器無法手動調用,只能通過對表的 DML 操作被動觸發
INSERT INTO tb1(name) VALUES ('張巖林'); -- 自動觸發上述 BEFORE INSERT 觸發器
存儲過程中構建動態 SQL 字符串並執行:
DELIMITER //
CREATE PROCEDURE dynamic_query(IN tbl_name VARCHAR(64))
BEGIN
SET @sql = CONCAT('SELECT * FROM ', tbl_name, ' WHERE id > 10');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END//
DELIMITER ;
應用場景:表名/列名作為參數、分表查詢、靈活構建複雜條件。
| 函數 | 說明 | 示例 |
|---|---|---|
CHAR_LENGTH(str) | 字符長度 (多字節字符計 1) | CHAR_LENGTH('中文') = 2 |
CONCAT(str1,str2,...) | 拼接,任一為 NULL 則結果 NULL | CONCAT('a','b') = 'ab' |
CONCAT_WS(sep,str1,str2) | 帶分隔符拼接,忽略 NULL | CONCAT_WS(',','a',NULL,'b') = 'a,b' |
SUBSTRING(str,pos,len) | 截取子串,支持負位置 | SUBSTRING('abcdef',2,3) = 'bcd' |
REPLACE(str,from,to) | 替換所有匹配 | REPLACE('a b c',' ','-') = 'a-b-c' |
TRIM([BOTH|LEADING|TRAILING] rem FROM str) | 去除前後綴 | TRIM('x' FROM 'xxxabcxxx') = 'abc' |
FORMAT(X,D) | 數字格式化帶千分位 | FORMAT(12345.67,2) = '12,345.67' |
NOW() / CURDATE() | 當前日期時間 / 日期 | -- |
完整函數列表請參考:MySQL 官方中文手冊
START TRANSACTION + 錯誤處理器 + COMMIT/ROLLBACK 是標準模式。BEFORE/AFTER + INSERT/UPDATE/DELETE,利用 NEW/OLD 訪問行數據,慎用避免隱式邏輯導致除錯困難。PREPARE/EXECUTE/DEALLOCATE 三部曲,防注入需嚴格驗證拼接參數。下一章將深入講解索引原理與優化實戰,敬請期待!