相信大家一定用过这样的语法:
代码语言:javascript复制DELETE TABLE itab(某内表) FROM wa(某工作区)
语法很简单,就是以工作区的内容,去删掉该内表中的这一行,但是这句代码又很不简单。
测试提交了一个BUG,大概问题是:有两条记录,需要根据条件删掉其中的一条,但是最终程序还是用了本该删掉的这一条数据去做后续处理。
由于不能重现问题,只能硬看代码,可是代码就那么几行,于是看了一会,通过语法检查的警告消息,我把bug定位到了这句代码上,并进行了一系列测试,最终发现了问题所在。
代码语言:javascript复制DELETE TABLE lt_data FROM <ls_data>.
代码语言:javascript复制<ls_data> 即是要删掉的数据,该数据在lt_data中是存在的,<ls_data>本就是lt_data表loop出来的,不会存在数据不对应的问题。
首先解析该语法,通过F1 DOCU可以查到如下语句:
If the USING KEY addition is not specified, the primary table key is used. If the USING KEY addition is specified, the table key specified in keyname is used.
翻译一下就是,如果后面没有指定using key,那么primary table key会被使用,如果指定了,则使用特定的key字段。
看起来也没什么问题,接着又有这么一句话:
If the primary table key is used to access a standard table and the key is empty, the first line of the internal table is deleted. If this is known statically, the syntax check produces a warning.
如果使用了primary table key这个东西,但是key又是空的,那么内表的第一条会被删掉,同时给你抛一个警告消息。
消息如下:
"LT_DATA" is a table with an empty primary key. Check the semantics of the statement.
由于大家都习惯不怎么看警告,只关系报错,于是这条消息被忽略了。
所以问题最终就是:
如果一个内表有两条数据,需要删掉其中的第二条,但是同时该内表没有key值,那么如果使用该语法,from后面跟第二条的工作区的话,第一条数据会被删掉。
那么问题又来了,这个key,是个什么东西呢?我们定义内表的时候,明明都没有定义过key值,为什么从来没有出过问题呢?现在为什么这个内表又没有key值呢?
关于第一个问题:如果内表没有明确定义key值,则该内表的所有char-like的字符,会被默认作为key值,如果明确定义空key,则默认没有key值。
代码语言:javascript复制DATA: lt_def_key TYPE TABLE OF ty_nokey. " default key set for all char-like fields
DATA: lt_def_key TYPE TABLE OF ty_nokey. " no key
而在当前bug中,属于第三种情况,出自ABAP7.5的新语法,内嵌声明,代码如下:
代码语言:javascript复制SELECT * FROM (table/view) WHER (条件) INTO TABLE @DATA(lt_data).
该代码表面上来说没有任何问题,但是在abap的key documentation的不知名角落里有这么一段话:
An empty primary table key can be created as follows:
- Explicitly, with the addition EMPTY KEY of the statements TYPES, DATA, and so on.
- Explicitly, using an inline declaration INTO TABLE @DATA(itab) in the statement SELECT
- Implicitly, when using the standard key if a structured line type does not contain any non-numeric elementary components or if an unstructured line type is table-like.
注意看第二条,如果用内嵌声明在SQL中定义的内表,是带有empty primary table key的。
这一点在绝大多数教程或者技术博客中都没有明确提到,在key documentation的内嵌声明页面也没有重点标注出来。
即使select后面跟的表或者视图明确定义了key值,在内嵌声明中也不会有,于是,用这种方式声明的内表,决不能用delete from 这种语法去删除,这样只能得到一个错误的结果。
而正如前文所述,即使这是错误的,编译器也仅仅会给出一个可有可无的warning消息。
那这个问题该怎么解决呢?首先删除内表可以使用index或者where 足够精确的条件,其次,建议提高对warning的重视程度,不要以为没有error就万事大吉,要多重视ATC的检查消息,从而规避各种可能的风险。