
在S/4HANA 2020之后的项目里把经典CDS视图改成CDS View Entity几乎是每个数据模型改造任务单里都要出现的动作。我去年接手的一个报表改造项目里有三十多个基于DEFINE VIEW的老视图对应着二十多个ALV报表和四五个下游接口全部都要平移到DEFINE VIEW ENTITY上去。最开始我以为只是把define view改成define view entity、删掉AbapCatalog.sqlViewName就完事真动起手来才发现语法只是一小部分底层存储对象变了、权限控制方式变了、各种直接读数据库底层视图的历史习惯全暴露出来了。这篇文章把整个迁移过程拆开讲包括为什么要迁、两种语法到底差在哪、ADT向导怎么用、报表侧如何配合改造以及我踩过的十几个坑。内容以S/4HANA 2020之后、ABAP开发工具ADT为背景适合正在做CDS迁移或准备把老视图升级的ABAP开发同学参考。整篇没有太多官话都是实际敲过代码、跑过回归后的经验总结。1. 为什么非迁不可CDS View Entity 不是在旧语法上打补丁1.1 旧语法的历史包袱CDS在NetWeaver 7.4时代刚进入ABAP时为了能和传统ABAP Dictionary、SE11、数据库视图机制和平共处每个经典CDS View都必须通过AbapCatalog.sqlViewName关联一个底层数据库视图。也就是说你写了一个define view系统实际上会在ABAP字典里维护一个CDS对象又要在数据库层创建一个SQL View两套对象绑定在一起才能工作。AbapCatalog.sqlViewName: ZMARA_LEGACY AbapCatalog.compiler.compareFilter: true AccessControl.authorizationCheck: #CHECK EndUserText.label: 物料主数据经典视图 define view ZI_MARA_LEGACY as select from mara left outer join t023 as _matkl on _matkl.matkl mara.matkl { mara.matnr as MaterialNumber, mara.ersda as CreationDate, mara.matkl as MaterialGroup, _matkl.wgbez as GroupDescription } where mara.mtart ROH;这段写法在S/4HANA 2020之前的时代非常好用SE11能看到、底层SQL能直接访问、RFC还能导数据。但它的双份对象状态在新平台上成了负担。最典型的问题有三个每次对CDS View做字段调整底层SQL View也要跟着同步激活链路更长权限控制要在CDS层理解一遍又要和数据库层的授权对象纠缠一遍在ABAP云环境BTP/Steampunk里底层SQL View被彻底禁止SAP根本不给这种双对象模型留活路。我接触过的一个客户系统里甚至出现过这种情况SE11里的数据库视图被人手动改了权限导致CDS层明明已经做了AccessControl.authorizationCheck: #CHECK但实际报表返回的数据还是和学生账号看到的权限结果不一致排查了大半天才发现问题出在数据库层的GRANT语句上。这种历史遗留问题在迁移到View Entity之后反而能一劳永逸地避开。1.2 新语法带来的实际收益define view entity是CDS的现代形态直接在ABAP层创建视图实体不再生成对应的数据库SQL View。表面看只是少了一行sqlViewName注解实际上整个对象模型简化了少掉一层DB View不意味着功能变弱反而让ABAP编译器对视图的定义解析更直接key字段被强制要求数据模型的完整性更好下游做OData、做分析查询都有明确主键可依赖新函数、新注解、参数化视图、关联导航等能力基本都以View Entity为基准在持续演进云版本、S/4HANA 2020之后的新项目官方默认推荐的就是View Entity。性能方面我不想神化它。我实测过一个三层视图嵌套的场景最底层是几张业务表中间两个经典CDS视图互相引用最上层是一个报表直接查这个视图。迁移到View Entity之后HANA的执行计划确实简化了原因是少了一层SQL View的名称解析和语义映射。但在单层视图、简单过滤的场景下新旧差异感知不强。所以我的建议很明确迁移的核心驱动力是平台的演进方向而不是追逐一个虚无缥缈的性能数字。2. DEFINE VIEW 与 VIEW ENTITY 的语法位移一行一行看差异2.1 两种语法骨架的直观对比最直观的方式是并排看。上面那个经典CDS视图迁移成View Entity之后长这样AccessControl.authorizationCheck: #CHECK EndUserText.label: 物料主数据视图实体 define view entity ZI_MARA_ENTITY as select from mara left outer join t023 as _matkl on _matkl.matkl mara.matkl { key mara.matnr as MaterialNumber, mara.ersda as CreationDate, mara.matkl as MaterialGroup, _matkl.wgbez as GroupDescription } where mara.mtart ROH;从字符层面看改动点只有三处define view变成define view entity、删掉AbapCatalog.sqlViewName和AbapCatalog.compiler.compareFilter、给主数据字段加上key。但就是这三处改动把整个对象在ABAP运行时中的角色完全改变了。2.2 被删除的注解意味着什么先看AbapCatalog.sqlViewName。老视图里这个注解负责把CDS视图映射到数据库视图映射关系建立之后你在SE11或者其他数据库工具里能看到一个真实存在的SQL View。别以为这只是个名字它牵扯到SE11里能够直接浏览的数据对象RFC或其他外部系统通过数据库连接直接读取的路径DBA在数据库层做的授权管理对象。迁移到View Entity之后这个数据库视图就不存在了。它的替代路径非常清晰所有读取都必须通过ABAP层的Open SQL、CDS消费方或者OData服务完成。如果你的系统里存在第三方工具直连数据库读SQL View的情况请务必在迁移前排查干净否则上线当天就是断供当天。AbapCatalog.compiler.compareFilter也一样。这个注解在旧编译器体系里影响过滤条件的比较优化在新Entity语法中已经没有对应概念。ADT向导处理的方式通常是直接丢弃。我建议不要心疼这个注解——它被丢弃后如果查询计划出现明显变化优先检查WHERE条件能否改写得更紧凑而不是试图在新语法里找回一个等价的开关。2.3 key 字段为什么绕不开经典CDS View其实不强制key。你可以写一个没有任何key字段的投影系统也能激活下游查询也能跑。但View Entity不同它要求投影出来的字段里至少有一个主键标记理由很直接ABAP和HANA需要依赖key做数据去重、做实体识别OData服务、ODP提取、分析引擎都需要稳定主键后续在这个entity上做association、做redirected tokey一致才能保证导航正确。我见过一个小白项目迁移时开发图省事把老视图里所有字段自动勾成key结果下游报表用SELECT *拉数据突然跑出来大量重复行——因为源表本身不是按这些字段唯一的多个业务主键被同时标记成key之后语义完全变了。正确做法是把key留给源表的真正主键字段。比如我们项目里物料主数据就只留matnr作为key即使它实际在某个JOIN场景下出现重复也至少和源表语义保持一致。2.4 表达式和函数的兼容性细节表达式和函数在迁移过程中是第二大类差异点。我遇到过的典型情况老视图里某些字段做了cast比如把matkl转成字符串或者把menge转成d34r迁移后编译器对类型推断更严格原先能过的隐式转换现在可能直接报Assignment to incompatible data type聚合字段和group by的配合老视图里sum出来的字段在group by之外被引用迁移前编译器睁一只眼闭一只眼迁移后会被卡住如果投影里有case when分支各分支的返回类型尽量显式统一。新语法对分支类型的一致性要求更苛刻尤其是返回CHAR和NUMC混用时一定用cast拉齐。我的建议是转换之后第一次激活不要急着冲报表先把所有字段的数据类型、精度、key标记抄录下来和旧视图做一次逐字段对照。这一步能省掉后面成堆的隐性BUG。3. 用 ADT 向导完成转换从右键到重新激活的每一步3.1 转换前必须准备好的三件事我不建议拿到ADT就直接右键转换。磨刀不误砍柴工以下三件事先做完确认系统版本和ADT版本。View Entity迁移在S/4HANA 2020ABAP 1909之后才被完整支持。如果你是2020之前的版本先别谈迁移老老实实升级或评估兼容性。ADT方面我环境里用的是2023年的版本右键菜单中能找到相关入口旧版ADT可能功能不全建议检查Help菜单里的CDS功能列表。梳理下游引用清单。你要知道这个CDS View被哪些上层视图引用、被哪些报表直接SELECT FROM、有没有OData服务在消费它、有没有外部程序通过AbapCatalog.sqlViewName对应的DB View直接读取。梳理方法很简单ADT里选中DDLS源右键Where Used List再全库搜索一遍sqlViewName对应的数据库视图名。做好旧版本备份。虽然ADT带Undo和本地历史但双击确认一下Git仓库状态或手动导出一份DDLS源文件成本很低。我见过不止一次转换向导中途报错把源文件改到一半结果两边都不好激活的尴尬场景。3.2 向导操作流程以我手头的ADT版本为例操作大概是这样的。在Project Explorer中选中要转换的DDLS源右键菜单的Refactor子菜单里能找到Convert to CDS View Entity相关入口。不同版本的菜单位置会有差异有的版本在右键菜单直接展示有的需要在Source菜单里翻一下但核心流程一致向导会先做一次静态检查列出所有潜在的不兼容项比如缺失key、不支持的注解、表达式类型冲突检查结果出来后可以选择单个对象或多个对象一起转换。项目里如果是一批视图批量导建议全选但注意分批执行每批控制在十几个以内比较好排查问题转换过程中向导会自动把语法从define view改成define view entity去掉不再支持的注解并对key字段给出默认建议最后生成一个新的DDLS源进入激活阶段。激活时如果报错错误列表会直接指向出问题的行。如果你用ADT找不到这个入口可以搜索 CDS View Entity Migration 相关帮助文档。还有一个土办法手动新建一个DDLS源选择define view entity模板然后把老视图的select body贴进去再手工修类型和key。虽然费点劲但对理解语法差异很有帮助特别适合本地没有向导版本的情况。3.3 转换完成后必须人工复查的点向导能帮的忙就到语法层为止下面这些点它管不了必须人工过一遍key字段的合理性。自动标记可能猜错尤其在有JOIN、有聚合、有union的场景下注解变动对下游的影响。比如EndUserText.label、UI.lineItem、ObjectModel.*这些注解如果在新语法里表现不一致OData服务和前端字段标签都可能变关联关系是否完整。如果老视图里定义了association迁移后目标entity是否还存在、关联字段的key是否保持对齐旧对象的释放策略。如果entity用了新名字旧view是立刻删除还是保留过渡期需要和运维评估。过渡期双跑是常见做法但别拖太久否则维护成本翻倍。4. 报表侧的实际适配调用方代码和平行测试方案4.1 改报表代码的第一刀数据源切换报表侧的改动通常没有想象中那么大。老代码里凡是SELECT ... FROM zi_mara_legacy这种写法直接把数据源名换成entity名即可。如果entity名沿用旧view的名字甚至在传输到生产后一行代码都不用改。这也是我强烈建议命名尽量平滑的原因语义没有变化时别为了焕然一新改名给自己找一堆下游修改的活。如果老报表喜欢SELECT *迁移后字段集合基本保持一致。但要注意INTO CORRESPONDING FIELDS OF的场景如果entity的key字段、字段顺序发生调整结构传输可能不完整最好的规避方式是INTO CORRESPONDING FIELDS OF之前先看一遍entity的字段清单别指望通配符永远替你兜底。4.2 JOIN 与关联导航的取舍我见过很多老报表为了拉取关联表的描述文本在ABAP层写了好几个LEFT JOIN t023 ON ...、LEFT JOIN t024 ON ...。迁到view entity之后这些JOIN可以保留毕竟语法兼容。但既然已经动刀了我建议顺手评估一下哪些JOIN可以收进entity的association里让ABAP报表侧的SQL更清爽。举个例子原来报表里要取物料组描述代码大概是SELECT mara.materialnumber, mara.materialgroup, t.wgbez AS groupdesc FROM zi_mara_entity AS mara LEFT OUTER JOIN t023 AS t ON t.matkl mara.materialgroup INTO TABLE DATA(lt_result) UP TO 10 ROWS.如果你的entity已经在CDS层定义好了到物料组的association报表里通过关联导航就能少写一段ON条件。这种改写对代码可读性提升非常明显而且关联的解析和join顺序由ABAP编译器统一处理出错的概率更小。当然改不改取决于团队约定不要为了炫技把简单查询改复杂。4.3 数据一致性平行测试方案迁移不是激活就算完数据一致性必须验证。我用的方案比较简单但很有效行数级校验。旧view和entity分别执行COUNT(*)比对结果是否一致数值汇总校验。挑几个业务金额字段做SUM最好跨几个过滤条件做几组明细抽样校验。对关键凭证号、物料号做点查比对每一列的值是否逐字一致异常值校验。重点关注CHAR类型字段的尾部空格、日期字段的00000000这类边界值。每个环节我都用一个传输请求跑一套比对报表数据不匹配就停下来查原因。这种测试方案虽然土但能覆盖掉迁移过程中绝大多数隐藏的语义差异。5. 迁移后的避坑清单十个真实踩坑点5.1 坑一DCL授权检查在激活时突然冒出来View Entity默认对权限控制的检查方式和老view不完全一样。如果你在老view上定义了AccessControl.authorizationCheck: #CHECK却没有配套的Access Control文件DCL激活时很可能直接报授权缺失。见过新手一看到这个报错就慌其实解决办法就是去ADT里给entity创建一个对应的Access Control源把原本的授权条件搬过去。注意在搬的过程中如果老view用的是#NOT_REQUIRED新entity里可以保留相同的注解值但生产环境强烈建议按#CHECK补全DCL否则数据权限裸奔。5.2 坑二底层SQL View断供的连锁反应这是最大的暗雷。一个老view在系统里运行了五年AbapCatalog.sqlViewName对应的DB View早就被各种程序、定时任务、第三方工具引用。迁移成entity之后那个DB View很可能直接消失。我们项目里就有一个外围数据抽取作业通过DB连接直连ZMARA_LEGACY这个视图拉数迁移次日凌晨作业进程报表不存在监控群直接报警。所以迁移前一定要做一次数据库视图引用排查SE11里搜视图名、ABAP代码里搜 FROM ZMARA_LEGACY、数据库工具里看依赖关系。确认没有外部引用再动手否则就保留一个兼容view作为过渡等外部程序切换完再清理。5.3 坑三key字段选错导致重复数据前面提到过自动标记的key不一定是源表主键。我见过一个跨多个表JOIN的宽表视图向导把所有投影字段都标成了key结果下游报表用SELECT *一拉原本应该按订单号聚合的行被拆成了若干重复行。解决方法是回归到业务语义key就是数据模型里识别一条记录的那几个字段多数情况就是源表主键。如果视图本身就是聚合结果没有任何自然主键那就选分组字段作为key至少逻辑上能表达这个视图里一行代表什么。5.4 坑四金额和单位注解丢失后报表格式全乱经典CDS view里如果投影了货币金额字段老系统可能依靠隐式转换维持着CURR和CURRENCY KEY的关系。迁移到view entity后如果没有显式加上Semantics.amount.currencyCode这类注解报表取出来的金额会变成裸数字小数位和货币显示完全看前端心情。这个坑非常隐蔽因为数据值本身没变但ALV显示格式变得非常奇怪。建议迁移后逐字段检查所有CURR、QUAN类型字段确认语义注解齐全。5.5 坑五CHAR类型尾部空格的变化HANA上CHAR字段的尾部空格处理本来就是老大难。老view通过DB View读取时数据库层的填充规则可能把尾部空格补齐entity直接走ABAP CDS解析后某些场景下CSTR和CHAR的尾随空格行为会有细微差异。如果平行测试发现某几个字段总是比对一致但TRUNCATED/填充不一致优先检查字段的精确类型定义在ABAP层用CONCAT或LTRIM显式处理。5.6 坑六OData服务需要重新激活runtime组件老view如果被OData服务作为数据源使用迁移后光激活entity不够还要去SEGW里检查服务定义确认数据源引用是否仍然有效。有的场景需要重新生成runtime artifact有的只需要重新激活服务。别忽略这一步否则前端App到生产环境一调接口就是500。5.7 坑七关联目标同步迁移的时序如果你迁移的view被另外十多个上层view当作association目标不要只迁这一个就收工。上层view里如果通过association [0..*] to ZI_MARA_LEGACY引用老对象老view一旦消失或改名上层全部激活失败。稳妥做法是从底层叶子视图开始迁一层一层往上顺带把每个引用方的语句同步改掉。项目里我习惯先列一张CDS依赖树自底向上分批而不是按字母顺序乱迁。5.8 坑八变更传输顺序问题一个CDS view entity涉及的主DDLS、DCL文件、下游消费view在传输请求里要保证顺序正确。如果只激活了entity就把传输释放而DCL和下游引用还没传目标系统会出现激活断层。项目里我吃过这个亏后来统一建议一个迁移unit里至少把entity DCL 受影响的消费方打包进同一个请求或者明确标记依赖顺序。5.9 坑九参数视图的参数列表改变老view如果用了with parameters迁移后参数列表的写法要保持兼容。不要随手把参数名改了否则所有调用方都会挂。ADT检查时会给提示但提示归提示最终拍板改不改的是人。5.10 坑十新旧对象并存期的维护混乱迁移过程中新旧对象会有一段并行期。最怕的是大家随手修数据、补字段一会儿改旧的一会儿改新的最后两边语义漂移谁也说不清哪个是准的。建议在并行期定一个强硬规则新需求一律改entity旧view只做业务维护不做新功能并行期超过一个季度还未完成迁移的重新评估风险。这个清单不算穷尽但基本覆盖了我迁移三十多个视图过程中遇到的大部分惊喜。每一类坑在测试环境都能模拟出来代价是走一遍测试计划千万别跳步。6. 最后聊两句实操节奏如果让我总结整个CDS View Entity迁移项目的最核心经验就一句话技术难点不深管理难点在协调。逐个改语法、调注解半天能搞定几个真正费时间的是下游引用梳理、外部系统配合、数据一致性比对。我建议把迁移节奏拉得优雅一点按依赖树分层推进每一层做完就做一轮报表回归达到可发布状态再动下一层。毕竟这种迁移不会带来即时可见的巨大性能提升但它决定了你的系统在下一个平台版本里还能不能顺畅升级。早迁早安心晚迁攒债。