上个月接了个改报表的需求客户希望在配置表里维护展示列今天要A列明天加B列程序不能动不动就改代码。这类需求做ABAP的人应该都遇到过从前台Excel导入、通用接口解析到列配置化的ALV报表只要字段集合不能在开发期敲死就一定会碰到同一个东西动态内表Dynamic Internal Table。所谓动态内表是指行结构在运行时才确定的内表。创建它通常有两条路第一种是“借”程序里或数据字典里已经有现成的结构类型运行时把它拿来直接实例化成内表第二种是“造”用RTTI的类组件从零开始拼出一个结构再套上表类型运行时生成一个全新的内表。这篇文章我会把两种方式从原理、代码到选型全部拆开讲适合已经会用内表、想在动态化场景里少走弯路的ABAP开发新手也能顺着代码一步步跑通。1. 动态内表的本质为什么列可以“晚一点”再定1.1 静态内表的“编译期定型”以及它解决不了的事平时我们写的DATA gt_mara TYPE TABLE OF mara.行结构在程序编译时就固定成了MARA的字段集合。ABAP编译器会为这个内表准备好精确的内存布局之后想访问哪个字段就访问哪个字段IDE还能自动补全代码写起来非常舒服。这是静态内表的好处但也正是它的天花板一旦程序编译完成行结构就再也不能变了。这种“写死结构”的模式在实际项目里经常碰壁。我遇到过太多这样的场景客户在配置表里维护报表的展示列今天要5列下周改成7列列名都是运行时才知道的从前台Excel导入数据表头行决定内表结构每个模板的字段都不一样做一个通用取数工具传入表名程序自动读取该表所有字段在多个系统接口之间做数据搬运目标字段集由配置或文件内容决定。你当然可以建一张宽表把所有可能用到的字段都塞进去用的时候只取一部分。但这样做的后果是字段越堆越多、代码里全是硬编码判断、别人接手根本不知道哪些字段真正在使用。真正干净的做法就是让列在运行时再定这就是动态内表存在的意义。1.2 动态内表的两条技术路径借类型与造类型动态内表的核心机制说起来很简单不再用DATA直接声明一个具体的表而是用数据引用REF TO DATA指向一块运行时才创建的对象再用字段符号去访问它。但这里的关键问题是运行时创建数据对象也必须先有类型信息计算机才知道这块内存怎么划分。类型信息从哪里来这就是两种方式的分水岭。第一种“借”。类型在编译期已经存在于程序里的TYPES或者存在于数据字典DDIC里。运行时要做的只是把这个现成的类型描述取出来然后实例化内表对象。你可以把它理解为地皮已经划好了线施工队照着线直接干活就行。第二种“造”。类型在运行时才被组装出来通过RTTI的类一个字段一个字段地定义字段名叫什么、是什么类型、多长、有多少位小数拼成组件表再生成结构描述、表描述最后才创建数据对象。你可以理解为先现画图纸再照着图纸施工。这两条路径在实际项目里并不是完全孤立的经常是“借一部分、造一部分”。但判断一个需求到底算哪种思路就看列的来源列已经提前定义好了是“借”列要由配置、文件头或用户输入现场决定那就是“造”。2. 方式一基于已有类型创建动态内表——“借”一种熟悉的类型2.1 最直接的写法CREATE DATA TYPE TABLE OF先看一段最常用的代码在程序里定义一个结构类型然后动态创建一张以它为行结构的内表 1. 在程序里定义一个结构类型 TYPES: BEGIN OF ty_person, name TYPE char20, age TYPE i, entry_date TYPE d, salary TYPE p LENGTH 8 DECIMALS 2, END OF ty_person. DATA: lr_table TYPE REF TO data, lr_line TYPE REF TO data. FIELD-SYMBOLS: ftable TYPE STANDARD TABLE, fline TYPE ty_person. 2. 用已有类型动态创建内表 CREATE DATA lr_table TYPE TABLE OF ty_person. ASSIGN lr_table-* TO ftable. 3. 创建行对象 CREATE DATA lr_line TYPE ty_person. ASSIGN lr_line-* TO fline. 4. 向动态内表里塞数据 fline-name 张三. fline-age 30. fline-entry_date 20240101. fline-salary 15000.00. APPEND fline TO ftable. 5. 读取 LOOP AT ftable INTO fline. WRITE: / fline-name, fline-age. ENDLOOP.这里有两个关键动作缺一不可CREATE DATA负责在内存堆里分配一块数据对象ASSIGN ... TO 字段符号负责把这块内存挂到一个可以访问的“门牌号”上。lr_table只是保存了数据引用的地址真正操作内表时必须用字段符号ftable。注意第三步创建行对象时用的是TYPE ty_person不是TYPE TABLE OF ty_person。这一步容易搞混CREATE DATA后面跟什么类型创建出来的就是什么对象跟表类型出来一张内表跟结构类型出来一个结构。后面往内表里APPEND时用的就是这个行对象。2.2 从DDIC结构动态取类型DESCRIBE_BY_NAME 与 TYPE HANDLE上面代码里TYPE TABLE OF ty_person中的ty_person是词法上已知的所以编译期就能确定。但很多场景下结构名本身是运行时才知道的比如用户在配置表里填了 MARA程序要按 MARA 的结构动态建一张内表。这时就不能在CREATE DATA后面直接写类型名了而是要借助cl_abap_typedescrdescribe_by_name拿到类型描述器再用TYPE HANDLE创建数据对象。FORM create_itab_by_structure USING iv_strucname TYPE ddobjname CHANGING cr_table TYPE REF TO data. DATA: lo_type TYPE REF TO cl_abap_typedescr, lo_table TYPE REF TO cl_abap_tabledescr, lo_struct TYPE REF TO cl_abap_structdescr. 1. 根据结构名拿到类型描述 lo_type cl_abap_typedescrdescribe_by_name( iv_strucname ). 2. 把类型描述转成结构描述再套一层表描述 lo_struct ? lo_type. lo_table cl_abap_tabledescrcreate( lo_struct ). 3. 用表描述创建动态内表 CREATE DATA cr_table TYPE HANDLE lo_table. ENDFORM.这里用到了TYPE HANDLE它的作用是把运行时才获得的类型描述对象传给CREATE DATA让系统按照这个描述去分配内存。可以说TYPE HANDLE就是静态类型名和运行时类型之间的桥梁。如果你用的是ABAP 7.40以上版本还可以用内联声明和CAST写得简洁一点DATA(lo_type) cl_abap_typedescrdescribe_by_name( iv_strucname ). DATA(lo_table) cl_abap_tabledescrcreate( CAST cl_abap_structdescr( lo_type ) ). CREATE DATA cr_table TYPE HANDLE lo_table.提示DESCRIBE_BY_NAME的参数要传数据字典里的结构名或表名如果不存在的类型系统会直接抛出异常。调用前最好用cl_abap_typedescrdescribe_by_name的异常捕获或者在DDIC里先查一下。2.3 工作区赋值与读取字段符号的“挂载”操作方式一里行结构类型有时是已知的有时是未知的对应的处理方式完全不同。如果行结构是编译期已知的比如ty_person那行字段符号可以直接用静态类型声明FIELD-SYMBOLS: fline TYPE ty_person.这样后面写fline-name、fline-age时有自动补全读取也快代码可读性最好。前面2.1的例子就是这个套路。如果结构名是运行时才知道的那字段符号只能声明成TYPE ANY具体是什么样的结构要等ASSIGN之后才知道DATA: lr_line TYPE REF TO data. FIELD-SYMBOLS: fline TYPE ANY. FIELD-SYMBOLS: fv TYPE ANY. CREATE DATA lr_line TYPE HANDLE lo_struct. ASSIGN lr_line-* TO fline. 字段名也是动态的用 ASSIGN COMPONENT 访问 ASSIGN COMPONENT NAME OF STRUCTURE fline TO fv. fv 张三.ASSIGN COMPONENT ... TO fv这句就是在运行时按字段名把一个字段“挂”到通用字段符号上。字段符号和动态字段搭配使用是动态内表里最常见的操作方式。有一个点要记住字段符号一旦被ASSIGN到某个字段上后续操作都作用在那个字段如果再ASSIGN到另一个字段原来的指向就变了。需要保留旧值的话先放到变量或内表里别依赖字段符号“记住”上一个位置。2.4 方式一适合什么场景方式一的适用场景有一个共同特征列的集合已经存在不需要新增字段只是对象本身要动态创建。典型场景包括通用取数、对比、导出程序传入DDIC结构名动态建表再配合动态OPEN SQL把数据装进去批量处理程序同一个处理逻辑要跑多张结构类似的表运行时根据参数选择表类型程序内部需要把某张DDIC表的数据“复制”成一份独立内表但又不想在代码里写死表名需要把内表类型作为参数传来传去的通用函数比如动态排序、动态去重工具。方式一的局限也很明显你只能“借”已经存在的类型不能现场加一列。如果需求里明确说了“列名由配置文件维护可能随时新增”那就要用下面第二种方式了。3. 方式二RTTC运行时类型构造——“造”一个全新的表结构3.1 先认识RTTI/RTTC里的几个主角方式二在ABAP里有个专门的术语叫RTTCRuntime Type Creation运行时类型创建它是运行在RTTIRuntime Type Information运行时类型信息基础上的一套类体系。搞懂这几个类动态内表就通关了一半类作用类比cl_abap_typedescr所有类型描述器的抽象基类提供describe_by_name、describe_by_data等方法所有类型描述的“总纲”cl_abap_datadescr数据类型的基类结构、表、元素都继承它描述“数据”这一大类的基类cl_abap_elemdescr基本元素类型描述C、I、STRING、P等砖头最基础的构建单元cl_abap_structdescr结构类型负责生成结构的组件表、创建结构描述施工图确定一行的布局cl_abap_tabledescr表类型把行结构包装成内表类型仓库围栏确定内表怎么装行这几个类之间的关系是层层递进的先用cl_abap_elemdescr的工厂方法定义出每个字段的类型再用cl_abap_structdescrcreate把这些字段拼成一个结构然后用cl_abap_tabledescrcreate把这个结构包装成表类型最后用CREATE DATA ... TYPE HANDLE真正在内存里把这张表创建出来。cl_abap_elemdescr里的工厂方法很关键它提供了get_c、get_i、get_string、get_d、get_t、get_p等一批静态方法分别对应ABAP里的C、I、STRING、D、T、P等基本类型。你不需要CREATE OBJECT直接调用就可以拿到一个描述该基本类型的元素描述器。3.2 从组件表到数据对象RTTC创建动态内表四步走现在动手搭一张只有三个字段的动态表一个10位的字符字段FIELD1一个整数字段FIELD2一个变长字符串FIELD3。DATA: lr_table TYPE REF TO data, lr_line TYPE REF TO data. FIELD-SYMBOLS: ftable TYPE STANDARD TABLE, fline TYPE ANY. 1. 搭建组件表 DATA: lt_comp TYPE cl_abap_structdescrcomponent_table, ls_comp LIKE LINE OF lt_comp. ls_comp-name FIELD1. ls_comp-type cl_abap_elemdescrget_c( 10 ). APPEND ls_comp TO lt_comp. ls_comp-name FIELD2. ls_comp-type cl_abap_elemdescrget_i( ). APPEND ls_comp TO lt_comp. ls_comp-name FIELD3. ls_comp-type cl_abap_elemdescrget_string( ). APPEND ls_comp TO lt_comp. 2. 创建结构描述 DATA(lo_struct) cl_abap_structdescrcreate( lt_comp ). 3. 创建表描述 DATA(lo_table) cl_abap_tabledescrcreate( lo_struct ). 4. 实例化内表和行对象 CREATE DATA lr_table TYPE HANDLE lo_table. ASSIGN lr_table-* TO ftable. CREATE DATA lr_line TYPE HANDLE lo_struct. ASSIGN lr_line-* TO fline.这段代码看着不长但每一步都有讲究。第一步组件表lt_comp的类型是cl_abap_structdescrcomponent_table它的行类型是abap_componentdescr这个行结构里就两个关键字段name字段名和type该字段的类型描述。你可以把它理解成一个“字段清单”ABAP就是靠这份清单来决定一个结构里有哪些字段、每个字段怎么存。第二步cl_abap_structdescrcreate( lt_comp )拿到字段清单后会做一次完整校验检查字段名是否合法、有没有重复、类型描述是否完整。校验通过就返回一个结构描述器lo_struct。这一步生成的对象就是“施工图”。第三步cl_abap_tabledescrcreate( lo_struct )把结构描述包装成表描述。默认创建的是标准表、非唯一键。如果要做排序表或者哈希表可以在create方法里传参数后面我会专门讲到。第四步CREATE DATA ... TYPE HANDLE终于登场。前面三步都在“定义类型”这一步才真正分配内存。创建内表用TYPE HANDLE lo_table创建行用TYPE HANDLE lo_struct。这里的TYPE HANDLE和方式二里的一样都是把运行时描述器交给系统。区别在于方式一的描述器是“读”出来的方式二的描述器是“造”出来的。3.3 动态字段的赋值与读取ASSIGN COMPONENT才是主角表建好了行结构也建好了接下来如果还像静态内表那样用fline-FIELD1直接访问编译器不会让你过的因为fline是TYPE ANY字段名在编译期根本不可见。访问动态结构里的字段唯一通用的办法就是ASSIGN COMPONENT。FIELD-SYMBOLS: fv TYPE ANY. 赋值 ASSIGN COMPONENT FIELD1 OF STRUCTURE fline TO fv. fv ABC. ASSIGN COMPONENT FIELD2 OF STRUCTURE fline TO fv. fv 100. APPEND fline TO ftable. 读取 LOOP AT ftable ASSIGNING fline. ASSIGN COMPONENT FIELD1 OF STRUCTURE fline TO fv. WRITE: / fv. ENDLOOP.ASSIGN COMPONENT支持按字段名访问也支持按序号访问ASSIGN COMPONENT 1 OF STRUCTURE fline TO fv.按序号访问在字段名特别长、字段列表特别多时能省去字符串解析理论上比按名字访问快一点。但代价是代码可读性差字段一调整就容易错位所以我个人还是推荐按名字访问只有在极端的性能热点里才考虑用序号。另外提醒一句如果字段符号fv前一次指向了某个字段后一次ASSIGN COMPONENT没有成功比如字段名拼错了那么fv仍然保留之前的位置。下次赋值时可能写到错误的字段上这种Bug非常隐蔽。稳妥的做法是在ASSIGN之后立刻判断sy-subrc非0就说明组件不存在及时报错。3.4 在已有DDIC结构上扩展动态列实际项目里最香的用法不是完全从零建结构而是“保留标准字段的同时增加动态扩展列”。比如DDIC里有MARA程序需要在MARA所有字段之后追加一列ZZ_EXT用来临时存放外部导入的标记信息。DATA: lt_comp TYPE cl_abap_structdescrcomponent_table, ls_comp LIKE LINE OF lt_comp. 1. 把 MARA 的组件全部取出来 DATA(lo_ddic_type) cl_abap_typedescrdescribe_by_name( MARA ). DATA(lo_ddic_struct) CAST cl_abap_structdescr( lo_ddic_type ). lt_comp lo_ddic_struct-components. 2. 追加一列动态扩展字段 ls_comp-name ZZ_EXT. ls_comp-type cl_abap_elemdescrget_c( 20 ). APPEND ls_comp TO lt_comp. 3. 用扩展后的组件创建新结构、新表 DATA(lo_new_struct) cl_abap_structdescrcreate( lt_comp ). DATA(lo_new_table) cl_abap_tabledescrcreate( lo_new_struct ). DATA lr_tab TYPE REF TO data. CREATE DATA lr_tab TYPE HANDLE lo_new_table.这段代码的关键在于lo_ddic_struct-components。components是结构描述器自带的一个只读属性返回的就是该结构的所有组件表。把它复制到局部变量lt_comp后就可以自由地追加、删除、修改字段了。用这种方法你可以不只是追加字段还能做“字段裁剪”。比如MARA有几十个字段但你只需要其中三个可以先取出components再DELETE lt_comp WHERE name NOT IN (MATNR, MTART, MEINS)然后用剩下的组件创建新结构。这在做性能敏感的批量程序时非常实用不用把整张大表结构都搬进内存。3.5 方式二适合什么场景与能力边界RTTC适合的场景就是那种“列的定义本身就是运行时输入”的情况动态报表列的集合由用户勾选、配置表维护、权限控制决定Excel/CSV导入第一行表头直接决定内表字段名和字段顺序通用配置表单不同流程节点显示不同的列字段不固定接口适配器下游接口字段集和上游不同需要在运行时拼表结构。但RTTC也有一个必须接受的天花板类型描述器一旦创建就不能再修改。如果你想在已有的结构描述器上增删字段只能重新生成一份新的描述器不能就地改。这就意味着如果你发现建出来的结构漏了一列不是“补一列”而是重新走一遍“组件表 → create结构 → create表 → CREATE DATA”的流程。所以在写这段逻辑时一定要提前确认字段清单是否完整。4. 两种方式对比与选型建议4.1 一张表看懂两种方式的差异对比维度方式一基于已有类型方式二RTTC运行时类型构造类型来源程序内TYPES、DDIC结构/表类型运行时通过组件表拼接字段灵活性只能使用已有字段不能增删任意字段、任意类型、任意顺序创建开销很小基本等同静态定义需要创建类型描述器开销明显编码复杂度低结构已知时能直接访问字段高字段访问靠ASSIGN COMPONENT运行效率字段访问编译期绑定速度快每次动态字段访问都要解析慢一些代码可读性好差维护者需要理解RTTI体系典型场景通用取数、动态表名、批量处理动态报表列、导入表头解析、配置化表单光看表格可能觉得方式二全面优于方式一其实不是。方式一在一个地方吊打方式二代码可读性。方式一里如果结构类型已知字段符号可以用静态类型后面字段访问有自动补全、有编译检查Bug率明显低。方式二则要求你所有字段访问都走动态路径任何一个字段名拼错都要到运行期才暴露。创建开销和运行效率也是实际需要考虑的。RTTC要先构建组件表再创建结构描述和表描述哪怕是一张三列的简单表也需要构造两个类型描述对象这些对象本身还要占用内存。如果程序里需要高频创建同一结构的动态表务必把类型描述器提取到循环外面复用而不是每次循环都重新创建。4.2 我的选型原则能借就不造该造别硬凑在项目里做了几年动态内表相关功能后我总结了几条选型原则基本能应付大部分需求。第一能借就不造。如果列的集合已经在DDIC或程序TYPES里存在直接方式一。代码简单字段访问可控测试成本低。很多“通用取数”需求本质就是传入表名、读出数据表的列全是现成的完全没必要上RTTC。第二列要动态配置就直接上RTTC别用备用字段糊弄。有的开发为了省事把可能用到的列全部预先定义成一两百个空字段用的时候往里塞。这种做法短期能跑长期是灾难字段含义靠文档维护代码里全是WHEN COL_023这种分支别人根本不敢动。RTTC的复杂度虽然比方式一高但比“万能宽表”的后期维护成本低得多。第三拿不准的时候先写个最小原型。不少需求听起来像“以后可能要加列”实际上业务一年都没变过。遇到这种情况不妨先按方式一实现等真正出现动态列需求再重构。过度设计比简单设计更容易让项目失控。第四组合使用是常态。最典型的场景就是第3.4节里说的从DDIC结构取出组件追加动态列再用RTTC生成新结构。这种情况下不必纠结“这到底算方式一还是方式二”只要清楚“列是从哪来的、要在哪一步追加”代码脉络自然就清晰了。5. 常见问题与排查技巧实录5.1 一运行就报GETWA_NOT_ASSIGNED忘了ASSIGN字段符号这是动态内表新手踩得最狠的坑。CREATE DATA执行完成后内存对象确实创建了但数据引用本身不是可访问对象。如果你不先ASSIGN lr_tab-* TO ftab就直接用ftab系统会抛出GETWA_NOT_ASSIGNED也就是“字段符号未分配”的运行时错误。还有另一个容易踩的变体ASSIGN了内表字段符号但忘了ASSIGN行字段符号就直接往工作区里写值。比如建完ftable后立刻执行APPEND fline TO ftable而fline根本没有指向任何结构同样会dump。正确的顺序永远是CREATE DATA内表 →ASSIGN内表字段符号 →CREATE DATA行对象 →ASSIGN行字段符号 → 操作字段 →APPEND。少一步都不行。如果行结构类型不确定在ASSIGN COMPONENT后还要额外检查sy-subrc。5.2 动态内表怎么作为参数来回传把动态内表传给函数或方法有两种常见声明方式按场景选。如果是传递引用接口参数声明为TYPE REF TO dataMETHOD pass_by_ref. 调用方传入 lr_table方法内部再 assign 到字段符号 FIELD-SYMBOLS ftab TYPE STANDARD TABLE. ASSIGN mr_table-* TO ftab. ENDMETHOD.如果希望调用方直接传内表实体接口参数可以声明为泛型内表TYPE ANY TABLEMETHOD show_data. ct_data 本身就是一张内表 LOOP AT ct_data ASSIGNING FIELD-SYMBOL(fline). ... ENDLOOP. ENDMETHOD.跨模块传递时我建议优先用TYPE REF TO data传引用因为引用参数不要求编译期确定内表结构灵活度最高。TYPE ANY TABLE也常用但调用方如果传的是动态表字段符号一样得先有具体的内表对象才能传进来。在方法内部无论哪种方式真正访问数据还是靠字段符号。5.3 构造类型时的“小白坑”get_c必须带长度STRING不需要用cl_abap_elemdescr构造字段类型时最常见的错误是get_c( )没带长度。ABAP里的C是定长字符长度是类型定义的一部分必须指定。比如get_c( 10 )就是一个10位的字符字段。STRING是变长字符串不需要长度直接get_string( )。字段类型工厂方法必填参数C 定长字符cl_abap_elemdescrget_c( len )长度N 定长数字字符串cl_abap_elemdescrget_n( len )长度I 整数cl_abap_elemdescrget_i( )无P 压缩数值cl_abap_elemdescrget_p( length .. decimals .. )长度、小数位F 浮点数cl_abap_elemdescrget_f( )无STRING 变长字符cl_abap_elemdescrget_string( )无D 日期cl_abap_elemdescrget_d( )无T 时间cl_abap_elemdescrget_t( )无X 十六进制cl_abap_elemdescrget_x( len )长度P型是一个比较容易记错的类型。它不像I型那么简单需要同时指定总长度和小数位。比如get_p( length 8 decimals 2 )表示一个8位长、2位小数的压缩数值。如果漏了小数位后面赋值时小数部分可能被截断或报错。另一种隐蔽问题是从DDIC结构复制components后直接追加字段但追加的字段名和原结构字段重名。cl_abap_structdescrcreate会校验重复字段名直接抛异常。像这种场景追加前应该先检查一下字段名是否已存在。5.4 动态内表的排序、去重与ALV输出动态内表不能像静态内表那样直接写SORT ftab BY field1 field2因为字段名是运行时才知道的。新版本ABAP支持在BY后面用括号括起一个字符变量DATA(lv_sort_field) FIELD1 FIELD2. SORT ftab BY (lv_sort_field).多个排序字段用空格分隔变量里顺序就代表排序优先级。去重新版本也支持动态写法DATA(lv_field) FIELD1. DELETE ADJACENT DUPLICATES FROM ftab COMPARING (lv_field).如果你的系统版本较老不确定是否支持这种动态写法稳妥的替代方案是先把动态内表转存到一张已定义好结构的内表再静态排序。但这样会牺牲动态性所以更推荐的做法是升级或直接让业务接受“排序字段配置化”。动态内表接ALV也很常见。旧函数REUSE_ALV_GRID_DISPLAY和新的cl_gui_alv_grid都支持把动态内表字段符号作为输出表传入CALL FUNCTION REUSE_ALV_GRID_DISPLAY EXPORTING i_structure_name it_fieldcat lt_fcat[] 字段目录按组件表动态生成 TABLES t_outtab ftable.如果it_fieldcat为空ALV会尝试按内表结构自行生成列但列文本通常不够友好。所以实际项目里我一般会遍历组件表把每个字段的fieldname、seltext、col_pos填进lvc_s_fcat这样列标题、顺序都完全可控。5.5 性能与内存动态内表不是免费的RTTC创建类型描述器的开销不是虚的。我做通用导入功能时曾经踩过一次在读取文件的循环里每读一行就调用一次cl_abap_structdescrcreate结果10万行数据跑了快两分钟。后来把类型描述器创建提到循环外同一份数据几秒就跑完了。类型描述器是可复用的同一结构建一次就够了。如果动态内表本身行数很大还要注意内存。动态表虽然灵活但每行数据在内存里的布局是通过运行时描述确定的相比静态表会有额外的元数据开销。处理完临时动态表后用FREE lr_table或直接让数据引用离开作用域尽早释放内存。另外动态字段访问本身也比静态字段访问慢。ASSIGN COMPONENT需要在运行时查找组件表如果内表有几百万行、每行都要动态访问两个字段耗时差距会非常明显。遇到这种热点场景我的做法是先把动态结构的关键字段取出来转成一张静态结构的内表后续处理全部走静态字段。动态性只放在入口和出口中间数据处理用静态速度和可读性都能保住。如果让我给一个最简单直接的落地建议列结构已经确定只是对象运行时生成用CREATE DATATYPE列结构本身要由配置、文件头或用户输入决定坚决上RTTC。这两种方式不是对立关系实际项目里经常是先用describe_by_name读一个DDIC结构再追加动态列最后通过tabledescr生成表。把这一套玩明白再做动态报表、通用导入、扩展框架这类需求会轻松很多。最后补一个我自己踩过的坑不要把RTTC类型描述器的创建写在循环里。刚开始做通用导入时我在读取文件的循环里创建结构描述10万条数据跑了快两分钟后来把类型描述器提到循环外耗时直接降到几秒。类似这样的细节代码不会报错但性能会教你做人。
