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

资讯详情

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

043、表类型与表定义

043、表类型与表定义 043、表类型与表定义昨天半夜被一个实习生拉去排障程序逻辑看起来完全没问题数据也没问题就是把一条记录INSERT到内表的时候运行时直接dump了。报错是ITAB_ILLEGAL_SORT_ORDER。我一看这哥们用的内表是SORTED类型往里面塞数据的时候并没有按关键字顺序放入。他一脸无辜“我循环里加了排序啊怎么可能没排序”我让他把定义贴出来他写的是DATA: lt_itab TYPE SORTED TABLE OF mara WITH NON-UNIQUE KEY matnr.这定义看着没问题吧问题出在他循环里用INSERT往这个表里插数据时数据来自两个不同的源合并后乱了序。SORTED表要求你插入时键值已经有序或者你直接APPEND但APPEND在SORTED表上很容易出问题因为你没法保证后加的键值比之前的大。实际上SORTED表内部用的是二叉搜索树你插入一个比根节点小的数据树会自动调整但ABAP运行时为了性能做了个假设你要是用APPEND那必须保证键值单调递增。如果违反了这个假设它不一定报错但如果你用了INSERT它会严格检查键的顺序一旦发现前一条比后一条大直接抛异常。这事让我想起另一个坑HASHED表。很多人以为HASHED表就是哈希索引访问快就什么都用它。但HASHED表最怕你用LOOP加SORT。它本身是无序的你没法按索引访问更不能APPEND因为哈希表根本不允许APPEND只能用INSERT。你要是想对它排序对不起你不能对HASHED表直接SORT必须先COPY到STANDARD表里再排序。这不是性能问题这是语义上不允许。所以回到“表类型”这四个字在ABAP里其实有两层意思。一层是数据字典里的“表类型”Table Type它是描述内表结构的类型对象属于DDIC里和域、数据元素、结构体并列的元数据类型。另一层是我们日常挂在嘴边的“内表类型”就是STANDARD、SORTED、HASHED这三种。而这三种类型在数据字典里又必须通过一个Table Type来定义不然你没法在ABAP程序里直接引用一个字典表类型作为内表类型。这两者千万别搞混。我见过不少从其他语言转过来的朋友一开始被“表类型”这个名字唬住以为是数据库表的结构定义。其实数据库表的定义在ABAP里叫“透明表”Transparent Table它对应物理上的数据库表而Table Type是纯逻辑层的内表模板。你在SE11里创建的是Table Type给别人用的时候别人直接用TYPE table_type_name就能得到一个内表。透明表则要用TYPE STANDARD TABLE OF dbtab之类的方式去引用。这是两个完全不同的东西甚至可以说一个管内存一个管存储。现在展开说下三种内表类型。STANDARD表是最普通的它按插入顺序排列可以用索引访问也可以键访问。它的查找是线性扫描数据量大时会慢但ABAP也会用二分法查找——前提是你先SORT并且用READ TABLE WITH KEY或者SORTED BY。注意如果你没排序就去用BINARY SEARCH那你得到的结果是未定义的可能在调试时看起来对但上线后偶发错乱。这不算踩坑这算自爆。SORTED表一旦声明了关键字你插入的数据就得符合排序规则。它的好处是读取非常快READ TABLE直接用键值二分查找不需要先SORT。但它的写入代价高而且容易触发上面说的dump。所以在SAP项目中SORTED表通常只用来存配置数据、主数据这些静态内容。动态拼接的临时数据千万别用SORTED。HASHED表则是为等值查询优化的键唯一性要求高内部采用哈希码映射。它没有索引不能做LOOP操作也不能SORT。如果你需要大表快速取单条记录并且键唯一用HASHED很合适。但如果你要把多个表关联或者做嵌套循环HASHED反而会因为不支持索引扫描而变慢但它的READ TABLE速度是O(1)和SORTED表的O(log n)有本质区别。实际开发中我用STANDARD表占了九成。为什么因为ABAP内表操作太灵活了APPEND、INSERT、DELETE、MODIFY、SORT、LOOP全部都能用。SORTED和HASHED只是特定场景的优化。不要为了炫技而用高级表除非你明确知道性能瓶颈在哪。曾经有个项目某个接口需要批量读取物料主数据用了STANDARD表然后READ TABLE WITH KEY在数据量上万以后明显卡顿。我把那个内表改成SORTED表并且确保数据插入时按MATNR排序后接口耗时从5秒降到0.3秒。这才是用SORTED表的正确姿势读多写少插入有序。再讲一下数据字典里的Table Type定义。SE11创建Table Type时有几个关键字段行类型Row Type、访问方式Access Type、键定义Key Definition。访问方式就是STANDARD、SORTED、HASHED还有RANGE特殊类型用于表示区间。键定义可以指定非唯一或唯一。你把这个Table Type建好后在程序里DATA: lt_itab TYPE zts_mytype.就直接得到一个内表。这个内表的类型和属性完全由字典控制好处是同一个表类型可以被多个程序复用改一处全生效。而且该类型还能作为函数方法接口的参数类型跨程序传递内表时不用反复定义结构。我们项目里有个公共的ZTT_AMC_MSG就专门用来传消息列表所有接口都引用它测试和排错都能统一处理。但有个细节容易坑人如果你在SE11里定义了一个SORTED表类型但它允许非唯一的键那么在ABAP里使用它时如果插入相同键的数据会排在后面符合预期。但如果你定义成唯一键却试图插入相同键直接运行时错误。这比SORTED表的排序错误更隐蔽因为很多人以为非唯一可以重复唯一不能重复但在SORTED表下唯一键是依靠键值比较来保证的。敲代码时别只想着业务逻辑务必先看表类型的键定义是UNIQUE还是NON-UNIQUE。还有一种“表定义”聊法是指创建透明表。透明表在SE11中定义它包含字段、数据类型、长度、是否允许空值以及搜索帮助、外键等。这个定义和Table Type不同它最终会在数据库中生成一个物理存储表。ABAP程序里访问透明表一般用OPEN SQL比如SELECT * FROM ztable INTO TABLE gt_data。透明表本身的定义直接决定了你的数据存储结构所以字段类型选择上要谨慎。这里有个常见坑数据库表字段类型如果是QUAN或CURR需要同时定义参考表域中的单位字段否则激活时可能报错或者激活后数据精度有问题。定义表时还要注意不要随便用CHAR类型做日期字段要用DATS否则排序大小和范围都可能出错。这些都是老生常谈但每次项目里总有人犯。还有如果你在透明表定义时加了某个字段做索引查询时能用到但如果建了太多索引写入就会变慢。索引不是越多越好。我的经验是一个表最多三个索引优先放在外键和常用过滤条件上。别为一个偶尔跑的报表建索引那只会拖累日常业务。写到这里回到我那个实习生的问题。他最后怎么解决的我让他把内表类型从SORTED改回STANDARD然后如果需要按物料号快速读取就用SORT BY matnr再READ TABLE WITH KEY matnr ... BINARY SEARCH。这样既不会dump性能也够用。但他不甘心问为什么不用SORTED表然后每次插入前排序我说你插入前排序意味着你每来一条数据都要对现有表排序那比维护一棵二叉搜索树还慢而且还要处理数据来源顺序的不确定性。何必呢内表类型选择是权衡不是炫技。最后说个实用的小建议。如果你在一个循环里往一个STANDARD表里插入数据并且之后要按某个字段频繁读取别急着APPEND你可以先APPEND等循环结束后再SORT然后READ时加BINARY SEARCH。这是最经典、最不容易出错的组合。如果你确定数据本身有序也想省那一次排序那就直接声明SORTED表但插入时一定要保证顺序否则dump了别怪我没提醒。如果你要的是按主键查一条并且主键唯一那HASHED表是不错的选择但你要不要再想着对它做SORT或LOOP动态修改了——那不是它该干的事。表类型和表定义看起来只是ABAP里几个名词但用不好就会在半夜收到runtime error短信。你是想半夜被叫醒还是想安稳睡个觉先从这几种类型的底层逻辑开始理解吧。
返回列表