1. 为什么需要重新理解S/4HANA的数据访问?
在传统SAP ERP时代,我们习惯了直接访问底层数据库表来获取业务数据。这种"裸奔式"的数据访问方式虽然直接,但随着系统复杂度提升,暴露出几个致命问题:
- 数据结构耦合度高:业务逻辑直接与物理表结构绑定,表结构变更会导致大量程序失效
- 语义不统一:同一个业务概念在不同模块可能有不同表结构,导致重复开发
- 性能不可控:缺乏统一的查询优化机制,复杂报表经常拖垮系统
S/4HANA引入的VDM(Virtual Data Model)和CDS(Core Data Services)View正是为了解决这些问题。我参与过多个S/4HANA迁移项目,发现很多团队还在用ECC时代的思维访问数据,这就像开着跑车却用马车的方式驾驶——既发挥不出HANA的性能优势,又增加了维护成本。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. VDM与CDS View的技术本质
2.1 VDM:业务语义的抽象层
VDM不是具体的技术组件,而是一种建模理念。它将分散的物理表按照业务领域重新组织,形成具有明确语义边界的逻辑模型。例如:
- Customer领域:聚合了KNA1(主数据)、KNB1(公司代码数据)、KNVV(销售数据)等物理表
- Material领域:整合了MARA(主数据)、MARC(工厂数据)、MVKE(销售数据)等
这种组织方式让开发者不用再记忆几十个表关联,只需知道"我需要客户主数据"就能找到对应视图。
2.2 CDS View:模型的具体实现
CDS View是VDM理念的技术载体,它通过声明式语法定义数据模型。一个典型的物料主数据CDS View可能是这样的:
sql复制@AbapCatalog.sqlViewName: 'ZMATERIALMD'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Material Master Data'
define view Z_MaterialMaster as select from mara
inner join makt on makt.matnr = mara.matnr
inner join marc on marc.matnr = mara.matnr {
key mara.matnr as Material,
makt.maktx as Description,
mara.meins as BaseUnit,
mara.matkl as MaterialGroup,
marc.werks as Plant
} where makt.spras = $session.system_language
这段代码体现了几个关键设计思想:
- 语义化字段命名:将MATNR重命名为Material,使字段名具有业务含义
- 自动关联处理:通过join自动处理表间关系
- 会话上下文感知:$session变量实现多语言支持
3. 从物理表到语义模型的工程化路径
3.1 基础层:物理表映射
这是最底层的CDS View,直接对应物理表结构。主要作用:
- 重命名字段为业务术语
- 添加必要的筛选条件
- 标记技术字段(如客户端字段)
sql复制@AbapCatalog.sqlViewName: 'ZBASEMARA'
define view Z_BaseMaterial as select from mara {
key matnr as Material,
ersda as CreatedOn,
ernam as CreatedBy,
laeda as LastChange,
aenam as ChangedBy,
// 其他字段映射...
}
提示:即使在这个简单映射层,也应该添加
@AccessControl注解定义权限检查策略,避免后续返工。
3.2 业务层:领域模型构建
在基础层之上,按照业务领域组织数据。这个层级需要:
- 跨表关联(如物料主数据+文本+库存)
- 添加计算字段(如库存可用性)
- 定义默认参数(如语言、公司代码)
sql复制@AccessControl.authorizationCheck: #CHECK
define view Z_MaterialStock as select from Z_BaseMaterial as mat
inner join mard as stock on stock.matnr = mat.Material {
key mat.Material,
mat.Description,
stock.werks as Plant,
stock.lgort as StorageLocation,
stock.labst as Unrestricted,
stock.umlme as InTransit,
// 计算字段
stock.labst - stock.umlme as AvailableStock
} where stock.mandt = $session.client
3.3 应用层:场景化视图
最上层是针对具体业务场景的优化视图。例如:
- 采购申请专用视图(仅显示采购相关字段)
- MRP运算优化视图(预计算关键指标)
- 财务报表视图(符合会计准则的金额汇总)
sql复制@EndUserText.label: 'Material for Procurement'
define view Z_Material4Purchasing as select from Z_MaterialMaster {
Material,
Description,
MaterialGroup,
BaseUnit,
// 采购特有字段
_PurchaseInfo.purchGroup as PurchGroup,
_PurchaseInfo.overDelivTol as OverDeliveryTolerance
} where MaterialGroup in ('RAW','SEMI')
4. 实战中的性能优化技巧
4.1 注解驱动的优化
CDS提供了一系列性能优化注解:
sql复制@Analytics.dataCategory: #FACT
@Analytics.dataExtraction.enabled: true
@ObjectModel.representativeKey: 'Material'
define view Z_MaterialAnalytics...
关键注解包括:
@Analytics:优化分析查询@ObjectModel:定义UI集成行为@Consumption:控制OData暴露方式
4.2 分区策略设计
对于海量数据表(如会计凭证),需要设计分区策略:
sql复制define view Z_AccountingDoc as select from bkpf
inner join bseg on bseg.bukrs = bkpf.bukrs
and bseg.gjahr = bkpf.gjahr
and bseg.belnr = bkpf.belnr {
// 字段定义...
} where bkpf.gjahr = $parameters.Gjahr
and bkpf.bukrs = $parameters.Bukrs
通过$parameters实现动态分区,避免全表扫描。
4.3 缓存策略配置
在CDS View属性中设置:
xml复制<annotation Term="UI.Identification" Bool="true"/>
<annotation Term="Common.CacheMaxAge" Int="3600"/>
合理设置缓存可以显著提升频繁访问视图的性能。
5. 迁移过程中的常见陷阱
5.1 事务一致性处理
传统SE16/SE38直接查表时,看到的是实时数据。而CDS View可能启用缓存,导致看到的数据状态不一致。解决方案:
sql复制@AccessControl.authorizationCheck: #NOT_REQUIRED
@Analytics.cache.enabled: false // 禁用缓存
define view Z_RealTimeStock...
5.2 权限继承问题
基础视图的权限控制不会自动传递到上层视图。必须显式处理:
sql复制@AccessControl.authorizationCheck: #CHECK
define view Z_SensitiveData as select from Z_BaseView
with PRIVILEGED ACCESS // 需要特殊权限
{
// 字段定义
}
5.3 货币/单位转换
传统方式使用函数模块转换,在CDS中应该:
sql复制define view Z_CurrencyConvert as select from ekpo {
key ebeln as PO,
key ebelp as Item,
netwr as NetAmount,
waers as Currency,
// 转换金额到本位币
currency_conversion(
amount => netwr,
source_currency => waers,
target_currency => $session.local_currency,
exchange_rate_date => $session.current_date
) as LocalAmount
}
6. 现代开发模式的最佳实践
6.1 基于Git的协作开发
使用ABAP Git管理CDS View开发:
- 每个业务领域建立独立package
- 通过feature分支开发新视图
- Pull Request合并前必须通过:
- 语法检查
- 性能分析
- 权限验证
6.2 自动化测试策略
为CDS View创建测试套件:
abap复制CLASS ltc_material_view DEFINITION FINAL FOR TESTING
RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION.
METHODS test_material_select FOR TESTING.
ENDCLASS.
METHOD test_material_select.
SELECT COUNT(*) FROM z_materialmaster
INTO @DATA(cnt)
WHERE material_group = 'RAW'.
cl_abap_unit_assert=>assert_not_initial( cnt ).
ENDMETHOD.
6.3 监控与调优
使用事务ST05跟踪CDS View执行:
- 识别未走索引的查询
- 检查不必要的字段传输
- 分析join效率
我发现在实际项目中,80%的性能问题可以通过以下方式解决:
- 添加缺失的索引
- 优化join顺序
- 减少传输字段
7. 与HANA原生特性的深度集成
7.1 计算视图调用
在CDS中直接调用HANA计算视图:
sql复制define view Z_AdvancedCalc as select from "SAPHANADB"."CALCVIEW" {
"MATNR" as Material,
"PREDICTED_VALUE" as ForecastDemand
}
7.2 机器学习集成
使用APPLY_PREDICTION函数嵌入机器学习:
sql复制define view Z_MaterialForecast as select from matdoc {
matnr as Material,
budat as PostingDate,
menge as Quantity,
// 调用预测模型
apply_prediction(
model => 'DEMAND_FORECAST',
menge as Quantity,
budat as PostingDate
) as PredictedDemand
}
7.3 时序数据处理
针对时间序列数据优化:
sql复制define view Z_TimeSeries with parameters
@Environment.systemField: #SYSTEM_DATE
p_date : abap.date
as select from matdoc {
matnr as Material,
budat as PostingDate,
menge as Quantity
} where budat > :p_date - 30
这种设计可以自动获取系统日期作为默认值。
