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

推荐订阅源

Jina AI
Jina AI
V
Vulnerabilities – Threatpost
Security Latest
Security Latest
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
量子位
H
Help Net Security
Attack and Defense Labs
Attack and Defense Labs
The GitHub Blog
The GitHub Blog
L
LINUX DO - 最新话题
A
Arctic Wolf
博客园_首页
S
Securelist
S
Secure Thoughts
Google DeepMind News
Google DeepMind News
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
N
Netflix TechBlog - Medium
Cyberwarzone
Cyberwarzone
小众软件
小众软件
T
Threatpost
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Blog — PlanetScale
Blog — PlanetScale
N
News and Events Feed by Topic
NISL@THU
NISL@THU
Forbes - Security
Forbes - Security
博客园 - 聂微东
F
Fortinet All Blogs
Simon Willison's Weblog
Simon Willison's Weblog
H
Heimdal Security Blog
罗磊的独立博客
S
Security @ Cisco Blogs
B
Blog
T
Troy Hunt's Blog
Engineering at Meta
Engineering at Meta
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The Hacker News
The Hacker News
The Last Watchdog
The Last Watchdog
Hacker News - Newest:
Hacker News - Newest: "LLM"
I
Intezer
T
Threat Research - Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
The Cloudflare Blog
S
Schneier on Security
月光博客
月光博客
L
LINUX DO - 热门话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

博客园 - 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 数据库。接下来看触发器和事务。

常见的错误对应代码: