☰
达梦数据库DELETE删除操作:从执行原理到批量删除与误删恢复
2026/11/4 4:50:13 网站建设 项目流程

说起数据库里的删除操作,很多人第一反应就是一句DELETE FROM 表名 WHERE 条件,好像没什么可讲的。但真到了生产环境,尤其是面对达梦数据库这类国产数据库,删除往往是最容易出事的操作之一:一个漏写的 WHERE 能把整张表清空,一次几十万行的大事务能把回滚段撑爆,一个没注意的外键级联能把三张关联表的数据一起带走。这篇内容就围绕达梦数据库的 DELETE 操作展开,把语法细节、执行原理、批量删除的落地写法、误删后的补救路径、以及排查问题的思路,一条一条拆开讲。不管你是刚接手达梦、从别的数据库转过来的开发,还是需要定期做数据清理的运维,看完都能直接拿去用。文中涉及的操作步骤都基于达梦数据库常见的部署形态和通用实践补全,具体小版本的差异请以你手头的版本手册为准。

1. 三种删除路径的取舍:先想清楚要删的是什么

删除这件事,动手之前最关键的不是怎么写 SQL,而是判断这一次到底该用哪种方式。达梦里能"让数据消失"的手段至少有三种:DELETE、TRUNCATE、DROP,另外还有一类业务层面的"逻辑删除"。选错了工具,轻则性能不达标,重则数据无法回滚。

1.1 DELETE、TRUNCATE、DROP 的本质差异

很多新手会把这三者当成"删数据"的三种写法,实际上它们的语义层级完全不同。DELETE 是 DML,操作的是行,走事务,可以回滚,会写 UNDO 和 REDO,会触发行级触发器;TRUNCATE 是 DDL,操作的是整个段,隐式提交,不可回滚,速度快但没得挑;DROP 干脆把表结构和数据一起抹掉,进回收站(如果回收站开着)还能捞回来,不进就真没了。

我在实际项目里见过最典型的误用,是有人为了"清空测试表快一点"直接写了 TRUNCATE,结果那张表被另外几个模块的存储过程引用着,外键约束直接报错;还有人反过来,用 DELETE 去删一张两千万行的历史表,跑了四十分钟还没结束,中间锁住了整张表,业务侧全部超时。这两种情况的根子都在于:没有先判断这次删除的"粒度"和"可回滚要求"。

用一张表把差异摊开看会更清楚:

维度DELETETRUNCATEDROP
语句类型DMLDDLDDL
是否可回滚可以(未提交前)不可以不可以(可走回收站/闪回)
是否支持 WHERE支持不支持不适用
触发器触发行级触发器不触发不触发
空间释放不释放,只标记立即释放段空间释放全部
锁粒度行锁为主表级排他表级排他
外键引用受约束限制被引用时通常不允许被引用时通常不允许
典型耗时随行数线性增长基本恒定基本恒定

看这张表就能明白一件事:DELETE 的价值不在快,而在"可控"。它能精确到行、能参与事务、能被触发器捕获、能在提交前反悔。凡是删除条件需要计算、需要跟业务状态挂钩、需要保留审计痕迹的场景,只能用它。

1.2 逻辑删除与物理删除的分界线

在动手写 DELETE 之前,还有一个更前置的问题:这条数据真的需要从物理上消失吗?我待过的几个团队,最后都走向了同一个结论——核心业务表尽量不做物理删除,用状态位或者删除标记列做逻辑删除,只有确定不再需要的归档数据才真正 DELETE。

这么做的理由很实际。第一是恢复成本,逻辑删除改个状态位就能"撤销",物理删除要走备份恢复,代价差着数量级。第二是关联完整性,一张订单表被物理删掉,下游的对账、统计、报表全部要跟着改口径。第三是审计要求,很多行业的数据需要留痕若干年,删掉就是违规。

但逻辑删除也不是万能的。它会带来两个副作用:一是所有查询都必须带上status <> 'DELETED'这类条件,只要有一处漏了就会出脏数据;二是表会越涨越大,索引膨胀,查询变慢,最后还是得做归档清理。我的做法是分层:业务主表用逻辑删除,日志表、临时表、中间结果表、超过保留期的归档数据用物理删除。判断标准很简单——这张表的数据如果丢了,会不会影响对账和追溯?会,就逻辑删;不会,就物理删。

提示:逻辑删除的标记列建议用定长字符或者小整数,不要用带时区的时间戳做标记,否则后续建索引、做分区都会很难受。

1.3 动手前的三项体检

确定要用 DELETE 之后,我习惯先做三项检查,这三项加起来不超过五分钟,但省下的可能是几个小时的恢复时间。

第一项,确认影响行数。把 DELETE 的 WHERE 条件原封不动搬到 SELECT COUNT(*) 里跑一遍,看清楚到底要删多少行。这一步看着笨,但能挡住绝大多数事故。如果查出来是几百万行,那就要走分批删除的路线,不能一把梭。

第二项,确认依赖关系。查一下这张表被哪些外键引用、上面挂了哪些触发器、有没有物化视图或者同义词依赖它。达梦可以通过数据字典视图查外键约束,也可以直接看建表语句。有级联删除的外键时,删主表会连带删子表,这个连锁范围必须提前算清楚。

第三项,确认备份和回滚能力。至少确认当前有可用的备份,或者这张表可以通过闪回、回收站找回。如果没有,那就先建一张影子表把要删的数据备份一份,语句很简单:CREATE TABLE 表名_bak_日期 AS SELECT * FROM 表名 WHERE 条件;。多花几秒钟,换一个保险。

2. DELETE 语句背后的执行细节

语法层面,达梦的 DELETE 和主流数据库基本一致,但真正决定成败的是 WHERE 条件、执行计划和锁行为这三块。这一章就按这个顺序展开。

2.1 语法骨架与几个容易写错的地方

达梦的 DELETE 基本形式是:

DELETE FROM 模式名.表名 WHERE 条件 [RETURNING 列名 INTO 变量];

方括号部分是可选的。RETURNING 子句在批量处理脚本里很有用,能直接把删除前的值带出来做记录。另外还有一种基于游标的写法WHERE CURRENT OF 游标名,适合在存储过程里逐行处理,但性能一般,实际用得不多。

几个容易踩的坑。第一个是模式名。达梦默认会把未加模式名的对象解析到当前用户的默认模式,如果你用 A 用户连上去想删 B 用户表里的数据,不加模式名往往会报"对象不存在",或者更糟——删到了同名的另一张表上。我的习惯是,凡是在脚本、存储过程、定时任务里出现的删除语句,一律带模式名。

第二个是视图。普通视图不能直接 DELETE,除非在视图上建了 INSTEAD OF 触发器。这个限制很多人不知道,写了半天以为是权限问题。第三个是分区表,DELETE 作用在整张表上,无法指定分区,想把某个分区整体清掉得用ALTER TABLE ... TRUNCATE PARTITION,那个是 DDL,不可回滚。

还有一点,达梦的命令行工具默认不是自动提交模式,执行完 DELETE 之后必须显式COMMIT,否则断开连接时数据会回滚。这个行为和某些数据库的默认自动提交不一样,从别的库转过来的人经常在这里犯迷糊——以为删完了,其实没生效;或者反过来,以为还没提交,结果另一条 DDL 语句把事务隐式提交了。

2.2 WHERE 条件写错是最大的事故源

我统计过自己经手过的删除事故,八成以上出在 WHERE 条件上。常见的有这么几种。

隐式类型转换导致条件失效。比如某个字段是 VARCHAR 类型,存的是纯数字,你写了WHERE id = 12345(不带引号),部分场景下达梦会把字段做隐式转换再比较,一旦字段里存在非数字内容,比较结果就可能不符合预期,甚至让索引失效变成全表扫描。更隐蔽的是日期字段,WHERE create_time = '2024-01-01'这种写法在某些格式下会被解析成当天的零点整,导致你想删当天的数据,结果只删了零点那一瞬间的记录。

NULL 值的坑。WHERE column <> 'X'这个条件永远筛不出 column 为 NULL 的行,因为 NULL 参与任何比较的结果都是未知。如果你的本意是"删掉所有不是 X 的行",那 NULL 行会被漏掉。正确写法是WHERE column <> 'X' OR column IS NULL。

字符串尾部空格。达梦在字符类型比较上遵循定长补空格的规则,'ABC'和'ABC '在某些比较场景下会被认为相等。如果业务标识字段允许带尾部空格,删除条件就必须考虑这一点。

没有主键或唯一键的缓一步。如果一张表没有主键,删除条件又依赖业务字段,那删完之后你连"到底删了哪几行"都说不清楚。这种表我一般要求先加主键或者唯一索引,再谈清理。

注意:任何在生产环境执行的 DELETE,都建议先在同一事务里跑一次SELECT COUNT(*)确认行数,再执行删除,最后核对影响行数是否一致。这三个数字对不上,立刻回滚。

2.3 执行计划怎么看才算看懂了

达梦的执行计划可以用 EXPLAIN 看。对 DELETE 语句,多数版本支持直接EXPLAIN DELETE FROM ...,如果解析不了,就把 WHERE 条件摘出来写成等价的 SELECT 语句看计划,效果是一样的,因为删除的定位过程和查询是同一套逻辑。

EXPLAIN DELETE FROM ORDER_LOG WHERE CREATE_TIME < TO_DATE('2024-01-01','yyyy-mm-dd');

看计划时我最关注三个点。

第一,走的是不是索引扫描。如果计划里出现全表扫描(TABLE SCAN)而你预期走索引,先检查 WHERE 条件里的列有没有索引、条件有没有对列做函数运算。WHERE TO_CHAR(CREATE_TIME,'yyyy-mm-dd') < '2024-01-01'这种写法会把索引废掉,改成CREATE_TIME < TO_DATE(...)才能用上。

第二,估算行数和实际行数差多少。差一个数量级说明统计信息过期了,需要重新收集。统计信息不准,优化器可能选错索引,也可能选错连接顺序,删除性能会莫名其妙地差。

第三,有没有额外的排序或临时表操作。大批量删除时如果计划里出现排序,说明需要额外的内存和临时空间,这时候要重点盯临时表空间的使用率。

我个人的经验是,删除语句的执行计划要和查询语句一样对待,尤其是那种运行在定时任务里的清理脚本。上线前看一次计划,心里有底;上线后如果发现慢,第一件事也是看计划有没有变化。

2.4 事务、锁与并发下的删除行为

DELETE 执行过程中会对目标行加行级排他锁,直到事务提交或回滚才释放。这意味着两件事:一是你删得越久,锁持有时间越长;二是如果同时有别的会话在更新同一批行,就会产生锁等待。

达梦默认的锁等待是有超时时间的,超时之后会报错退出。在业务高峰期跑大事务删除,最常见的现象就是删除会话和业务会话互相等,最后双双超时。处理这类问题,我的做法是错峰加缩小事务:把一次大删除拆成若干个几百到几千行的小事务,每个小事务独立提交,锁持有时间从几十分钟缩短到几秒,对其他会话的影响就小得多。

如果要提前探查锁的情况,可以在会话里查锁相关的动态视图,看看当前有哪些会话持锁、在等什么资源。实际操作中更实用的做法是写一个监控脚本,定期采样锁等待情况,一旦发现有会话等待超过阈值就告警。

还有一种情况值得单独说:删除和更新的顺序死锁。两个会话,一个先删 A 表再更新 B 表,另一个先更新 B 表再删 A 表,交叉等待就死锁了。达梦会自动检测死锁并回滚其中一个事务,报错信息里会明确指出。解决办法是统一访问顺序——所有涉及多表的写操作,都按同一顺序(比如按表名字母序或者按主外键层级)执行,从根上消掉循环等待。

2.5 触发器和外键带来的连锁反应

触发器是删除操作里最容易被忽视的性能杀手。一个 BEFORE DELETE 的行级触发器,如果你删十万行,它就执行十万次。触发器里如果还有查询、还有写日志表、甚至还有远程调用,那性能会直接塌掉。

排查方法很直接:先看表上有哪些触发器,再看触发器体里做了什么。如果发现触发器里做了与业务主流程无关的统计更新,能挪到异步处理就挪走。实在不能挪的,就把单次删除的行数压下来,配合分批提交,让单次事务的触发器执行次数可控。

外键方面,定义时如果写了ON DELETE CASCADE,删主表会自动删子表;写了ON DELETE SET NULL,会把子表的外键列置空。这两种行为都是隐式的,从删除语句本身看不出来。我在做数据清理方案评审时,一定会要求把相关表的外键定义全部列出来,标明哪些是级联、哪些是置空、哪些是限制删除。曾经有一次清理任务,主表删了八万行,因为没有注意到一个级联外键,子表被连带删掉了六十多万行,虽然数据本身是该删的,但执行时间和锁范围完全超出预期,把整个维护窗口撑爆了。

3. 大批量删除的实操全过程

前面讲的是原理和判断,这一章落到具体操作上。批量删除是达梦上最常见的重活,写法对不对,效率能差出一个数量级。

3.1 为什么要分批,分多少批

一次性删几十万行,问题出在三个地方。第一,UNDO 空间会被大量占用,事务提交前这些空间不能复用,极端情况下会把回滚段撑满,导致整个实例的其他事务也受影响。第二,锁持有时间过长,业务侧的更新操作全部排队。第三,一旦中途失败需要回滚,回滚的时间和删除的时间差不多甚至更长,维护窗口直接翻倍。

分批之后,每个事务只处理几千行,UNDO 可以及时释放,锁快速释放,失败时只需要重跑失败的那一批,代价可控。至于每批多少行,没有一个放之四海皆准的数字,我的经验区间是 2000 到 10000 行,具体怎么定要看单行数据的大小和触发器开销。单行宽表、带触发器的,往小的取,2000 到 3000;纯日志表、无触发器的,可以取 8000 到 10000。

有一个简单的估算方法:先跑一批 1000 行,记录耗时,然后按线性外推。如果单批耗时在 1 秒以内,说明可以适当加大;如果超过 5 秒,就往小调。目标是把单批耗时控制在 1 到 3 秒之间,这样锁的影响和事务开销都比较均衡。

3.2 分批删除的三种写法与选型

写法一:基于 ROWID 的子查询。这是我最推荐的通用写法,稳定性最好。

DECLARE V_CNT INT := 1; BEGIN WHILE V_CNT > 0 LOOP DELETE FROM SALES.ORDER_LOG WHERE ROWID IN ( SELECT RID FROM ( SELECT ROWID RID FROM SALES.ORDER_LOG WHERE CREATE_TIME < TO_DATE('2024-01-01','yyyy-mm-dd') ) WHERE ROWNUM <= 5000 ); V_CNT := SQL%ROWCOUNT; COMMIT; DBMS_LOCK.SLEEP(0.1); END LOOP; END; /

ROWID 是行的物理地址,用它定位最精确,不受业务字段重复值的影响。中间那个DBMS_LOCK.SLEEP(0.1)是刻意加的,给其他会话一点喘息机会,如果你的库负载不高可以去掉。

写法二:基于主键区间的循环。适合主键单调递增的表,比如自增 ID 的日志表。

DECLARE V_MIN_ID BIGINT; V_MAX_ID BIGINT; BEGIN SELECT MIN(ID), MAX(ID) INTO V_MIN_ID, V_MAX_ID FROM SALES.ORDER_LOG WHERE CREATE_TIME < TO_DATE('2024-01-01','yyyy-mm-dd'); WHILE V_MIN_ID <= V_MAX_ID LOOP DELETE FROM SALES.ORDER_LOG WHERE ID >= V_MIN_ID AND ID < V_MIN_ID + 5000; COMMIT; V_MIN_ID := V_MIN_ID + 5000; END LOOP; END; /

这种写法的优势是范围扫描效率极高,缺点是要求主键分布均匀且连续,如果中间有大段空洞,会空跑很多轮。

写法三:基于 LIMIT 的子查询。达梦在兼容模式下支持 LIMIT 语法,但在 DELETE 里直接带 LIMIT 不一定被支持,稳妥的写法是套一层子查询:

DELETE FROM SALES.ORDER_LOG WHERE ID IN ( SELECT ID FROM SALES.ORDER_LOG WHERE CREATE_TIME < TO_DATE('2024-01-01','yyyy-mm-dd') LIMIT 5000 );

三种写法我都用过,结论是:表有稳定主键且分布连续用写法二最快;主键不规则或者表没主键用写法一最稳;写法三适合快速验证,长期跑批还是建议换成前两种。

3.3 参数与资源侧的配套调整

光改 SQL 不够,大事务删除还需要在资源侧做点准备。

UNDO 保留时间。达梦有一个控制 UNDO 数据保留时长的参数,默认值在多数场景下够用,但如果你的删除事务特别长,或者同时要保证闪回查询可用,就需要调大。这个参数一般是静态的,要在配置文件里改并重启,或者用系统过程按范围设置。具体数值怎么定:按照"最长单次事务时长 × 2"来估,如果单批事务最长 3 秒,那保留个几百秒绰绰有余。

临时表空间。排序、去重、某些执行计划路径会用到临时空间。批量删除前用SELECT * FROM V$TABLESPACE;之类的视图看一眼剩余空间,留出至少 30% 余量。

日志空间。大批量 DML 会产生大量 REDO,日志文件如果切换过于频繁会拖慢整体性能,必要时临时加大日志文件或者增加日志组数量。

会话级调优。在删除会话里适当调大排序区之类的内存参数,可以减少落盘。这个属于锦上添花,不是必做项。

3.4 一次清理任务的现场记录

去年做过一次日志表清理,场景是订单流水表按时间保留一年,历史数据转归档后物理删除。表有 1.2 亿行,需要清理的是一年以前的约 3800 万行。我先按 1.3 节做的三项体检:COUNT 确认行数 3800 万,外键检查发现有一个级联外键指向流水明细表,触发器检查发现有一个 AFTER DELETE 行级触发器往审计表里写记录。

基于这些信息,方案调整为:先删明细表再删主表,避免级联删除的隐式行为;触发器的写入改成批量插入;分批大小从 5000 降到 3000,因为触发器有额外开销。

实际执行时用了脚本方式,每批 3000 行,批间休眠 0.1 秒,整体跑了大约 50 分钟。中途观察到的几个数据:单批耗时稳定在 0.6 到 0.9 秒,锁等待峰值没有超过 200 毫秒,UNDO 使用率最高到 42%,业务侧的接口响应时间没有明显波动。这组数字后来被我当成了类似任务的参考基线。

批次大小单批耗时总耗时锁等待峰值UNDO 峰值
10000.25s约 95 分钟80ms18%
30000.75s约 50 分钟200ms42%
100003.2s约 34 分钟900ms78%

这张表是我在一台测试机上用小规模数据外推得到的趋势参考,不是绝对标准,但能看出取舍关系:批次越大总耗时越短,锁等待和 UNDO 压力越大。生产环境的选择永远是"够快且不打扰业务",而不是"最快"。

4. 误删之后:能救回来的几条路

删除操作最怕的就是"删完之后才发现删错了"。这一章讲的就是这个场景下的应对,以及更重要的——怎么让这个场景不发生。

4.1 回收站与闪回查询

达梦的回收站机制能救回被 DROP 的表。DROP TABLE 之后,表并不会立即物理消失,而是进入回收站,可以通过查询回收站视图看到被删的对象,用闪回语句把它恢复回来,或者用 PURGE 彻底清除。这套机制对 DROP 有效,对 DELETE 无效——DELETE 删的是行,不是对象。

对 DELETE 的补救主要靠闪回查询,也就是回到过去某个时间点去看表里的数据。语法形式是在查询里加时间点限定,比如SELECT * FROM 表名 AS OF TIMESTAMP TO_DATE('2024-05-01 10:00:00','yyyy-mm-dd hh24:mi:ss');。这个功能要把对应的开关参数打开,而且能回溯多远取决于 UNDO 数据的保留时长。

这里有个认知要纠正:闪回查询不是"无限回溯"。UNDO 空间被覆盖之后,老的时间点就查不回来了。所以真出了误删,第一反应应该是立刻停止对相关表的写入,越快越好,拖得越久能救回来的概率越低。

4.2 备份恢复的边界

闪回救不回来的时候,只能走备份。备份恢复有两个绕不开的问题:一是恢复粒度,物理备份通常只能恢复到某个时间点,整个库一起回,想单独捞一张表出来就得恢复到临时实例再导出来;二是时间成本,库越大恢复越慢。

所以我在做方案设计时会给核心表加一层保险:删除前先建影子表。

CREATE TABLE SALES.ORDER_LOG_BAK_20240501 AS SELECT * FROM SALES.ORDER_LOG WHERE CREATE_TIME < TO_DATE('2024-01-01','yyyy-mm-dd'); -- 核对行数一致后再执行删除 DELETE FROM SALES.ORDER_LOG WHERE CREATE_TIME < TO_DATE('2024-01-01','yyyy-mm-dd'); COMMIT;

影子表占空间,但删错了能立刻从影子表导回来,几分钟搞定。对于一周只跑一次、数据量又不算夸张的清理任务,这点空间代价完全可以接受。用完删掉影子表就行。

4.3 事前防御比事后抢救划算

真的误删过几次之后,我的做法就变成了往前提。具体是三件事。

权限收紧。生产环境的业务账号不给 DELETE 权限,只给 SELECT、INSERT、UPDATE。需要删除的场景走存储过程,存储过程里限定死条件,业务方只能传参不能改语句。开发人员需要手工删数据时,走工单申请临时授权,用完回收。

加一层"删除保护"。在关键表上建 BEFORE DELETE 触发器,把被删的行完整写进审计表。这样即便闪回失效、备份过期,审计表里至少还有一份记录。触发器会影响删除性能,所以只在核心表上用,日志类表不用。

把删除做成可回放的脚本。所有清理脚本都走版本管理,脚本开头必须包含 COUNT 校验和影子表备份两步,执行记录留档。这件事听起来繁琐,但真正遇到问题时,能省掉大量"到底删了什么"的争论。

5. 常见问题排查速查

这一章按现象归类,把我在实际工作中遇到频率最高的几类问题整理成速查表,配合排查动作。

5.1 删除卡住不动

现象是 DELETE 语句执行之后长时间不返回,会话状态显示为活动,但看不到任何进展。

可能原因排查动作处理方式
被其他会话的行锁阻塞查锁视图,看是否有其他会话持有目标行锁找到阻塞源会话,确认后终止或等其提交
执行计划走了全表扫描EXPLAIN 看计划补索引或改写 WHERE 条件
触发器内部有慢操作检查表上触发器的实现优化触发器或临时禁用
外键级联导致连锁删除检查外键定义改为先删子表再删主表
事务日志写入瓶颈看日志切换频率和 IO 等待加大日志文件,错峰执行

排查锁的第一步永远是把当前所有会话和锁的信息拉出来看一眼,通常会很快定位到阻塞源。达梦提供的锁相关视图能查到会话号、锁类型、锁对象、阻塞关系,这些信息足够了。

5.2 删得慢、回滚更慢

删除慢的原因前面讲过,主要是索引维护、触发器、约束检查和大事务。回滚比删除还慢,是因为回滚要按相反顺序重建每一行,还要维护索引,工作量只多不少。规避方式就是分批——分批之后单次回滚最多几千行,秒级完成。

还有一种隐蔽的慢:删除条件里的字段没索引,但表上有七八个索引,每删一行都要维护这七八个索引。这种情况下先看能否临时把非必要的索引置为不可用,删完再重建。索引重建的代价通常比边删边维护要低。不过在达梦里,索引能否临时禁用要看具体的索引类型和版本支持情况,动手前先在测试环境验证。

5.3 约束、触发器、权限类报错

报错类型典型提示根因与处理
外键约束冲突违反引用完整性约束子表还有引用行,先删子表或改外键策略
触发器执行异常触发器内部语句报错查看触发器体内具体语句,逐条排查
权限不足没有对象权限或系统权限授予目标表的 DELETE 权限
对象不存在表或视图不存在检查模式名,确认对象归属
唯一索引维护冲突索引键冲突检查是否有延迟约束或索引异常

这里要特别提醒一句:达梦的错误码在不同小版本之间偶有差异,报错信息的具体措辞也可能不同,但错误类型是稳定的。排查时不要死记错误码,抓住"这是约束类、权限类还是锁类"这个分类,效率更高。

6. 我在生产环境里踩出来的几条规矩

最后分享几条纯粹来自实践的习惯,跟语法没关系,但每一条都帮我挡过事故。

第一条,删除语句永远先跑 SELECT。把 WHERE 条件复制出来,SELECT COUNT(*)走一遍,再看一眼样本数据是不是你期望的那批。这五秒钟的检查,我做了快十年,救回来的数据加起来大概有几千万行。

第二条,不在命令行里手敲 DELETE。所有生产删除都写成脚本文件,脚本里包含行数校验、影子表备份、分批循环、提交控制四段。手敲的语句没有留痕,出问题连复盘都没法做。

第三条,提交之前多看一眼行数。SQL%ROWCOUNT或者客户端显示的影响行数,跟预期差一个数量级就立刻回滚。大事务还在内存里的时候回滚代价很小,提交之后再想救就要走闪回或者备份。

第四条,给每次清理任务留一个可追溯的记录。什么时间、谁执行、删了多少行、依据什么条件、备份放在哪,写进一个简单的日志表里。这件事在平时看起来多余,但在对账、审计、追责的时候,能省掉大量扯皮。

第五条,别在业务高峰做删除。哪怕你算出来只要三分钟,也不要。执行计划会变,锁会撞,参数会抖,任何一点意外在高峰期都会被放大。凌晨的维护窗口就是为这类操作存在的,用它。

第六条,删除方案的评审重点不在 SQL 本身,在影响面。一次评审,我通常要求提供三样东西:影响行数的估算、依赖对象清单、回滚方案。这三样齐了,SQL 写得糙一点问题也不大;缺一样,我就不同意上线。

达梦在删除这块的能力其实挺完整——行级锁、事务回滚、闪回查询、回收站、审计,该有的都有。真正难的从来不是数据库不支持什么,而是操作的人有没有把"删"当成一件需要设计的事,而不是一句随手敲出来的 SQL。把前面这些细节吃透,DELETE 就从一个危险动作变成了一个可控的常规操作。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询