044、创建透明表
今天聊透明表。写ABAP绕不开这玩意儿或者说绕不开SE11。上次帮同事排查一个生产机上的问题他写了个取数逻辑直接SELECT一张Z表结果一跑就炸报“Table ZXXX is not active”。我说那去SE11激活一下呗他去了激活的时候又报字段类型错误。折腾半天最后发现是建表时抄了一个域长度是10但字段需要12他直接套上去激活就卡了。这种破事在开发机上也常见问题不大但真要了亲命。所以把创建透明表这件事捋一遍。进SE11选“数据库表”填个名字比如ZPERSON点创建。这时候系统让你建一张表。你会看到几个区字段区、由表名体现的数据表结构、还有下面的基础信息。先填关键内容字段名、键标记、类型、长度、小数位。透明表必须有MANDT字段类型MANDT这其实是CLNT客户端这是系统的默认客户端字段没有它你的数据会跨客户端混在一起别省。举个例子做张人员信息表。结构大概是MANDT MANDT 客户端 ZID CHAR10 人员编号 ZNAME CHAR40 姓名 ZAGE NUMC3 年龄 ZDAT DATS 出生日期 ZSALARY CURR 工资 ZCURR WAERS 货币这里ZCURR是货币键ZSALARY必须配置参考字段。什么意思呢CURR类型是个数量维度你不能光写一个金额得告诉系统这个金额是哪种货币否则你在表里存了个100但不知道是人民币还是美元。在SE11的字段设置里选择“可选字段”或“货币/数量字段”属性给ZCURR指定为“参考字段”的类型。这是最容易被忽略的地方很多人创建CURR字段时不选参考字段激活时系统可能不报错但实际运行时金额会被当成分离的小数处理数值就错了。这里踩过坑我建过一个类似表工资存进去就变了样后来才明白是货币参考字段没配。字段类型有讲究。ZNAME用CHAR40一般够用。ZAGE用NUMC3数字文本就是系统里显示的年龄排序没问题。ZDAT是DATS系统内部是YYYYMMDD别搞成CHAR8。注意NUMC和CHAR的区别CHAR可存字母数字NUMC只能存数字但前端会有前导零比如001当文本用比较时小心。ZCURR是WAERS这是数据元素直接用里面带了货币代码的功能例如USD、CNY。字段设置好以后保存系统让你创建数据元素和域。域是最底层定义类型和长度数据元素定义语义比如标签。你可以直接创建局部的数据元素也可以复用已有的。但我的习惯是凡自建表字段都自己建数据元素不要图省事拉一个现成的过来套。原因以后你要给字段加搜索帮助、加值检查或者调整长度改自己的域不影响别人。别为了省五秒钟埋一年的雷。主键怎么设表的主键就是在字段区勾选“主键”那列。MANDT肯定要勾上然后是ZID业务主键。主键不能重复你一个人的编号是唯一的。如果你把ZNAME也勾成主键那同一个人同名但不同编号就不能存两条了这逻辑就有问题。所以主键一定是业务上唯一的。另外主键字段不能为空系统会强制。主键也影响按表创建索引的效率别乱加。然后就是“技术设置”这个页签。这里挺坑的数据类你选APPL0、APPL1、USER1这些。差别在于数据归档和存储。一般自建配置表选APPL1或USER1。数据类选错了不报错但影响后续规划。大小类别也选好按你的数据量来别选太大浪费空间选太小了表变大后会有性能问题。另外“允许覆盖”这个勾慎选。如果你选了“允许覆盖”意味着透明表的字段可以在程序中使用但前提是逻辑没问题。实际上这个选项是给“定制化”表用的系统表一般不能覆盖但自建表嘛看着办。还有“缓冲属性”如果你表的数据不频繁变动可以设缓冲用“单记录缓冲”或“全缓冲”这样SELECT时不用总打数据库性能好很多。但要注意如果其他地方用UPDATE直接改数据库缓冲的缓存不会自动更新会读到旧值。这里经常出问题所以我个人的做法是自建的主数据表默认不缓冲除非确认很少变。激活是个关键节点。你点激活前系统会做一堆检查。最常见的报错是“字段长度不一致”比如你在域里定义了CHAR10但字段设置里用了CHAR12。要么改域长度要么改字段长度。还有“内容不够”之类就是长字段被截断。激活之前先把语法检查做了。另外激活会生成数据库表。但注意如果你建的是“table with structured types”别搞那种对于新手透明表老老实实按基础字段来用类型、用数据元素别想着在表里搞嵌套结构那样后续查询麻烦。写完表之后在程序里怎么用你可以在ABAP里直接SELECT比如SELECT SINGLE * FROM ZPERSON INTO DATA(ls_person) WHERE mandt sy-mandt AND zid A001. IF sy-subrc 0. WRITE: ls_person-zname, ls_person-age. ENDIF.注意WHERE里必须带mandt或者依赖系统自动加客户端其实SELECT时系统会自动用当前客户端但如果你用了FOR ALL ENTRIES最好显式指定不然可能出问题。这里有个坑如果你不是直接用SELECT *而是SELECT zname zage那么赋值时要用列表对应。内联声明是个好东西但别在LOOP里用性能差。透明表用起来最大的好处是持久化。数据存到数据库里关了系统还在。但你也要知道透明表对于性能和锁是有代价的。可以写更新操作直接用UPDATE/INSERT/Modify。注意修改表数据时必须注意逻辑锁不然并发更新会毁数据。写代码时其实很多人喜欢用结构体比如定义TYPES: BEGIN OF ty_person, zid TYPE char10, … END OF ty_person. 然后DATA: ls_person TYPE ty_person. 但这样的结构体跟透明表没关系。如果要从表里取数据最好直接用表类型或者显示声明“RANGES”等。但咱们这个章节是建表所以不多展开。再提一个真实调试问题有人建了透明表在SE11里也维护了数据但程序里却查不到。原因要么是程序用的逻辑数据库不对要么是客户端不对。你SE11里的数据是当前客户端但程序运行的客户端可能不同。你可以在事务代码中敲“/NSE38”直接看表数据也可以用SB反正查一下当前sy-mandt。很多“表没激活”“查不到数据”这种低级问题都是客户端搞错了。说了这么多最后给点个人建议。建表前一定画个草稿把业务主键和业务字段分开。不要为了省事把所有字段都设成可空因为透明表空值在查询时索引利用很差除非业务上真的可选。不要一个表塞几十个字段如果字段超过五十想想能不能拆开。另外交付类选“A”应用程序表还是“C”自定义表都行但项目有规范就按规范来。要别忘了给表加描述要在“短文本”里写清楚不然过几个月你自己看着都费劲。还有自建表一定要放到传输请求里不然别的系统没法同步。我见过有人建完表直接忘了请求导致预生产环境跑业务表不存在整个程序崩了。这种低级错误咱不要犯。建表是个细致活但别被吓到。你多建几次就知道哪些坑要避开。比如CURR货币参考字段、MANDT主键、域长度一致性这三个点你自己踩过去写代码时会格外小心。别人的代码你一眼就能看出问题在哪。这就是老手和菜鸟的区别。行了就聊到这该去建表了。