惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
SecWiki News
SecWiki News
Project Zero
Project Zero
C
Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy International News Feed
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Scott Helme
Scott Helme
A
Arctic Wolf
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
Apple Machine Learning Research
Apple Machine Learning Research
T
Tailwind CSS Blog
The Hacker News
The Hacker News
T
Tenable Blog
雷峰网
雷峰网
有赞技术团队
有赞技术团队
V
V2EX
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threat Research - Cisco Blogs
T
Threatpost
AWS News Blog
AWS News Blog
L
LINUX DO - 热门话题
Application and Cybersecurity Blog
Application and Cybersecurity Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
SegmentFault 最新的问题
月光博客
月光博客
Spread Privacy
Spread Privacy
S
Secure Thoughts
宝玉的分享
宝玉的分享
博客园 - 三生石上(FineUI控件)
Forbes - Security
Forbes - Security
T
The Exploit Database - CXSecurity.com
G
GRAHAM CLULEY
The Last Watchdog
The Last Watchdog
Y
Y Combinator Blog
I
Intezer
博客园 - 【当耐特】
B
Blog RSS Feed
Attack and Defense Labs
Attack and Defense Labs
I
InfoQ
博客园 - 叶小钗
Cyberwarzone
Cyberwarzone
V2EX - 技术
V2EX - 技术
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hugging Face - Blog
Hugging Face - Blog
H
Help Net Security
C
CERT Recently Published Vulnerability Notes

博客园 - lzhdim

JavaScript 中的原型与继承 1分钟查清局域网所有IP 20260714 - 个人小作品更新 WorkBuddy 完整教程手册 专为AI而生的企业级QLC SSD!长江存储PE501 61.44TB评测:超过1PB的写入测试 32GB显存才是第一生产力!英特尔锐炫 Pro B70评测:中小企业AI降本神器 JavaScript中的this关键字 C#开发的PopoSaver泡泡屏保应用 - 开源研究系列文章 - 个人小作品 6天不间断测试告诉你到底有多省电!米家空调巨省电大1.5匹评测:制冷峰值功率不超过500W 三倍双摄加持 + D-Log 2 曲线!大疆DJI OSMO Pocket 4P全面评测:补齐短板,化身口袋电影机 晶栈4.0闪存立威!长江存储致态TiPro9000 2TB SSD评测:缓外写入超4GB/s独一档 C# 多线程同步机制详解:原理、使用与性能对比 JavaScript作用域与闭包讲解 最受欢迎的 10 大免费 AI 工具 六种AI生意,从月入一万到月入十万 5个国产大模型实测对比2026:DeepSeek、Kimi、豆包、通义、智谱哪个好用 豆包、元宝、千问、Kimi、DeepSeek,这么多AI软件,到底该用哪个? 微软免费开源了一个 Linux 操作系统,没错!是微软出品 一次看懂5种咖啡冲煮方式:意式、手冲、法压、冷萃、摩卡壶到底有什么区别 120个 实用CSS 技巧汇总合集 JavaScript运算符与表达式详解 C#开发的ScreenSaver屏保应用 - 开源研究系列文章 - 个人小作品 WinForm 中跨线程操作 UI 的解决方案 JavaScript变量与数据类型详解 苹果良心!iOS 27适配设备公布:30款机型 7年前的iPhone 11还能升级 C++图形用户界面开发入门 10个不该免费的GitHub神级项目 JavaScript语言全面概述:从历史到现代实践 一张图看懂常见咖啡 C盘空间多出来4GB:谷歌服软 Chrome本地AI大模型可禁用、删除了 96GB显存运行230B大模型!七彩虹灵创K16笔记本评测:160W性能释放 AMD锐龙AI Max+ 395加持全能移动AI工作站 AI PC最强存储搭档!雷克沙NM1090 PRO 8TB固态评测:14400MB/s满速不缩水 C#已经不是当年的C#了——我用它写了个微秒级数据库引擎 EasyTool:轻量全能的 .NET 工具类库 SQL 入门 17:MySQL 数据类型:从字符串到 JSON 的全面解析 SQL 入门 16:SQL 事务隔离级别与死锁解析(易懂) 原相3955XM+TL3228主控!小米电竞鼠标2全面评测:原生双8K究竟有多强 PCIe 4.0火力全开:闪迪奥丁马仕GX 7100 NVMe SSD上手 超乎预料的千元级电竞主板!七彩虹iGame B850M Ultra-OC V14主板评测 SQL 入门 15:SQL 事务:从 ACID 到四种常见的并发问题 名人 - 我的闪存 C#性能优化技巧 SQL 入门 14:SQL 触发器与事件:自动化数据处理 38个实用的JavaScript 技巧 一款基于 .Net WinForm 开发的节点编辑器,纯 GDI 实现,体积仅 100+Kb Masuit.Tools:这个 C# 工具库 49个 JavaScript 代码快捷技巧,让你在 2026 年成为代码高手 20260503 - 个人小作品更新 SQL 入门 12:SQL 视图:创建、修改与可更新视图 C#如何实现Windows系统桌面截图功能... 6999元AMD新旗舰首测!锐龙9 9950X3D2性能解禁:这颗U根本不是给游戏玩家造的 SQL 入门 11:日期时间格式化、IF、CASE的使用 基于C#的校时类库的实现及使用 - 开源项目研究文章 39g无打孔+原生双8K!雷柏VT0 Air MAX / VT3s Air MAX鼠标评测:轻到飞起还快到极致 WinForm 中控件自适应窗体变化 20260413 - 个人小作品更新 SQL 入门 10:SQL 内置函数:数值、字符串与时间处理 最强麒麟芯+自研风冷 华为Mate80 Pro MAX 风驰版全面评测:涡轮散热封神 性能稳到骨子里 WiFi8核心技术特性与性能分析 2026年最新AMD/Intel桌面CPU排名:多线程、单线程、游戏性能谁才是第一 用DG对C盘进行扩容 - 初学者系列 - 学习者系列 还有比它更强的OEM固态吗!长江存储PC550 1TB评测:不用散热片也能满速的PCIe 5.0 SSD SharpCompress:跨平台的 C# 压缩与解压库 SQL 入门 9:SQL 高级子查询:ANY、EXISTS 与多位置应用 盘点接私活20个最实用的 .NET 开源项目 C#中加载图片的资源释放 20260404 - 个人小作品更新 开启iphone的墙纸玻璃效果 可能是综合性能最强的PCIe 5.0 SSD!铠侠EXCERIA PRO G2 2TB评测:AIDA64线性写入全程不掉速 SQL 入门 8:SQL 复杂查询:子查询与ALL关键词 性能最强的锐龙AI 9 H 465轻薄本!华硕无畏Pro16锐龙版2026评测 4盘RAID 0读写超1100MB/s!希捷酷狼Pro 32TB硬盘评测:百万文件快速完成备份 项目(Project)、项目集(Program)、项目组合(Portfolio) SQL 入门 7:SQL 聚合与分组:函数、GROUP BY 与 ROLLUP
SQL 入门 13:SQL 存储过程与函数:封装逻辑与参数处理
lzhdim · 2026-05-07 · via 博客园 - lzhdim

讲存储过程和函数,帮你把 SQL 查询打包,省事还能复用。跟着注释走,边看边搞。

学习内容

  1. 存储过程(Stored Procedure)
    • 一堆 SQL 语句打包,存在数据库里,干特定活儿。
    • 优点:编译好直接调,效率高,复用方便。
    • 用 DELIMITER $$ 改分隔符,防止过程里的 ; 搞乱。BEGIN 和 END 包住主体,每条语句得加 ;。
  1. 调用与删除
    • 用 CALL 跑存储过程。
    • 删除用 DROP PROCEDURE,加 IF EXISTS 防报错。
  2. 带参数的存储过程
    • 支持输入参数(IN)、输出参数(OUT)。
    • 可以设默认值或用 IFNULL 处理空值。
  3. 参数验证
    • 用 IF 和 SIGNAL SQLSTATE 校验参数,扔自定义错误。
    • 示例:检查付款金额 ≤ 0,报错代码 '22003'(数值超范围)。
  4. 变量
    • 用户会话变量:SET @var = value,整个会话有效。
    • 局部变量:DECLARE 在存储过程内定义,仅过程内用。
  5. 函数(FUNCTION)
    • 类似存储过程,但返回单个值,用 RETURNS 指定类型。
    • 特性声明:READS SQL DATA(只读)、DETERMINISTIC(同输入同输出,动态数据可不用)。
    • 用在 SELECT 里,像内置函数。

示例代码与讲解

1. 创建存储过程

DELIMITER $$
CREATE PROCEDURE get_clients()
BEGIN
    SELECT * FROM clients;
END $$
DELIMITER ;

  • 简单例子,查所有客户。
  • DELIMITER $$ 改分隔符,防止 ; 误判。END $$ 结束定义,DELIMITER ; 恢复。
  • 跑完没输出,但在数据库里多个存储过程(见图片,暂缺)。

CALL get_clients();

  • 调用过程,输出客户表。

2. 删除存储过程

DROP PROCEDURE get_clients;

  • 直接删。

DROP PROCEDURE IF EXISTS get_clients;

  • 加 IF EXISTS,删不存在的也不报错。

3. 带参数的存储过程

DELIMITER $$
CREATE PROCEDURE get_clients_by_state(p_state CHAR(2))
BEGIN
    SELECT * FROM clients c
    WHERE c.state = p_state;
END $$
DELIMITER ;

  • 按州查客户,p_state 是输入参数。

CALL get_clients_by_state('CA');

  • 查 CA 州的客户,必须传参,不然报错。

4. 带默认值的存储过程

DELIMITER $$
CREATE PROCEDURE get_clients_by_state(p_state CHAR(2))
BEGIN
    IF p_state IS NULL THEN
        SET p_state = 'CA';
    END IF;
    SELECT * FROM clients c
    WHERE c.state = p_state;
END $$
DELIMITER ;

  • 没传 p_state 就默认 CA。

CALL get_clients_by_state(NULL);

  • 传 NULL,输出 CA 客户。

DELIMITER $$
CREATE PROCEDURE get_clients_by_state(p_state CHAR(2))
BEGIN
    IF p_state IS NULL THEN
        SELECT * FROM clients;
    ELSE
        SELECT * FROM clients c
        WHERE c.client_id = p_state;
    END IF;
END $$
DELIMITER ;

  • 空值查所有客户,不然按 client_id 查。

DELIMITER $$
CREATE PROCEDURE get_clients_by_state(p_state CHAR(2))
BEGIN
    SELECT * FROM clients c
    WHERE c.client_id = IFNULL(p_state, client_id);
END $$
DELIMITER ;

  • 用 IFNULL 简化,空值时 client_id 匹配自身,查所有。

5. 参数验证

DELIMITER $$
CREATE PROCEDURE make_payment(
    p_invoices_id INT,
    p_payment_amount DECIMAL(9,2),
    p_payment_date DATE
)
BEGIN
    IF p_payment_amount <= 0 THEN
        SIGNAL SQLSTATE '22003'
        SET MESSAGE_TEXT = 'Invalid payment amount';
    END IF;
    UPDATE invoices i
    SET i.payment_total = p_payment_amount,
        i.payment_date = p_payment_date
    WHERE i.invoice_id = p_invoices_id;
END $$
DELIMITER ;

  • 录入付款,DECIMAL(9,2) 是 7 位整数 + 2 位小数。
  • 金额 ≤ 0 就报错,22003 表示数值超范围

CALL make_payment(2, -100, '2019-01-01');

  • 负金额触发错误。

6. 输出参数

DELIMITER $$
CREATE PROCEDURE get_unpaid_invoices_for_client(
    p_client_id INT,
    OUT invoices_count INT,
    OUT invoices_total DECIMAL(9,2)
)
BEGIN
    SELECT COUNT(*), SUM(i.invoice_total)
    INTO invoices_count, invoices_total
    FROM invoices i
    WHERE i.client_id = p_client_id AND i.payment_total = 0;
END $$
DELIMITER ;

  • 查客户未付发票数量和总额,输出到 invoices_count 和 invoices_total。

SET @invoices_count = 0;
SET @invoices_total = 0;
CALL get_unpaid_invoices_for_client(3, @invoices_count, @invoices_total);
SELECT @invoices_count, @invoices_total;

  • 定义会话变量,调用过程,查 client_id = 3 的结果。

7. 变量

SET @variable_name = 0;

  • 会话变量,全程可用。

DELIMITER $$
CREATE PROCEDURE get_risk()
BEGIN
    DECLARE risk_factor DECIMAL(9,2) DEFAULT 0;
    DECLARE invoices_total DECIMAL(9,2);
    DECLARE invoices_count INT;
    SELECT COUNT(*), SUM(invoice_total)
    INTO invoices_count, invoices_total
    FROM invoices;
    SET risk_factor = invoices_total / invoices_count * 5;
    SELECT risk_factor;
END $$
DELIMITER ;

  • 局部变量 DECLARE 定义,算风险系数(平均发票额 * 5)。

8. 函数

DELIMITER $$
CREATE FUNCTION count_risk_for_client(client_id INT)
RETURNS INT
READS SQL DATA
BEGIN
    DECLARE risk_factor DECIMAL(9,2) DEFAULT 0;
    DECLARE invoices_total DECIMAL(9,2);
    DECLARE invoices_count INT;
    SELECT COUNT(*), SUM(invoice_total)
    INTO invoices_count, invoices_total
    FROM invoices i
    WHERE i.client_id = client_id;
    SET risk_factor = invoices_total / invoices_count * 5;
    RETURN IFNULL(risk_factor, 0);
END $$
DELIMITER ;

  • 函数算客户风险值,返回整数。
  • READS SQL DATA 表示只读,动态数据不需 DETERMINISTIC。

SELECT client_id, name, count_risk_for_client(client_id) AS risk
FROM clients;

  • 像内置函数一样用,列出客户风险值。

作业

1. 视图存储过程

DELIMITER $$
CREATE PROCEDURE get_invoices_with_balance()
BEGIN
    SELECT * FROM invoices_with_balance
    WHERE Balance > 0;
END $$
DELIMITER ;

  • 从 invoices_with_balance 视图查未付发票。

2. 带参数存储过程

DELIMITER $$
CREATE PROCEDURE get_invoices_by_client(p_client INT)
BEGIN
    SELECT * FROM invoices
    WHERE invoice_id = p_client;
END $$
DELIMITER ;
CALL get_invoices_by_client(8);

  • 查指定 invoice_id 的发票。

3. 默认值与多参数

DELIMITER $$
CREATE PROCEDURE get_payment(p_client_id INT, p_pay_method_id TINYINT)
BEGIN
    SELECT p.client_id, pm.name
    FROM payments p
    JOIN payment_methods pm ON p.payment_method = pm.payment_method_id
    WHERE p.client_id = IFNULL(p_client_id, p.client_id)
    AND p.payment_method = IFNULL(p_pay_method_id, p.payment_method);
END $$
DELIMITER ;
CALL get_payment(5, 4);

  • 查客户和支付方式,参数为空时返回所有。

总结

讲了存储过程的创建、调用、参数、验证、变量,还搞了个函数,方便复用逻辑。用的 sql_invoicing 数据库。接下来看触发器和事务。

常见的错误对应代码: