尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

061、数据库操作 INSERT、UPDATE、MODIFY、DELETE

061、数据库操作 INSERT、UPDATE、MODIFY、DELETE 061、数据库操作 INSERT、UPDATE、MODIFY、DELETE昨晚值班被电话吵醒说是某个夜批程序跑完财务对账发现总金额差了几分钱。我登进系统一看日志里清清楚楚写着一条UPDATE把汇率字段写成了空值因为上游接口传了个空结构程序没判断就直接拿去更新了。这种问题不常见但一出就是大事故。今天把 ABAP 里四个最基础的数据库操作语句掰开揉碎讲一遍很多坑就藏在这些看似简单的语法里。先说INSERT往表里插新记录。你可能会说这有什么好讲的INSERT INTO ztable FROM wa.完事。但注意如果表里主键已经存在这条语句会直接报错而且sy-subrc返回 4关键是当前这条记录根本没插进去。我见过不少新同事写循环插入时不检查sy-subrc结果数据缺了一半程序还显示“执行成功”因为循环里异常被吞掉了。正确姿势是先查一下主键或者用INSERT ... ACCEPTING DUPLICATE KEYS这个附加项允许重复键不报错sy-subrc返回 4 表示有重复0 表示插入成功。看代码INSERT INTO zorder_login FROM wa_order. IF sy-subrc 0. COMMIT WORK. ELSE. ROLLBACK WORK. 别假装成功该记日志记日志 ENDIF.这里踩过一个坑INSERT不像UPDATE那样会覆盖字段重复键就整个拒绝。有时候你想“没有就插有就改”别自己写SELECTINSERT直接用MODIFY。MODIFY是 ABAP 里最“霸道”的语句它不跟你商量主键不在就插在就更新。单条操作MODIFY zorder_login FROM wa. IF sy-subrc 0. 这里其实一般不会失败但万一碰上数据库锁呢 ENDIF.表内操作更酸爽MODIFY ztab FROM TABLE gt_data.一条语句搞定批量内部循环和数据库操作分开处理性能好很多。但要注意MODIFY是整行覆盖如果你只想改一个字段其他字段没赋值那其他字段就被清空了。我当年写过一次用MOVE-CORRESPONDING从内表填充工作区结果某几个字段因为源字段名为空目标表对应字段全变初始值害得我加班到凌晨两点查数据。所以用MODIFY前务必把工作区里所有非目标字段从原表读出来填好。UPDATE就温柔多了它只更新你指定字段。两种用法UPDATE ztable SET f1 v1 f2 v2 WHERE key ...或者UPDATE ztable FROM wa.但后者是更新主键匹配的那条记录工作区里非主键字段全都会写进去没赋值的照样被你清空。所以推荐用SET方式明确只改哪些列UPDATE zorder_login SET login_time sy-uzeit login_date sy-datum WHERE order_id lv_oid AND user_id lv_uid. IF sy-subrc 0. COMMIT WORK. ENDIF.这里的陷阱是如果WHERE条件查不到记录sy-subrc返回 4但不会报错。你以为更新成功了其实没动。很多程序把UPDATE的返回值写 0就报错那如果是因为没找到记录你会得到一条让人摸不着头脑的“更新失败”。最好先判断一下是不是“无此记录”再决定是报错还是转成INSERT。DELETE稍微简单点但“删”这个动作在业务数据上一定要留后路。一般不建议物理删除用逻辑删除加一个删除标记字段更稳妥。但ABAP里物理删除免不了要用比如临时表、日志表清理DELETE FROM zlog_tmp WHERE created_at sy-datum - 30. IF sy-subrc 0. COMMIT WORK. ENDIF.注意DELETE删除多条时sy-dbcnt会返回实际删除的行数。如果你想确认删了多少条用它别用sy-subrc因为sy-subrc只告诉你有没有成功不告诉你删了几行。对还有个大坑当你要删除内表所有数据然后重新插入时千万别用DELETE FROM ztable不带条件那等于 DROP TABLE 的数据文件虽然保留了结构但日志文件会瞬间膨胀碰上大数据量直接锁表。再说说事务控制。这四个语句都涉及数据库更新而 ABAP 的更新机制是隐式的你执行INSERT/UPDATE/DELETE时数据并没有真正提交直到执行COMMIT WORK才会真正生效。如果你什么都不写程序结束时会自动回滚。很多初学者以为MODIFY执行完数据就变了然后在同一个程序里再查询结果查不到——因为事务还没提交除非你在同一个逻辑单元里用SELECT是能看到更新缓冲区里的数据的。这里有个小知识点ABAP 的数据库更新是先写到UPDATE CURRENCY之类的事务数据表COMMIT才推送到底层。所以切记涉及关键数据修改COMMIT和ROLLBACK必须显式控制别指望程序自己处理。还有一个容易被忽略的附加项UPDATE ... SET ... WHERE ...可以配合UP TO 1 ROWS之类的选项吗不行那是SELECT的。但UPDATE可以用RETURNING获取旧值抱歉ABAP 没有RETURNING语法。想保留旧值做审计只能先SELECT读一遍再更新。这种“读改写”操作要小心并发——两个会话同时读到相同旧值各自更新后提交的覆盖先提交的。解决方式是用 ECP扩展的经典锁或者直接在UPDATE条件里带上版本号或旧值条件UPDATE zstock SET quantity lv_new_qty WHERE material lv_mat AND quantity lv_old_qty. CAS 思想乐观锁 IF sy-subrc 0. COMMIT WORK. ELSE. 说明别人改了提示用户重试 ENDIF.这方法很土但管用。再聊一下MODIFY和UPDATE的选择。如果你不确定记录在不在且不关心原有数据是否被覆盖用MODIFY最省事。如果你只想改部分字段且保证记录必须存在用UPDATE SET。如果记录不存在也允许插入那就先SELECT判断存在就UPDATE SET不存在就INSERT但这样三步操作不保证原子性——中间崩了就可能出现“既没更新也没插入”的情况。更优方案是用MODIFY然后专门只读那些不需要更新的字段不行MODIFY整行覆盖。所以这时候有人会用UPDATE不存在就INSERT但UPDATE的sy-subrc4 又会误判。我的习惯是业务上能接受整行覆盖就用MODIFY不能接受就写UPDATE并额外判断sy-subrc 4时转INSERT这种写法的性能损耗可以接受但一定要把SET字段列全。调试真实问题的场景有时候你看程序逻辑没问题数据就是没写进去。这时候除了检查sy-subrc还要盯COMMIT放在哪里。ABAP 里如果在循环中频繁COMMIT性能极差且容易造成数据库锁等待。正确做法是攒一批数据MODIFY一次COMMIT一次sy-dbcnt检查条数。另外COMMIT之后sy-subrc会被重置吗不会但sy-dbcnt在SELECT语句里会返回查询行数别搞混。还有一个 SCN 上老鸟常提的坑UPDATE语句不能直接更新视图必须用基础表。如果你对连接视图执行UPDATE系统会报“视图不可更新”。还有的数据库比如 HANA对INSERT空值很敏感如果工作区字段初始值没填插入后查出来却是NULL你拿IS INITIAL判断却判不出来——因为 ABAP 的初始值就是空字符串但数据库的NULL不是空字符串。这个问题在UPDATE SET里特别隐蔽你把字段设为spaces如果表字段有NOT NULL约束会直接抛出数据库错误虽然sy-subrc可能返回非0但错误堆栈会被丢进SLOG。所以写代码时宁可多写几行也要把工作区的每个字段用CLEAR或MOVE显式赋值。有的老系统里还有MOVE-CORRESPONDING只移动相同字段名的坑字段名大小写不敏感但SQL语句里WHERE条件如果用了不适当的类型转换可能导致索引失效。比如WHERE zdate lv_date而lv_date是STRING类型数据库可能隐式转换全表扫描。这类性能问题在数据量小的时候看不出来上线后跑大批量就原形毕露。最后一个务实的建议写数据库操作语句时顺手把sy-subrc的接收变量声明成一个局部常量比如CONSTANTS: c_success TYPE i VALUE 0.然后判断IF sy-subrc c_success.这样比死记数字强代码可读性也好。但我不建议为了“干净”把所有操作都塞进一个通用子例程搞一个万能DB_OPERATION函数参数传表名、操作类型、工作区。这种封装看起来省代码实际调试时你根本不知道具体操作了哪张表哪个字段报错信息也含糊。数据库操作就该写清楚每一条UPDATE或DELETE都让人一眼看懂。这比任何设计模式都重要。今天先写到这里。如果你也遇到过MODIFY把字段搞丢的情况或者有更好的并发处理手段欢迎留言。下次接着讲SELECT的高级用法和性能调优那又是一个大坑。
返回列表