MySQL数据分析进阶(八)存储过程

※食用指南:文章内容为'CodeWithMosh'SQL进阶教程系列学习笔记,笔记整理比较粗糙,主要目的自存为主,记录完整的学习过程。(图片超级多,慎看!)

【中字】SQL进阶教程 | 史上最易懂SQL教程!10小时零基础成长SQL大师!!https://www.bilibili.com/video/BV1UE41147KC/?spm_id_from=333.1007.0.0&vd_source=b287f1f4a1fa54cc438e31a0f87ef4e2

第八章:存储过程

1、WHAT ARE STORED PROCEDURES------什么是存储过程

不建议在应用代码里写语句:会让应用代码很混乱且难以维护

①在使用C#、Java或者Python开发应用

如果把Java代码和SQL混在一起,那么SQL代码介入会让应用代码很混乱和难以维护

②一些C#、Java这样的编程语言需要编译工作

在这类应用代码里写SQL查询,一旦发现需要修改其中的查询,就必须重新编译应用代码才能生效。所以每次修改SQL代码都需要重新编译或者可能重新部署应用代码

++解决方法:将SQL代码储存在它应属的数据库里------存储过程或函数中++

存储过程Stored Procedure:一个包含一堆SQL代码的数据库对象

存储过程Stored Procedure的优势:

①Store and organize SQL(存储和管理SQL)

在应用代码里,调用这些过程来获取或保存数据,使用存储过程来存储和管理SQL代码

②Faster execution(更快速执行)

大多数DBMS可以对存储过程里的代码做一些优化,因此存储过程里的SQL代码有时执行起来更快

③Data security(数据安全性)

取消对所有表的直接访问权限,让很多操作如插入、更新和删除数据由存储过程来完成,然后可以指定能够执行特定存储过程的某一人,从而限制用户对我们数据的操作范围*(防止一些用户删除数据)*

2、CREATING A STORED PROCEDURE------创建一个存储过程

把这个查询存在存储过程里

①get_clients,使用小写字母并用下划线分隔,是MySQL开发者通用的

②BEING和END关键字之间的内容成为存储过程的主体(body)

③通常创建的存储过程有好几条语句(SELECT部分),每条(包含一条)语句都需要分号终结

其他的DBMS,如SQL Server可能就不需要

DELIMITER重新定义分割符:让存储过程不会到clients时报错

视为新的分隔号,把所有的语句当成一个整体; 最后再用DELIMITER ;把符号还回分号 🔺DELIMITER后面要**加空格** 其他的DBMS,如SQL Server不需要改动默认分隔符 执行后刷新 ![](https://i-blog.csdnimg.cn/direct/30d7eccc67d64762855b820720e82cef.png) ![](https://i-blog.csdnimg.cn/direct/334e56b24c1c4946a6cb4428db3f4a41.png) 一般来说,我们会用C#、Java、Python等应用代码调用存储过程,但有时想用SQL代码**调用**存储过程,使用CALL即可 ![](https://i-blog.csdnimg.cn/direct/18f7d0a349ef404c9ab9bae3c2575595.png) 练习: ![](https://i-blog.csdnimg.cn/direct/c1a4adfcd4884a42b33149e70c9964cb.png) ![](https://i-blog.csdnimg.cn/direct/2006d4f1702742e5acee8dfa3ad2db99.png) ### **3、CREATING PROCEDURES USING MAYSQLWORKBENCH------使用MYSQL工作台创建存储过程** 更为简单的存储工程创建方式: 导航面板→右击存储过程文件夹→创建一个存储过程 ![](https://i-blog.csdnimg.cn/direct/9e49ba885ee24c529c30589df34e678c.png) 在这个窗口就不用担心改默认分隔符,重点放在SQL语句 ![](https://i-blog.csdnimg.cn/direct/4a669f0e182347919f4ed514bbc65b25.png) MySQL会默认添加反引号,++**防止SQL关键字的名称冲突**++ ![](https://i-blog.csdnimg.cn/direct/28dec1344395413383f54c69474dd052.png) 编写完Apply,MySQL自动生成SQL语句,再次Apply ![](https://i-blog.csdnimg.cn/direct/7af3a00f4c2c4042abac4c80456a0667.png) ![](https://i-blog.csdnimg.cn/direct/b615face36804ce497d5c0b4f77aeb07.png) ![](https://i-blog.csdnimg.cn/direct/0a66e9a358124372b615ea2d7d706689.png) ### **4、DROPPING STORED PROCEDURES------删除存储过程** 出了错的或者想要重新创建存储过程的方法: 执行后即刻消失,再次执行就会报错(删除一个并不存在的过程) ![](https://i-blog.csdnimg.cn/direct/43733d5bca424cc9a526dd31db570a19.png) 可以使用IF EXISTS关键字来防止错误出现(参考view) ![](https://i-blog.csdnimg.cn/direct/a529635e1c0a4d439e680e0914d7f59e.png) **AGAIN:**最好把删除和创建每一个存储过程的代码存储在不用的SQL文件中,并把文件放在GIT源代码控制下,可以和其他组员共享GIT存储库,任何人都可以在自己的电脑上使用所有视图和存储过程再建数据库 **安全重建**:删除检查是否有同名的存储过程存在,有的话就删掉,再重新创建 (并存到了新stored_procedures文件夹,名为client_id) ![](https://i-blog.csdnimg.cn/direct/83f1967ac1934961b2b66bd426aab7bc.png) ### **5、PARAMETERS------参数** 在存储过程中添加参数 一般使用参数为存储过程传递值,也可以为调用程序赋值 ![](https://i-blog.csdnimg.cn/direct/bfd7ac1159af44209c41196b988ebb91.png) **state CHAR (2)**:有两个字符的字符串 **VARCHAR**:可改变长度的字符串(存储姓名、电话号码、信息),一般能确定字符串有固定长度才使用 **WHERE state = state**:这里不会起任何作用因为在用它本身列的值对比(无论如何都是对的) 🔺需要把这两个state(state这一列的值、定义的state这个系数)区分开: ①加前缀p(parameter) ![](https://i-blog.csdnimg.cn/direct/5fb592de41d447cbb2d5ad10c66ffa0f.png) ![](https://i-blog.csdnimg.cn/direct/16fa0627b28243de81bb557e1e9261bc.png) ②加后缀_ param ![](https://i-blog.csdnimg.cn/direct/cb49301afd924ea4a393219498cfd3d4.png) ③不变动,给表格起别名 ![](https://i-blog.csdnimg.cn/direct/a41216a1bda842d1be7088efcfb1b248.png) ![](https://i-blog.csdnimg.cn/direct/7ec8abd91adb4a2a96e487d8eaca367f.png) 调用情况: ![](https://i-blog.csdnimg.cn/direct/806d1ab5b31a4c73a1c85bf073945e73.png) 如果不提供任何值,将会出现错误,因为MySQL中++**所有系数都是必填**++的 练习: ![](https://i-blog.csdnimg.cn/direct/96a9333502cb42909cab4e5fa386d6cf.png) Invoices表中client_id系数为INT(整数) ![](https://i-blog.csdnimg.cn/direct/4429066a60c64e06b94b078c6e4a8260.png) ![](https://i-blog.csdnimg.cn/direct/b76ef44719334e42899c99b7f34d7c41.png) 完成后调用,输入你的系数(即哪位客户) ![](https://i-blog.csdnimg.cn/direct/e03cf7bb91964198a51b544ab17b3276.png) ![](https://i-blog.csdnimg.cn/direct/3ad10f8a6c7f4d3aae0d3b5575b92ad0.png) ### **6、PARAMETERS WITH DEFAULT VALUES------带默认值的参数** 为系数配置默认值 在get_clients_by_state制定一个**规则**:如果存储过程调用这无法明确具体在那个state,就默认返回CA的客户 ①使用IF函数 IF函数结尾需要用**END IF**:告诉MySQL语句该到哪里结束 ![](https://i-blog.csdnimg.cn/direct/811e8919f21b4b5396f4e722e24a70cc.png) ②创建完毕使用空值调用这个过程------验证 ![](https://i-blog.csdnimg.cn/direct/ecd3fa38f04945a6a9aa5cb66825a61f.png) ③再改变一下规则:不只是返回位于CA的客户,而是返回所有客户 ![](https://i-blog.csdnimg.cn/direct/27845bdb04874d8c8445ce6fe6976c6e.png) 当输入NULL ![](https://i-blog.csdnimg.cn/direct/f0e57f502b9a441eaeb7e52decda1a85.png) 当输入CA ![](https://i-blog.csdnimg.cn/direct/090a1708c1b744378d86341efceb9602.png) ④将两段查询合并为一段 IFNULL(state,C.state):如果第一个值是空值,就返回第二个值 ![](https://i-blog.csdnimg.cn/direct/13fc1a6b47284003b9cc46383ba26cf3.png) 练习: ![](https://i-blog.csdnimg.cn/direct/33b02009c96c49d1840a7346d34faa0f.png) **INT、TINYINT都是整数** **INT**:占用4个字节,可以存储更大的数字- **TINYINT**:占用1字节的内存,可以存储0-255的数字 client_id、payment_method_id 这两个系数都不是必填的, \^ 如果传递NULL,过程应该能够返回数据库里的所有付款记录 \^ 如果提供client_id,则返回这个客户的payment_method_id \^ 如果这两个系数都赋值,就返回指定客户使用付款方式支付的所有付款 ++AND:两个条件都需要满足++ payment_mothod_id:是**参数名称**,可以与列名不同 ![](https://i-blog.csdnimg.cn/direct/21cb99a3774e4eb786ba583c212a1fe9.png) ++**Arguments实参:提供给形参的值**++ ++**Parameters形参:占位符,过程或函数中的小小坑位**++ 如上定义了两个形参:client_id、payment_method_id ### **7、PARAMTETER VALIDATION------参数验证** 使用过程插入、更新和删除数据 创建一个过程更新发票过程中学习**参数验证**,确保过程不会意外地往数据库存储错误数据 **DECIMAL** (9,2):**小数数据类型** 第一个参数代表位数,第二个参数是小数点后的位数:最多9位数,小数点后两位 只想更新i.payment_total 、i.payment_date 不想意外更新invoice表中的number,invoice_date等 ![](https://i-blog.csdnimg.cn/direct/6c8468c51bac4c9bb962345e49ea85e3.png) 之前数据: ![](https://i-blog.csdnimg.cn/direct/cefc22220c684a3b8b9c9d595a158547.png) 调用执行 ![](https://i-blog.csdnimg.cn/direct/09a02998b86946acac039b2e6e750f9e.png) ![](https://i-blog.csdnimg.cn/direct/a79e0f62e5234f379b1149a0b33ecf63.png) 如果数值为负数,则会出现不合理的数据 ![](https://i-blog.csdnimg.cn/direct/860d204e9cd64b5f9c5971e55d2e9357.png) ![](https://i-blog.csdnimg.cn/direct/6b947a958e4f41b3a5d25389186f7a6d.png) ❗如何验证传递给这个存储过程的参数: **SIGNAL语句**:标志或者引发错误(像其他编程语言中抛出异常) 查找错误的完整列表在此处查询:sqlstate errors 前两个字母界定了错误类别 22:数据异常 22003:在存储过程中,处理超位值 **SIGNAL SQLSTATE '22003'**:这里的22003是字符串,而不是数字 ![](https://i-blog.csdnimg.cn/direct/707a4103ec154b0a954792f623405f63.png) 确认后再次执行,弹出错误提醒 ![](https://i-blog.csdnimg.cn/direct/dc3e9aa2b72745968c3683f2bcfdd174.png) 综上:在做任何数据变动前线检查存储过程系数很有帮助 但如果写太多参数验证,也会使存储过程变得很复杂并难以维护 这里的payment_amount本身就是不允许有空值,即便传递了空值,MySQL自动标注这一错误,不需要再编写 ![](https://i-blog.csdnimg.cn/direct/d2990572a1404f44b269af1ded6d5370.png) **++要尽量利用最少的验证逻辑,只保留最最关键的++** 应该从用户端接受输入信息,再在应用中使用更多验证*(前后端中进行验证)* 相比访问数据库,在应用在检测和报告错误会更快捷 把参数验证作为**终极备选方案**,以防止有人没有通过应用直接调用了存储过程 ### **8、OUTPUT PARAMETERS------输出参数** 用参数来给调用程序返回值 创建后验证: ![](https://i-blog.csdnimg.cn/direct/25e8dba46363442589507fb29ffee93c.png) ![](https://i-blog.csdnimg.cn/direct/19da32194c1e4210995b43030c8e9547.png) ![](https://i-blog.csdnimg.cn/direct/5667f52b3d1b4b7e9dab970442e11a32.png) ![](https://i-blog.csdnimg.cn/direct/a12a9ba2e28744f7897d98e5e205938a.png) 也可通过参数获取这些值 使用TINYINT或者INT,具体根据未支付发票数量决定 ![](https://i-blog.csdnimg.cn/direct/4b28770c25b148ce89d0362f9c1affb1.png) 默认情况下,存储过程的所有这些系数都是**输入参数**,也就是只能在给过程传递值的时候才可以使用它们,所以需要给两个参数附上OUT关键字前缀,就会把参数标记为输出参数 ![](https://i-blog.csdnimg.cn/direct/09a33bb246be41b28722f00ba3ff24e5.png) **SELECT COUNT(\*),SUM(invoice_total):**取读数据 **INTO invoices_count,invoices_total**:复制到这些输出参数 ![](https://i-blog.csdnimg.cn/direct/c6df2d006de64870bde2970d178a3284.png) ![](https://i-blog.csdnimg.cn/direct/61df74713f01403aacb8436a2c2cc286.png) 代码解释 ①用户定义变量:set @invoices_count = 0;、set @invoices_total = 0; 变量就是可以用来存储单一值的对象 @作为前缀来定义变量,调用过程中传递这些变量 ②(3, @invoices_count, @invoices_total); 第一个参数3,即为客户3,后面连个参数就是先前定义的变量 ③select @invoices_count, @invoices_total; 调用过程后,需要用选择语句来取读这些值,并在此显示 ![](https://i-blog.csdnimg.cn/direct/6fcaaaf6db634bb891c5815b635589cd.png) 使用输出参数需要在读取数据上花精力,除非有足够的动机,尽量**避免使用** ### **9、VARIABLES------变量** **++User or session variables :用户或者会话变量++** SET语句定义,@符号作为前缀 @invoices_count = 0; 通常在调用有输出参数的存储过程时使用这些变量(传递变量,获取输出参数值) 变量在整个客户会话过程中被保存,客户从MySQL断线时,变量又被清空 ++**Local variables:本地变量(局部变量)**++ 不会在整个客户端会话过程中被保存,一旦存储过程完成执行任务,这些变量就会被清光 一般在存储过程中执行计算任务 ①设定一个商业规则 ![](https://i-blog.csdnimg.cn/direct/15c881f7dc644c6bbb6fa2bd31e93bbd.png) 此处就可使用本地变量,在过程中定义这些本地变量,能轻易地计算这个公式 ②在BEGIN语句后声明变量 DECLARE:是在复合语句中声明变量地指令,在过程中定义的变量并不是真正的定义。 只是在BEGIN/END块内定义了而已(也就是**形参**) ![](https://i-blog.csdnimg.cn/direct/be6697cca271478c8813e83e420bbb55.png) ③第二、三个变量不设置默认值,用选择语句来设定 ![](https://i-blog.csdnimg.cn/direct/a1ec89cbf7554c8c948a5fbbdfd5a355.png) ④计算风险因素 SET设计变量的值 ![](https://i-blog.csdnimg.cn/direct/e0cc6f7759a9421fb234a45868bea773.png) 以上为如何在存储过程中声明并使用本地变量 这些本地变量**只有在存储过程中才有意义**,只要声明它们,就可以被使用了 然后一旦我们执行完毕存储过程,就被抹去了 ![](https://i-blog.csdnimg.cn/direct/1331471bc6cc44f8ba727890f4e2f546.png) (结果错了,但暂时找不到原因) ### **10、FUNCTIONS------函数** 创建自己的函数 函数只能返回单一值,比起存储过程,无法返回拥有多行和多列的结果集 如上节,创建函数计算每位客户的风险因素 ![](https://i-blog.csdnimg.cn/direct/7014419e88934be8b5cdebf479473ada.png) **RETURNS**:明确了这个函数返回的值的类型,可以是INT、INTEGER或者任何其他MySQL中的数据类型 在RETURNS设置函数属性(每个MySQL**至少要有一个属性**) **DETERMINISTIC(确定性)**:如果给予这个函数同样的一组值,它永远会返回一样的值 在不想根据数据库中的数据返回值的时候很有用 **READS SQL DATA(读取SQL数据)**:函数中会配置选择语句,以读取一些数据 **MODIFIES SQL DATA(修改SQL数据)**:函数中有插入、更新或者删除函数 可以同时有多种属性,既取读又修改数据 ![](https://i-blog.csdnimg.cn/direct/677b618cede74a459e5241669c3455be.png) 创建函数计算每位客户的风险因素,函数没有DETERMINISTIC(确定性) 输入同个client_id可能会返回不同值,这个客户后续可能会支付其他发票,所以风险因素随时可以改变 ①选择READS SQL DATA 从获取风险因素过程复制变量、选择语句、计算风险因素的公式 ②修改选择语句 ![](https://i-blog.csdnimg.cn/direct/ce45aed99fd24ed699b8e9a130539b1f.png) 执行后使用: ![](https://i-blog.csdnimg.cn/direct/cc07236196f14a849009a60dce28d91b.png) (结果为NULL,,暂时无解) ③解决出现空值: ![](https://i-blog.csdnimg.cn/direct/37da43995ce54240b94299776fbf9532.png) ![](https://i-blog.csdnimg.cn/direct/9fee0a9f60574485aaba0242803b6856.png) 把函数放在SQL文件,并放在源代码里 使用DROP FUNCTION IF EXISTS ![](https://i-blog.csdnimg.cn/direct/a89b56a340b646ec97b4c0df41ea4a3c.png) ### **11、OTHER CONVENTIONS------其他约定** 函数:加fn前缀 存储过程: ①加proc、proc_前缀 ②用驼峰式proGetRiskFactor(每个词首字母都大小写,除了第一个单词) ③直接getGetRiskFactor、get_risk_factor DELIMITER使用$$或者// 以上约定根据已经在用的那种使用即可,无需强制使用那一种 *(不要和老同事吵用哪种)* 但如果从头推进一个项目,就可以根据自己的喜好约定 **关键在于遵循已经存在的约定** **------------TBC**

相关推荐
一 乐1 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
鹏码纵横1 小时前
已解决:java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 异常的正确解决方法,亲测有效!!!
java·python·mysql
美林数据Tempodata3 小时前
大模型驱动数据分析革新:美林数据智能问数解决方案破局传统 BI 痛点
数据库·人工智能·数据分析·大模型·智能问数
野槐3 小时前
node.js连接mysql写接口(一)
数据库·mysql
Zzzone6834 小时前
PostgreSQL日常维护
数据库·postgresql
chxii4 小时前
1.13使用 Node.js 操作 SQLite
数据库·sqlite·node.js
冰刀画的圈4 小时前
修改Oracle编码
数据库·oracle
这个胖子不太裤4 小时前
Django(自用)
数据库·django·sqlite
麻辣清汤4 小时前
MySQL 索引类型及其必要性与优点
数据库·mysql
2501_915374355 小时前
Neo4j 图数据库安装教程(2024最新版)—— Windows / Linux / macOS 全平台指南
数据库·windows·neo4j