MySQL存储过程原理、实现及优化(富途证券、新浪微博、昆仑万维面经)

先说说啥是存储过程吧。简单点讲,存储过程就是一组预先写好并存储在数据库里的SQL语句集合。你可以把它想象成一个封装好的小工具,里面装着一堆命令,调用一下就能执行一连串的操作,不用每次都手动敲SQL语句。它的“存储”是指这些代码直接存在数据库服务器上,而不是写在你的应用程序里;“过程”嘛,就是它能按顺序执行一系列逻辑,完成特定的任务。举个例子,假设你经常需要更新某个表的数据,还要顺便记录日志,如果每次都手动写SQL,那得多麻烦?有了存储过程,你只要写一次,之后直接调用就完事了,省时又省力。

存储过程这东西并不是MySQL独有的,它的历史可以追溯到数据库发展的早期。早在上世纪80年代,关系型数据库开始流行的时候,像Oracle、SQL Server这些大佬就已经在用存储过程了。那时候,数据库不仅仅是存储数据的地方,更是业务逻辑处理的核心。存储过程的出现,主要是为了解决当时应用程序和数据库之间的性能瓶颈问题。毕竟,把逻辑放在数据库端执行,比在客户端来回传数据要快得多。MySQL虽然起步稍晚,但从5.0版本开始,也正式支持存储过程,算是赶上了这波潮流。如今,存储过程已经成了数据库开发中不可或缺的一部分,尤其是在复杂业务场景下。

那为啥存储过程这么重要呢?咱们从几个角度来聊聊它的价值。先说性能提升吧。存储过程是预编译的,意思是它在第一次运行时就会被优化并缓存起来,之后再调用就直接执行,不用每次都解析SQL语句。这就好像你做菜,第一次得洗菜、切菜、调料啥的都准备好,但如果有现成的半成品,下次直接下锅就行,效率自然高得多。尤其是在高并发的场景下,比如电商系统的订单处理,存储过程能显著减少数据库的解析开销,响应速度嗖嗖地快。

再聊聊代码管理方面的好处。假设你有个复杂的业务逻辑,涉及多张表的操作,如果把这些逻辑写在应用层,比如Java或者PHP代码里,那一旦需求变了,你得改代码、测试、部署,流程长得要命。但如果用存储过程,逻辑都在数据库端,改起来就集中多了,维护成本低不少。而且,存储过程还能被多个应用共享,比如你的公司有好几个系统都需要同样的数据处理逻辑,那直接调用同一个存储过程就行了,不用每个系统都写一遍代码,简直不要太方便。

安全性也是存储过程的一大亮点。你可以通过权限控制,让用户只能调用存储过程,而无法直接操作底层表数据。这样一来,即便有人拿到了数据库账号,也没法随便乱改数据,相当于多了一道防护墙。比如说,一个财务系统里,你可以设置一个存储过程专门处理薪资计算,普通用户只能调用这个过程,看不到具体的薪资表数据,安全性一下子就上去了。

第一章:MySQL存储过程的基础知识

存储过程(Stored Procedure)是MySQL里一个挺重要的功能,尤其是在处理复杂业务逻辑的时候,简直是救命神器。

存储过程是个啥?

说白了,存储过程就是一堆预先写好的SQL语句的集合,这些语句被打包在一起,存到数据库服务器上,随时等着你去调用。想象一下,你经常要执行一组固定的操作,比如每个月统计一次销售数据,涉及好几条SQL查询和更新语句。如果每次都手动敲这些代码,那得多累啊!存储过程就相当于把这些操作封装成一个“按钮”,你只需要点一下,它就能自动完成所有步骤。

更具体点,存储过程不像普通的SQL语句那样临时写、临时跑。它是提前定义好的,存储在MySQL数据库里,名字、参数啥的都定死了。调用的时候,你只需要告诉数据库:“嘿,跑一下这个存储过程!”然后它就会按部就班地执行里面的逻辑。这玩意儿有点像编程语言里的函数或者方法,只不过它是直接在数据库层面运行的。

存储过程有啥特点?

聊到存储过程的特点,咱们得先搞明白它为啥会存在,为啥很多人愿意用它。毕竟,如果它没啥特别的优势,谁会费劲去学呢?

一个明显的特点是性能提升。存储过程在创建的时候会被MySQL预编译,啥意思呢?就是说,里面的SQL语句已经被解析和优化过了,存成一种“半成品”状态。每次调用的时候,数据库不用再从头解析这些语句,直接拿来用就行。这就省下了不少时间,尤其是在高并发场景下,比如电商平台处理订单,存储过程能显著减少数据库的负担。

还有一点,存储过程能让你的代码管理变得更轻松。假设你有一个复杂的业务逻辑,涉及到多张表的更新和查询,如果把这些逻辑写在应用程序里,比如Java或者PHP代码中,一旦逻辑改了,你得去改代码,还要重新部署,麻烦得要死。但如果用存储过程,逻辑都在数据库这边,改起来就方便多了,改完直接重新定义一下存储过程就完事,应用层都不用动。

另外,存储过程还能增强安全性。咋说呢?因为它可以限制用户直接访问底层表数据。你可以给用户调用存储过程的权限,但不给他们直接操作表的权限,这样就避免了一些不必要的风险。比如,一个普通员工只能通过存储过程查询自己的工资记录,而不能直接去数据库里翻所有人的数据。

最后,存储过程还支持参数化操作,啥意思?就是你可以给它传参数,让它根据不同的输入执行不同的逻辑。这就跟函数差不多,比如你传一个员工ID,它就返回这个员工的具体信息,灵活性很高。

存储过程跟普通SQL有啥不一样?

说到这儿,可能有朋友会问:存储过程跟普通的SQL语句有啥区别啊?不都是在数据库里跑的吗?确实,它们本质上都是SQL,但用途和运行方式有挺大差别。

普通SQL语句通常是临时写的,你在客户端工具里,比如Navicat或者命令行,敲一条SELECT或者UPDATE,跑完就没了,下次还要再敲。而存储过程是提前写好、存储在数据库里的,相当于一个固定的“脚本”,可以反复调用,不用每次都重新写。

再者,普通SQL语句一般是单条执行的,最多通过脚本批量跑几条。而存储过程可以包含多条语句,甚至还能有条件判断、循环这些控制逻辑,功能更强大。举个例子,普通SQL只能简单地查个数据,而存储过程可以先查数据,再根据结果更新另一张表,逻辑全在里面封装好了。

还有运行效率的区别。普通SQL每次执行都要经过解析、优化、执行这一整套流程,而存储过程因为预编译,省去了重复解析的步骤,速度更快,尤其在频繁调用的场景下,效果特别明显。

存储过程的基本语法结构

好了,聊了这么多理论,咱们得看看存储过程到底咋写。别担心,语法其实不复杂,掌握几个关键点就行。下面是存储过程的基本结构,配合一个简单的例子,帮你快速上手。

在MySQL里,创建存储过程用的是CREATE PROCEDURE语句,基本格式是这样的:

CREATE PROCEDURE 存储过程名字 (参数列表)
BEGIN
    -- 这里写具体的SQL逻辑
END;

参数列表可以有,也可以没有,具体看你的需求。参数一般分三种:(输入参数)、(输出参数)、(既能输入也能输出)。这跟编程语言里的参数有点像,待会儿咱们举例说明。

存储过程的主体部分在和之间,这里可以写任意SQL语句,包括查询、更新、插入、删除啥的,还能用、这些控制语句,功能相当丰富。

下面是个简单的例子,假设我们要写一个存储过程,接收一个员工ID,查询这个员工的名字和薪水:

DELIMITER //

CREATE PROCEDURE get_employee_info (IN emp_id INT)
BEGIN
    SELECT name, salary 
    FROM employees 
    WHERE id = emp_id;
END //

DELIMITER ;

这里有个小细节,DELIMITER //是啥?因为存储过程里有很多语句,每个语句都以分号;结尾,为了不让MySQL把分号当成整个存储过程的结束标志,我们得临时把分隔符改成//。写完存储过程再改回来。这个是MySQL的一个小特性,记住了就行。

咋调用存储过程?

写好存储过程后,咋用它呢?调用存储过程用的是语句,简单得不得了。还是拿上面的例子来说,假设你想查ID为5的员工信息,直接这样:

CALL get_employee_info(5);

如果存储过程有输出参数,那就稍微复杂点。比如,咱们改一下之前的例子,让它返回员工的名字和薪水作为输出参数:

DELIMITER //

CREATE PROCEDURE get_employee_details (IN emp_id INT, OUT emp_name VARCHAR(50), OUT emp_salary DECIMAL(10,2))
BEGIN
    SELECT name, salary INTO emp_name, emp_salary
    FROM employees 
    WHERE id = emp_id;
END //

DELIMITER ;

调用的时候,你得先声明变量来接收输出值,然后再用:

SET @name = '';
SET @salary = 0.0;
CALL get_employee_details(5, @name, @salary);
SELECT @name, @salary;

看到没?调用存储过程跟调用函数差不多,输入参数直接传,输出参数得用变量接住,然后再查变量的值。

存储过程在MySQL里的应用场景

讲了这么多,你可能还是有点懵:存储过程到底在啥时候用啊?别急,我给你举几个实际场景,保准你立马明白它的价值。

一个常见的场景是批量数据处理。比如,你每个月要统计销售数据,把结果更新到汇总表里。这个过程可能涉及好几张表,还要计算平均值、总和啥的。如果用普通SQL,你得写一堆语句,手动跑一遍。而用存储过程,你可以把所有逻辑写进去,定时任务调用一下就搞定,省时省力。

还有就是复杂事务管理。在一些业务场景里,你的操作必须是原子性的,要么全成功,要么全失败。比如转账操作,A账户扣钱,B账户加钱,这两步必须同时完成。如果用存储过程,你可以在里面用`START TRANSACTIONCOMMIT`来控制事务,确保数据一致性。

另外,存储过程在权限控制上也很有用。假设你有个系统,普通用户只能查看自己的订单,不能随便翻别人的数据。你可以写个存储过程,接收用户ID作为参数,只返回这个ID对应的订单数据,然后把存储过程的执行权限给用户,但不给直接查表的权限,这样就安全多了。

再举个例子,存储过程在数据迁移或者清洗的时候也挺好使。假设你要把旧系统的数据导入新系统,中间需要格式转换、去重啥的,这些逻辑都可以写在存储过程里,跑一遍就完事,不用写一堆脚本。

存储过程的基本操作小总结

为了让你更直观地掌握存储过程的操作,我弄了个小表格,列出常见命令和用途:

创建存储过程

CREATE PROCEDURE proc_name() ...

定义一个新的存储过程

调用存储过程

CALL proc_name(param1, param2)

执行存储过程,传入参数

查看存储过程

SHOW CREATE PROCEDURE proc_name

查看某个存储过程的定义

删除存储过程

DROP PROCEDURE proc_name

删除指定的存储过程

查看所有存储过程

SHOW PROCEDURE STATUS

列出数据库中所有存储过程的信息

这个表格简单明了,平时用的时候可以参考一下。尤其是`SHOW CREATE PROCEDURE`,有时候你忘了自己写的存储过程里是啥逻辑,直接查一下就清楚了。

小插曲:存储过程的局限性

当然啦,存储过程也不是万能的,用之前也得了解它的局限性。比如,它不适合处理特别复杂的逻辑,如果逻辑太复杂,维护起来反而麻烦,建议还是放应用层处理。另外,存储过程的调试功能比较弱,MySQL不像一些编程IDE那样有强大的调试工具,出错的时候定位问题可能有点费劲。

还有,存储过程跟数据库绑定太紧,如果将来你想换数据库,比如从MySQL转到PostgreSQL,存储过程的代码基本得重写,可移植性不太好。所以,用之前得想清楚,业务逻辑到底放哪合适。

第二章:MySQL存储过程的工作原理

要真正搞懂MySQL存储过程的魅力,光知道它的定义和用法可不够,咱们得深入到它的“内脏”里去,看看它到底是怎么工作的。这部分内容会带你走进存储过程的底层实现,聊聊它的编译、执行过程,以及它和MySQL引擎之间的互动方式。

存储过程的底层实现:从创建到调用

存储过程本质上是一段预先写好的SQL代码块,被存储在数据库里,等待被调用。它的特别之处在于,这段代码不是每次调用时都临时解析,而是提前编译好存储在数据库的元数据中。这个过程有点像咱们写程序时把代码编译成机器码,省去了重复翻译的麻烦。

当你用CREATE PROCEDURE语句创建一个存储过程时,MySQL会干几件事儿:先是把你的SQL语句解析成内部的语法树,然后检查语法是否正确、权限是否够用。如果一切没问题,这些信息就会被存到系统表里。这个表记录了存储过程的名字、参数、主体代码等关键信息,相当于一个“目录索引”。存储过程的主体代码会被编译成一种中间格式,类似于字节码,方便后续快速执行。

到了调用阶段,用命令触发存储过程时,MySQL不会重新解析代码,而是直接从里调出预编译的结果,然后结合传入的参数,交给执行引擎去跑。这个过程省去了大量重复的语法解析和优化步骤,效率自然就上去了。

举个例子,假设咱们写了个简单的存储过程来统计某个表里的记录数:

DELIMITER //
CREATE PROCEDURE count_records()
BEGIN
    SELECT COUNT(*) FROM employees;
END //
DELIMITER ;

创建完成后,MySQL会把这段代码编译并存起来。每次调用CALL count_records()时,它直接读取编译好的逻辑,执行统计操作,而不是每次都从头解析SELECT COUNT(*)这条语句。这就像你做菜时已经备好了配料和步骤,直接下锅炒就行,不用每次都从头洗菜切菜。

编译与执行的详细流程

为了更清晰地理解这个过程,咱们可以用一个简化的流程图来展示存储过程从创建到执行的步骤:

创建存储过程

解析SQL、检查语法和权限、编译成中间格式

解析器、元数据表

存储

将编译结果保存到系统表

mysql.proc

调用存储过程

读取编译结果、绑定参数

执行引擎

执行

运行逻辑、返回结果

存储引擎(如InnoDB)

这个流程的核心在于“预编译”。普通的SQL语句每次执行都需要经过解析、优化、生成执行计划等步骤,而存储过程因为提前干完了这些活儿,后续调用时直接跳到执行阶段,节省了大量时间。尤其是在高并发的场景下,这种优势会非常明显。

另外,存储过程的执行还涉及到参数绑定。存储过程支持传入参数,这些参数会在调用时被动态绑定到预编译的逻辑中。MySQL会确保参数类型和数量匹配定义时的要求,如果不匹配会直接报错,防止运行时出问题。这种机制有点像编程语言里的函数调用,保证了代码的安全性和可控性。

存储过程与MySQL引擎的交互

存储过程的执行离不开MySQL的存储引擎,比如常见的InnoDB或MyISAM。存储过程本身只是一层逻辑封装,实际的数据操作还是得靠底层的存储引擎来完成。简单来说,存储过程就像一个“指挥官”,它告诉存储引擎去执行具体的增删改查操作,而存储引擎负责具体的实现细节,比如索引查找、锁管理、事务处理等。

以InnoDB为例,当存储过程里有一条语句时,存储过程会把这条命令翻译成内部的执行计划,交给InnoDB去处理。InnoDB会根据索引、锁机制等完成数据更新,并把结果反馈给存储过程。如果存储过程涉及到事务,比如用`BEGIN TRANSACTIONCOMMIT`,这些事务控制指令也会被传递给存储引擎,由它来保证数据的一致性。

这种交互方式有个好处,就是存储过程可以屏蔽底层的复杂性。你不需要关心数据是怎么存的、索引是怎么用的,只要写好逻辑,MySQL会帮你协调好一切。当然,这也意味着存储过程的性能一定程度上受限于存储引擎的实现。如果底层的引擎效率不高,存储过程再怎么优化也难有质的飞跃。

减少网络开销:存储过程的核心优势

聊到存储过程的效率提升,网络开销的减少是个绕不过去的话题。在传统的客户端-服务器架构里,每次执行SQL语句都需要客户端和数据库之间来回通信。假如你有个复杂的业务逻辑,需要执行10条SQL语句,那可能就要来回10次网络请求,每次请求都带着解析、传输、响应的成本。

存储过程的妙处就在于,它把多条SQL语句打包成一个逻辑单元,存储在数据库端。客户端只需要发送一次请求,就能触发整个逻辑的执行,中间的所有操作都在数据库内部完成,客户端只接收最终结果。这就像你去餐厅点菜,不用每次点一道菜都跑去厨房下单,而是直接告诉服务员一个套餐名字,厨房一次性把所有菜都做好端上来。

举个实际场景,假设你有个电商系统,需要处理订单生成逻辑,包括插入订单记录、更新库存、记录日志三步。如果用普通SQL,每次操作都得单独发请求:

1. INSERT INTO orders ...

2. UPDATE inventory SET stock = stock - 1 ...

3. INSERT INTO logs ...

每一步都涉及网络传输和解析开销。而如果用存储过程,可以这样写:

DELIMITER //
CREATE PROCEDURE process_order(IN order_id INT, IN product_id INT)
BEGIN
    INSERT INTO orders (id, product_id) VALUES (order_id, product_id);
    UPDATE inventory SET stock = stock - 1 WHERE id = product_id;
    INSERT INTO logs (order_id, action) VALUES (order_id, 'order_created');
END //
DELIMITER ;

客户端只需要调用CALL process_order(1001, 2001),一次请求就搞定所有操作,网络开销直接从三次降到一次。这种方式在高频操作或者网络延迟较大的场景下,效果尤为显著。

执行效率的提升:预编译与计划缓存

除了网络开销,存储过程的另一个效率提升点在于预编译和执行计划缓存。普通的SQL语句每次执行时,MySQL都要重新解析语句、生成执行计划,这个过程在复杂查询中可能耗费不少时间。而存储过程因为已经预编译过,执行计划也被缓存下来,后续调用时可以直接复用。

举个例子,假设你有个复杂的报表查询,涉及多表关联和聚合操作。如果每次都直接跑SQL,MySQL得反复计算最优的执行路径,比如先扫描哪个表、用哪个索引。而用存储过程后,第一次创建时就确定了执行计划,后续调用直接走老路,省去了优化的时间。

当然,这也有个小坑:如果表结构或者数据分布发生了剧烈变化,存储过程的执行计划可能不再是最优的。MySQL目前不会自动重新优化存储过程的计划,所以在这种情况下,你可能需要手动重建存储过程,让它重新生成计划。这个问题在动态数据场景下需要特别注意。

存储过程的局限与权衡

虽然存储过程在效率上有不少优势,但它也不是万能的。它的预编译特性决定了它更适合逻辑固定、重复调用的场景。如果你的业务逻辑经常变,频繁修改存储过程可能会带来额外的维护成本。而且存储过程的调试比较麻烦,MySQL不像编程语言那样有完善的调试工具,出问题时排查起来可能会比较头疼。

另外,存储过程的性能提升也不是绝对的。如果你的业务瓶颈不在SQL解析或者网络传输上,而是在数据量或者存储引擎的I/O上,存储过程能带来的改进就很有限。所以在使用之前,最好先分析一下性能瓶颈在哪里,别盲目把所有逻辑都塞到存储过程里。

一个实战案例:批量数据处理的优化

为了把这些理论落到实处,咱们来看一个实际案例。假设你有个任务,需要批量更新一堆用户记录的状态,每次处理1000条数据。如果用普通SQL,可能会写个循环,每次发一条语句,代码大概是这样的:

-- 伪代码,客户端循环
FOR i = 1 TO 1000
    UPDATE users SET status = 'active' WHERE id = i;
END FOR;

这种方式每次循环都发一次请求,网络开销巨大。改用存储过程后,可以把逻辑放到数据库端:

DELIMITER //
CREATE PROCEDURE batch_update_users(IN start_id INT, IN end_id INT)
BEGIN
    UPDATE users SET status = 'active' WHERE id BETWEEN start_id AND end_id;
END //
DELIMITER ;

客户端只需要调用CALL batch_update_users(1, 1000),一次请求就搞定所有更新操作。实际测试中,这种方式的执行时间往往能缩短到原来的1/10甚至更低,特别是在网络延迟较大的环境下,效果非常明显。

第三章:存储过程的实现与开发技巧

存储过程作为MySQL中一种强大的工具,不仅仅是为了提升性能,更是为了让复杂的业务逻辑在数据库层得以封装和复用。接下来咱们就深入聊聊如何从零开始创建、修改和删除存储过程,同时还会探讨参数的使用、条件判断、循环结构以及异常处理这些核心开发技巧。

从零开始:创建、修改和删除存储过程

要玩转存储过程,第一步当然是学会怎么把它写出来。MySQL中创建存储过程的语法其实挺简单,但细节上有些坑需要注意。基本语法是这样的:

CREATE PROCEDURE procedure_name ([parameter_list])
BEGIN
    -- 这里写你的SQL逻辑
END;

比如,假设我们要写一个简单的存储过程来查询某个用户的订单总数,可以这么干:

DELIMITER //
CREATE PROCEDURE get_order_count(IN user_id INT)
BEGIN
    SELECT COUNT(*) AS total_orders
    FROM orders
    WHERE user_id = user_id;
END //
DELIMITER ;

这里用到了来临时修改分隔符,因为存储过程体内的语句可能包含分号,直接用默认分隔符会报错。参数表示输入参数,稍后咱们会细聊参数类型。创建好之后,调用它就很简单,用关键字:

CALL get_order_count(1001);

如果发现存储过程逻辑有问题咋办?别慌,MySQL支持修改,用ALTER PROCEDURE可以调整一些属性,但如果你要改逻辑,干脆直接用删掉再重新创建更省事。删除的语法是:

DROP PROCEDURE IF EXISTS get_order_count;

这里的IF EXISTS是个好习惯,能避免因存储过程不存在而报错。修改的话,通常是先删后建,因为MySQL不像某些其他数据库那样直接支持修改存储过程体。

有一点要提醒,创建存储过程时,权限管理很重要。默认情况下,只有创建者或有对应权限的用户才能调用或修改,所以在团队协作时,记得用给相关用户授权,比如:

GRANT EXECUTE ON PROCEDURE get_order_count TO 'user_name'@'localhost';

参数类型:IN、OUT和INOUT的妙用

存储过程的参数类型有三种:、和,这三者决定了数据怎么在存储过程内外流动。是输入参数,调用时传值进去,过程内部只能读不能改;是输出参数,过程内部可以赋值,调用结束后返回给调用者;则是双向参数,既能传入也能返回。

举个例子,假设我们要写一个存储过程,既接受用户ID作为输入,又返回该用户的订单总数和最后下单时间:

DELIMITER //
CREATE PROCEDURE get_user_order_info(
    IN user_id INT,
    OUT total_orders INT,
    OUT last_order_time DATETIME
)
BEGIN
    SELECT COUNT(*) INTO total_orders
    FROM orders
    WHERE user_id = user_id;
    
    SELECT MAX(order_time) INTO last_order_time
    FROM orders
    WHERE user_id = user_id;
END //
DELIMITER ;

调用时需要声明变量来接收参数的值:

SET @total = 0;
SET @last_time = NULL;
CALL get_user_order_info(1001, @total, @last_time);
SELECT @total, @last_time;

至于,它适合那种需要传入一个值,处理后又返回修改后的值的场景。比如,我们要根据用户积分计算折扣率,同时更新积分:

DELIMITER //
CREATE PROCEDURE calculate_discount(
    INOUT user_points INT,
    OUT discount_rate DECIMAL(5,2)
)
BEGIN
    IF user_points >= 1000 THEN
        SET discount_rate = 0.2;
        SET user_points = user_points - 100;
    ELSE
        SET discount_rate = 0.05;
    END IF;
END //
DELIMITER ;

调用时,参数也得用变量:

SET @points = 1200;
SET @rate = 0.0;
CALL calculate_discount(@points, @rate);
SELECT @points, @rate; -- 结果可能是 1100 和 0.2

这里有个小技巧,参数名别和表字段名冲突,不然容易引发歧义,调试时会头疼。

条件语句:让逻辑更灵活

存储过程里少不了条件判断,MySQL支持和两种方式。语句适合简单的二选一逻辑,比如上面计算折扣的例子。而更适合多分支判断,比如根据订单金额分级给奖励:

DELIMITER //
CREATE PROCEDURE award_bonus(
    IN order_amount DECIMAL(10,2),
    OUT bonus INT
)
BEGIN
    SET bonus = 0;
    CASE
        WHEN order_amount > 1000 THEN SET bonus = 100;
        WHEN order_amount > 500 THEN SET bonus = 50;
        WHEN order_amount > 100 THEN SET bonus = 10;
        ELSE SET bonus = 0;
    END CASE;
END //
DELIMITER ;

条件语句用得好,能让存储过程的逻辑非常灵活,但别嵌套太深,不然代码可读性会直线下降,维护起来跟噩梦一样。

循环结构:批量处理的神器

循环在存储过程中特别有用,尤其是处理批量数据时。MySQL支持、和三种循环方式。是最常见的,先判断条件再执行;是先执行再判断,至少跑一次;则需要手动设置退出条件。

假设我们要写一个存储过程,批量插入测试数据,可以用循环:

DELIMITER //
CREATE PROCEDURE insert_test_users(
    IN start_id INT,
    IN end

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

17年+码农经历了很多次面试,多次作为面试官面试别人,多次大数据面试和面试别人,深知哪些面试题是会被经常问到。 在多家企业从0到1开发过离线数仓实时数仓等多个大型项目,详细介绍项目架构等企业内部秘不外传的资料,介绍踩过的坑和开发干货,分享多个拿来即用的大数据ETL工具,让小白用户快速入门并精通,指导如何入职后快速上手。 计划更新内容100篇以上,包括一些企业内部秘不外宣的干货,欢迎订阅!

全部评论

相关推荐

不进大厂不改名qaq:对啊,我要是真有很好的技术实现思路,还要mentor干嘛
投递字节跳动等公司9个岗位 实习吐槽大会
点赞 评论 收藏
分享
没有offer的瓦学弟:我去!这么晚还有HC?大佬,牛
投递字节跳动等公司9个岗位 腾讯求职进展汇总
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务