046、外键与检查约束 那阵子半夜改表结构被一个诡异的脏数据折腾得够呛。SE16N里明明看着一条订单记录关联的客户主数据却已经被物理删除了。系统没报任何错误报表里左连接出来客户名称是空业务部门一口咬定程序有bug。后来翻数据库底表才发现当初建表时偷懒没设外键导致这种“孤儿数据”堂而皇之地躺在生产环境里。从那以后但凡涉及关键业务表我对外键和检查约束的执念就到了近乎强迫症的程度。这期就聊聊这两个容易轻视却救命的家伙。ABAP里所谓外键不是数据库层面那个硬邦邦的constraint而是数据字典里的一个逻辑关联定义。它告诉系统这张表的某个字段值必须存在于另一张表的某个字段里。比如EKPO的EBELN要对应EKBE的BELNR或者更常见的是MARA的MATNR关联MAKT的MATNR。定义外键不是为了让数据库帮你强制完整性而是让ABAP环境里各种工具能自动生成check逻辑并且让搜索帮助自动带出关联字段。这里有个关键点外键关联的两张表关联字段的域domain要一致才行。比如我用的是CHAR10你也得是CHAR10换了个CHAR12系统直接报错别指望它能自动转换。创建外键的路径不复杂SE11打开你要建的表维护界面切到“字段”页签双击某个字段弹窗里有个“外键”输入框填上对应的表名和字段保存激活。但要注意外键关联的字段最好在目标表里是主键或者有唯一索引。如果你随便拿一个非唯一字段去关联激活时虽然不报错但运行时系统会警告“外键定义不完整”搜索帮助还会出现重复值那时候再回头补索引就折腾了。检查约束check constraint就更有意思了。它不是为了跨表而是给单表字段加业务规则。比如状态字段只能取’01’,‘02’,‘03’数量字段不能为负。在SE11里进入“检查约束”页签新建一个约束名称建议跟表名或用途挂钩比如ZORD_STAT_VALID。约束条件写的是ABAP语法但逻辑简单STATUS 01 OR STATUS 02 OR STATUS 03。这里踩过一次坑约束里引用字段时不要加表名前缀直接写字段名就行。我曾经按数据库习惯写成ZORDER~STATUS结果激活时解释器不认愣是折腾半天。激活检查约束后系统是不是就自动拦截非法数据了答案会让你失望。在ABAP里检查约束只在特定时机生效——比如通过SE16N直接维护、联机事务中的某些屏幕逻辑以及激活时对已有数据执行验证。但如果你在ABAP代码里用UPDATE dbtab SET...或者INSERT dbtab系统并不会默认强制校验。程序员写代码一时爽垃圾数据照样能塞进去。所以真正的兜底还得靠程序里显式调用CHECK语句或者干脆在数据库层建硬约束。不过很多公司DBA不允许底库加约束怕影响性能那检查约束就当是文档级别的规范和预检工具。说到预检这里有个实用小技巧。当你定义完外键或检查约束激活时系统会有一个“激活时检查数据”的选项。如果表里已有数据一定勾选它。否则旧数据里若有违反约束的值激活照样成功但后续其他操作会莫名报错排查方向都找不到。我遇到过激活时没勾结果生产机所有插入操作都被拒错误信息还指向主键冲突绕了一大圈才发现是约束没通过。再回到开头的那个脏数据案例。如果当初EKPO和KNA1之间设了外键维护表数据时系统就会弹警告或者拒绝保存。但注意外键的作用强度还取决于你在外键页签里选的“外部键定义”类型——是普通外键还是空值允许。如果选择允许空值那关联字段为空的时候系统不检查这种设计适合非必填的关系。但别把所有关联都设成允许空值否则等于没设。代码层面你可以用SAP_DB_INSERT或者MODIFY往带外键的表中写数据系统会在后端检查外键吗答案是取决于调用方式。如果你通过逻辑数据库或SELECT-OPTIONS操作ABAP运行时会有部分检查但直接用Native SQL绕过字典层那外键就是摆设。所以我在团队里立了个规矩任何涉及带外键表的写操作一律使用Open SQL并且预先调用CALL FUNCTION BAPI_...之类封装好的事务绝不让新人用UPDATE裸奔。别问为什么线上事故见得多。检查约束还有个冷门用途当你想给一个表加字段级权限时可以写个判断当前用户组的约束不行约束里不能引用SYSUID那种运行时变量只能操作字段本身的静态值。所以别太天真检查约束就是限制字段取值范围的别指望它能做动态逻辑。另外外键关联其实有个“隐形福利”。如果你在SE11里维护了外键那么在SE16N维护数据时双击关联字段会自动弹出目标表的参考值列表。这个对于人工核对数据非常友好。我经常跟新同事说外键不光是技术约束更是一张内嵌的数据字典文档。新人看表结构时沿着外键跳转马上就能理清E-R关系比看那些冗长的设计文档有效得多。最后给点压箱底的经验第一外键字段的域名一致只是底线实际建议连数据元素都用同一个。如果两张表的关联字段用了不同数据元素但相同域名激活能成功但搜索帮助的值列表可能不自动带出语义后期维护时很别扭。第二检查约束的写法最好全大写下划线别用驼峰。因为ABAP的SQL解析对大小写敏感度在不同版本有诡异行为全大写最稳妥。第三改表时如果涉及已存在的约束系统会提示你“检查约束将失效”这时候别头铁直接激活先导出数据清理干净再改。血泪教训有次我为了加一个范围约束强行激活结果全表历史数据被要求必须验证几百万行直接STOP调度任务全卡死。外键和检查约束说到底不是枷锁而是帮你把业务规则从程序代码里抽出来沉淀到数据字典里。这样无论多少年过去换了几波程序员表结构自己会说话。做技术的有时候就得相信规则越早固化灾难越晚降临。