DROP EVENT 只有两种写法:带 IF EXISTS 和不带,差别在「可重入」与「报错」。
删完必须验:事件真的不跑了、注册表真的空了——只看一句「删除成功」不够。
批量删要先按前缀筛名单,再逐个删;图省事写成「清空所有」,别的任务一起没。
第 17 章最后一篇。教材 17.4 只有删除这一节,17.5 小结和 17.6 实践与练习并进本篇末尾。
删除这件事本身很短,但「删完之后怎么确认」不短——这一篇的重心在后半段。
和第 14 章删过程、第 15 章删触发器是同一套纪律:IF EXISTS 让脚本可重入,
但也会把「名字拼错」一起吞掉。
一、DROP EVENT 的两种写法
教材给了两种:带 IF EXISTS 和不带。
DROP EVENT e_log;
DROP EVENT IF EXISTS e_log;
| 写法 | 再来一次的结果 |
|---|---|
DROP EVENT e_log |
EVENT e_log does not exist |
DROP EVENT IF EXISTS e_log |
跳过,不报错 |
第二次报 EVENT e_log does not exist——这是好事,说明它没假装成功。
加了 IF EXISTS 就变成「跳过」,脚本可以反复跑而不中断。
代价照旧:它会把「名字拼错」也一起吞掉。
这一条和第 15 章删触发器、第 14 章删过程是同一个套路。
IF EXISTS 让脚本可重入,但也让错误静默;删完必须回头查一次。
判断标准很简单:这个脚本会不会被跑第二遍?会,就得带 IF EXISTS;
带了,就必须配一条「删完查一遍」的语句。
二、删完要验:事件真的不跑了
删掉一个事件,要验两件事:它不再执行,而且它真的从注册表里没了。
这两件事不一样——状态是 DISABLE 的事件也不执行,但它还在。
| 时机 | 跑了几次 | 事件数 |
|---|---|---|
| 删之前,推到 00:02 | 2 | 1 |
| 删之后,推到 00:04 | 0 | 0 |
删之前跑了 2 次,删之后一次都没有,注册表也空了。 这一步必须验:删错名字、删错库、权限不够而「看起来成功」的情况都有。 只看一句「删除成功」不够——很多客户端在语句成功时只打一行「OK」,不告诉你影响了几行、 删的是哪个库里的哪个对象。 所以验的动作要落到数据上:数一下、读一下、跑一下。
三、批量删:拿到名单再动手
要清掉一批带前缀的事件,先按前缀筛出名单,再逐个删。
复刻版里筛到 2 个:e_clean 和 e_clean_old。
| 名字 | 结果 |
|---|---|
e_clean |
已删除 |
e_clean_old |
已删除 |
e_clean_nope |
EVENT e_clean_nope does not exist |
最后一个不存在,报错被接住,脚本继续——这就是「跳过不存在的、删掉该删的」。
再看剩下的:e_stat 和 e_keep 没被碰。
这两行是这一步真正的价值:按前缀筛,别的任务就保住了。
如果图省事写成「清空所有事件」,那两条一起没了,而且也是静默消失。
「先筛名单、再逐个删」这个套路可以推广到所有批量操作。
它的关键不是循环,是那一步「把名单打印出来看一眼」——人眼扫一遍,比任何断言都可靠。
四、本章小结与实践与练习
第 17 章四篇,真正要带走的是五条:
一、事件是「库内的定时任务」,和约束、触发器构成「库自己动」的三件事。
二、两层开关:全局 event_scheduler 默认 OFF,单个事件还有 ENABLE/DISABLE,
两层都得开。
三、ON SCHEDULE 只有 AT 和 EVERY 两种;不给 STARTS 时起点是建事件的时刻。
四、ON COMPLETION 决定跑完留不留;不留就等于「连记录都没有了」,慎用。
五、补不补跑是引擎行为,不是你能要求的;要补就在事件体里查断点。
这一章最该记住的一句是:事件是「看不见的进程」。
过程要有人 CALL,视图要有人 SELECT,触发器至少还挂在某张表上——事件什么都没有,
它只在时间里存在。
所以它出问题的方式也很特别:不报错、不告警,就是「昨天那份统计没生成」。
要防这个,只有两条:一是把事件列进监控(查 SHOW EVENTS 的 LAST_EXECUTED),
二是让事件体自己写日志。
17.6 的练习可以挑这三道:
建一个每分钟跑一次的事件,让它往日志表写一行,跑十分钟后数行数;
给同一批事件加 ENDS,把它设成五分钟之后,观察它自己变 DISABLE;
写一段脚本,按名字前缀列出、确认、批量删,删完再查一遍还剩什么。
三道都动手做过,这一章就算真读完了。
五、动手 Demo:删之前、删之后、批量删,三步都要验
这一段把删除的三件事跑一遍:两种 DROP 写法各试一次、
删之前推一次时钟再删之后再推一次(看跑的次数从 2 变 0)、最后按前缀筛出名单批量删,
末尾故意混一个不存在的名字。
BASE = datetime.datetime(2026, 11, 1, 0, 0, 0)
s = Sched(BASE)
s.create("e_log", every=60)
# ① 两种写法
s.drop("e_log")
try:
s.drop("e_log") # 第二次:EVENT e_log does not exist
except KeyError as e:
print(e.args[0])
def drop_if(name): # 相当于 DROP EVENT IF EXISTS
try:
s.drop(name)
return u"已删除"
except KeyError:
return u"跳过(不存在)"
print(drop_if("e_log")) # 跳过(不存在)
# ② 删完要验:跑的次数和注册表一起看
s.create("e_run", every=60)
print(len(s.tick(BASE + datetime.timedelta(minutes=2))), len(s.ev)) # 2 1
s.drop("e_run")
print(len(s.tick(BASE + datetime.timedelta(minutes=4))), len(s.ev)) # 0 0
# ③ 批量删:先筛名单,再逐个删
for nm in ("e_clean", "e_clean_old", "e_stat", "e_keep"):
s.create(nm, every=3600)
names = sorted(k for k in s.ev if k.startswith("e_clean"))
print("筛到", len(names), names)
for nm in names + ["e_clean_nope"]:
print(nm, drop_if(nm))
print("剩下", sorted(s.ev)) # ['e_keep', 'e_stat']
实跑输出:
--------------------------------------------------------------------
删除事件:删完记得回头看一眼
--------------------------------------------------------------------
DROP EVENT 的两种写法
--------------------------------------------------------------------
[教材原码 · 本沙箱不执行]
| DROP EVENT e_log;
| DROP EVENT IF EXISTS e_log;
写法 结果
----------------------------------------------
`DROP EVENT e_log` 删掉了
再来一次 EVENT e_log does not exist
**第二次报 `EVENT e_log does not exist`——这是好事,说明它没假装成功。**
**加了 `IF EXISTS` 就变成「跳过」,脚本可以反复跑而不中断。**
**代价照旧:它会把「名字拼错」也一起吞掉。**
**这一条和第 15 章删触发器、第 14 章删过程是同一个套路:**
**`IF EXISTS` 让脚本可重入,但也让错误静默;删完必须回头查一次。**
删完要验:事件真的不跑了
--------------------------------------------------------------------
时机 跑了几次 事件数
----------------------------------
删之前推到 00:02 2 1
删之后推到 00:04 0 0
**删之前跑了 2 次,删之后一次都没有,注册表也空了。**
**这一步必须验:删错名字、删错库、权限不够而「看起来成功」的情况都有,只看一句「删除成功」不够。**
批量删:拿到名单再动手
--------------------------------------------------------------------
**要清掉一批带前缀的事件,先按前缀筛出名单:**
筛到 2 个:e_clean、e_clean_old
名字 结果
-----------------------------------------------
e_clean 已删除
e_clean_old 已删除
e_clean_nope EVENT e_clean_nope does not exist
**最后一个不存在,报错被接住,脚本继续——这就是「跳过不存在的、删掉该删的」。**
**再看剩下的:`e_stat` 和 `e_keep` 没被碰。**
剩下的事件
----------
e_keep
e_stat
**这两行是这一步真正的价值:按前缀筛,别的任务就保住了。**
**如果图省事写成「清空所有事件」,那两条一起没了,而且也是静默消失。**
第①步那个报错文本是原样的:EVENT e_log does not exist。
注意它说的是 does not exist,不是 already dropped——库只告诉你「现在没有」,
不告诉你「是不是你删的」。
第②步两组数要一起读:2 1 和 0 0。
前一组是「跑了两次、库里还有一个」,后一组是「一次没跑、库里空了」。
只看「跑的次数」会漏掉「对象还在」这种情况,只看「事件数」会漏掉「状态停用但没删」。
第③步最后那行 ['e_keep', 'e_stat'] 是整段的目的:批量删不等于清空,
删的必须只是筛出来的那一批。
漏掉这一步,一次「清理」就可能把定时统计、定时备份一起带走。
30 秒实验
- 把第②步的顺序改成「先删、再推时钟」,看两次打印分别是什么, 说明为什么这样验不出「它真的不跑了」。
- 给批量删加一条保护:如果筛出来的名单超过 5 个就中止,打印名单让人确认。
- 把
e_clean换成e_前缀再跑一次,看会删掉哪些,再想想这个前缀该不该这么写。
常见误区
- 以为
DROP EVENT之后事件就不存在了、什么都不用管。删错名字、删错库都可能「看起来成功」, 删完必须查一遍。 - 以为
IF EXISTS是纯好处。它会把「名字拼错」也一起吞掉,脚本不中断但事情没做成。 - 批量删时图省事写成「清空所有事件」。按前缀筛出名单再删,是保住别的任务的唯一办法。
自测三问(点开对答案)
1. `DROP EVENT` 带不带 `IF EXISTS`,差别在哪? —— 不带:事件不存在时报错,脚本中断。带:跳过,脚本继续。 前者适合「必须删掉」的场景,后者适合「可重复执行的部署脚本」。 但后者会把名字拼错也吞掉,必须配一条删后复查。 2. 验证一个事件真被删掉了,要验哪两件事? —— 一是它不再执行(推一次时钟,跑的次数为 0),二是它从注册表里没了(事件数为 0)。 只验一件不够:状态 `DISABLE` 的事件也不执行,但它还在。 3. 批量删之前为什么要先把名单打印出来? —— 因为「按前缀筛」这一步本身就可能筛错。 人眼扫一遍名单,比任何断言都可靠——尤其是前缀写短了一位的时候。第 17 章到这里结束。下一篇进入第 18 章:MySQL 用户管理——从「谁能连上来」讲起。
- ☐ (本篇完,下一篇 18.1 数据备份)