在本文中,我会通过CDS视图来介绍虚拟数据模型(Virtual Data Model,以下简称VDM)。
在SAP HANA平台出现后,SAP的业务应用开发模式已经产生了变化,新的经验法则是:尽可能在数据库中做更多的事情(PUSH DOWN),以得到最佳的性能。
本文链接:https://www.cnblogs.com/hhelibeb/p/9223993.html
嵌入式分析
SAP S/4嵌入式分析是S/4的一部分,用于对业务数据执行复杂的实时报表和分析。SAP S/4嵌入式分析的关键架构组件包括SAP HANA数据库,VDM,分析引擎(嵌入式SAP BW),OData服务,和如下图所示的接口。
本文将介绍其中的VDM。
CDS视图
为了在应用开发中利用SAP HANA的优点,SAP引入了新的数据建模基础设施,名为Core Data Service(以下简称CDS)。通过CDS,数据模型会在数据库而非应用服务器中定义和消费。CDS也提供了传统数据建模工具的兼容性,包含支持概念建模和关系定义,内置函数和扩展。
技术上讲,CDS是提供数据定义语言(DDL)的加强版SQL,用于在数据库中定义富语义数据库表/视图(CDS entity)和用户定义的类型。增强包括:
- Expression:用于在数据模型中计算和查询。
- Association:在概念层上,通过简单的path expression代替join(关于Association可参考之前的文章)。
- Annotation:通过附加的(特定领域的)元数据来丰富数据模型。
CDS在ABAP和SAP HANA中都得到了原生支持。数据模型通过数据定义语言(DDL)表达,并且定义为CDS视图,可以在ABAP程序中通过Open SQL语句访问。CDS为业务和开发提供了一系列优点,包含:
- 富语义数据模型:CDS基于众所周知的实体-关系模型,并且实质上是声明式的,非常接近概念思维。
- 兼容任何数据库平台:CDS会被生成为受管理的Open SQL视图,并且原生地集成进入SAP HANA层。这些基于Open SQL的视图得到了所有主流数据库供应商的支持。
- 高效:CDS提供了多样化和高效的内置函数,例如SQL运算符,聚合和表达式,来帮助开发者构建视图。
- 支持annotation:CDS语法支持特定领域的annotation,可以轻松的被其他组件利用,比如UI,分析和OData服务。
- 支持概念关联( conceptual associations):CDS帮助你定义association,作为不同视图的关系。Path expression可以用于在关系间导航。association引入了抽象的外键关系和join,来对可消费的entity进行导航。
- 可扩展性:客户可以扩展SAP定义的CDS视图,扩展字段将随其使用层次结构自动添加到CDS视图中。
VDM
在HANA之前,查询ERP系统中的大量数据集会花很多时间,并会降低系统的整体性能。数据仓库可以通过高级建模技术创建持久化的数据模型,以提高查询性能。SAP HANA去除了ERP中的性能问题,使我们能够直接在ERP中创建虚拟数据模型(VDM),并提供令人难以置信的高性能。
VDM是什么?VDM是富语义CDS视图的结合,它将ERP源表的数据符合逻辑地结合到一起,创建成为有意义的数据集,并且可以被前端工具便利地消费。
VDM的核心原理是在现有的S/4数据库模型之上构建语义层,隐藏它的技术细节。基于提供的内容和复用选项,VDM中的CDS视图被分为接口视图或消费视图,如下:
私有视图
私有视图(Private Views)实际上不是提供给终端用户消费的视图,它是技术驱动的辅助视图。引入私有模型是为了帮助底层数据模型到公共视图模型的转换。私有CDS视图的名字前缀是P_。不建议修改或扩展它们。
接口视图
接口视图(Interface views)是VDM最重要的组成部分。接口视图组成了可复用的entity视图,在业务语义是定义上的重点。接口视图对任何消费者而言都是公开的、稳定的并且可复用的。公共接口视图的结构需要不受correction、补丁和升级的影响。
接口视图分两种:
基本接口视图(Basic interface Views)代表没有数据冗余的核心entity,即对每个核心entity做单一的表示,(比如,正好一个客户或者销售订单)并且只带有依赖于核心entity自身的、无法从其他字段计算得到的字段。基本视图组成了低冗余模型和SAP Business Suite数据库表的简单投影。
组合接口视图(Composite Interface Views)继承自多个简单接口视图,也许会有关联、聚合和复杂计算。它们可以属于特定消费域,或者被复用,根据设计,它们总是暴露冗余的数据。按照实际的使用情况,组合接口视图可以分为很多层。
消费视图
如名字所示,消费视图(Consumption Views)是暴露给终端用户消费的视图。消费视图会使用到一个或更多接口视图。消费视图是共用的特定领域视图,用于分析、搜索和事务应用。VDM建模的指导原则是,数据库表一定不可以被消费视图直接访问(即不可以绕过接口视图)。
扩展包含视图
扩展包含视图(Extension include Views)用于暴露自定义新字段。可以扩展SAP发布的扩展包含视图,以条件附加字段。自定义扩展包含视图会在单独的DDL源中创建和传输。
总之,在高层上,CDS视图从数据库表得到数据,这些视图又会被其它CDS视图读取,VDM就是由所有的这些CDS视图组成。没有任何持久化过程,一切都是实时的。
Annotation
Annotation用于描述CDS视图,为CDS视图中的字段提供语义和意义。
- 它可以应用在整个CDS视图entity上;
- 它可以指定SELECT列表中特定的字段的语义;
- 总是在@符号后面
这面是annotation的列表:https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/abencds_annotations_sap.htm
也可以通过以下路径访问:ABAP – Keyword Documentation > ABAP – Dictionary > ABAP CDS in ABAP Dictionary > ABAP CDS – Syntax > ABAP CDS – Annotations
下面是用于VDM的定义整个CDS视图的一些关键的annotation:
Annotation | Description |
VDM.viewType | 定义VDM视图的类型 |
Analytics.dataCategory | 分析查询可以在CDS视图之上定义。通过指定数据分类,开发者可以提供指令和hints,告诉analytic manager如何解释各个实体。 |
Analytics.dataExtraction.enabled | 应用开发者通过该annotation标记适合于数据复制的视图(例如,必须为海量数据提供增量复制能力)。 |
Analytics.query | 通过标记CDS视图,开发者可以指定暴露给analytic manager的视图。这类视图会被analytic manager解释为分析查询。 |
ObjectModel.dataCategory | 定义数据分类(#TEXT 或 #HIERARCHY) |
ObjectModel.representativekey | Most specific element (field or managed association) of the primary key (indicated by the keyword KEY) that represents the entity which the view is based on |
AccessControl.authorizationCheck | 针对指定的CDS视图启用行级别权限控制 |
/名词解释:analytic manager是一个BW概念,它提供OLAP功能和服务,以及BW集成规划和分析过程设计的服务。
因为我还是个BW开发者,我找了几个最重要的VDM的annotation来和BW对象对比:
Annotation: @VDM.viewType
Value | Description |
BW 对等物 |
#BASIC | 组成核心基础的视图,无数据荣誉,它只是从数据库物理表的SELECT。 | 等于ADSO(高级数据存储对象),即通过某些ETL后的raw data。 |
#COMPOSITE | 提供从基础视图继承和/或组合而成的数据 | 等同于Composite Provider,是允许进行join和union的虚拟数据层。 |
#CONSUMPTION | 为特定应用目的服务的视图,也许会基于公共接口视图定义(比如BASIC 视图或COMPOSITE视图) | 等于BW Query,可以指定特殊的样式,变量,RKF,CKF,总计,等等。 |
Annotation: @Analytics.dataCategory
Value | Description | BW 对等物 |
#DIMENSION | 表示主数据的视图需要有annotation:ObjectModel.dataCategory: #DIMENSION | 等于Infoobject Attributes |
#FACT | 事实表表示业务数据(星型模型的中心)通常包含度量。这些视图通常是复制所必须的,因此,他们不能和主数据视图join。 | 等于从一个单一数据源加载的ADSO,没有任何主数据。 |
#CUBE | CUBE也代表事实数据,和FACT很像,但是CUBE不必是无冗余的。这意味着它可以和主数据join。查询基本都是构建在CUBE之上的,CUBE从FACT复制数据。 | 等于从单一/多个数据源加载的ADSO,和主数据的attributes/texts/hiererachies连接。 |
Annotation: @Analytics.dataExtraction.enabled
Value | Description | BW 对等物 |
#TRUE | 应用开发者用这个annotation表示视图适合于数据复制。(比如对于大量数据必须提供增量复制能力) | N/A |
#FALSE | 不适合数据复制 | N/A |
Annotation: @ObjectModel.dataCategory
Value | Description | BW 对等物 |
#TEXT |
表示该entity代表文本,通常一个key元素是语言。 NOTE: 在VDM中,文本视图总是独立于语言的。 |
等于 infoobject Texts/descriptions |
#HIERARCHY | 表示该entity代表层次相关的数据。可以是header相关的数据或者结构信息。 | 等于 infoobject Hierarchy |
Annotation: @AccessControl.authorizationCheck
Value | Description | BW 对等物 |
#NOT_REQUIRED | 不存在针对该视图的DCL(行级别安全对象),所以不会对该CDS视图进行权限检查,所有数据都会被展示。 | N/A |
#CHECK | 存在针对该视图的DCL(行级别安全对象),会对该CDS视图进行行级别权限检查。 | 有点像 BW authorizations |
#NOT_ALLOWED | 存在针对该视图的DCL(行级别安全对象),但是不会进行行级别的权限检查。 | N/A |
现在我们看过了CDS的主要annotation,我们将会进一步审视VDM的详细架构。
注意这些概念和BW环境中的那些有多么相似。我们在CDS中有文本和维度,我们可以一次构建、多次在不同的事务性CDS视图中重用它们。
假设这是在构建一个物料维度和文本视图。 你只需要建立它一次,因为基础表不会改变(MARA和MAKT)。 但是,对于需要物料描述或属性的各个事务模型(销售,交货,盈利分析,库存等),你可以重复利用该维度和文本视图。
例子
下面是有关上述各个CDS视图的细节:
TEXT
@AbapCatalog.sqlViewName: 'ZBTMATERIAL'
@ObjectModel.dataCategory: #TEXT
@Analytics: { dataCategory: #TEXT, dataExtraction.enabled: true }
@AccessControl.authorizationCheck: #NOT_REQUIRED
@VDM.viewType: #BASIC
@EndUserText.label: 'Material Text'
@ObjectModel.representativeKey: 'Material' define view Zbt_Material as
select from makt
{
@ObjectModel.text.element: [ 'MaterialName' ]
key makt.matnr as Material,
@Semantics.language: true
key makt.spras as Language,
@Semantics.text: true
makt.maktx as MaterialName} where makt.spras = $session.system_language
DIMENSION
@AbapCatalog.sqlViewName: 'ZBDMATERIAL'
@Analytics: { dataCategory: #DIMENSION, dataExtraction.enabled: true }
@VDM.viewType: #BASIC
@AbapCatalog.compiler.compareFilter: true
@EndUserText.label: 'Material Attributes'
@ObjectModel.representativeKey: 'Material'
@AccessControl.authorizationCheck: #NOT_REQUIRED define view Zbd_Material as select from mara association [0..1] to Zbt_Material as _Text on $projection.Material = _Text.Material
association [0..1] to Zbd_MaterialType as _MaterialType on $projection.MaterialType = _MaterialType.MaterialType
association [0..1] to Zbd_MaterialGroup as _MaterialGroup on $projection.MaterialGroup = _MaterialGroup.MaterialGroup
association [0..1] to I_UnitOfMeasure as _BaseUnit on $projection.MaterialBaseUnit = _BaseUnit.UnitOfMeasure
association [0..1] to I_UnitOfMeasure as _WeightUnit on $projection.MaterialWeightUnit = _WeightUnit.UnitOfMeasure
association [0..1] to Zbt_Storage_Conditions as _StorCond on $projection.StorageCondition = _StorCond.StorageCond { @EndUserText.label: 'Material'
@ObjectModel.text.association: '_Text'
key mara.matnr as Material, _Text, @ObjectModel.foreignKey.association: '_MaterialType'
@EndUserText.label: 'Material Type'
mara.mtart as MaterialType, _MaterialType,
@ObjectModel.foreignKey.association: '_MaterialGroup'
@EndUserText.label: 'Material Group'
mara.matkl as MaterialGroup, _MaterialGroup,
@EndUserText.label: 'Base Unit of Measure'
@Semantics.unitOfMeasure: true
@ObjectModel.foreignKey.association: '_BaseUnit'
mara.meins as MaterialBaseUnit, _BaseUnit,
@EndUserText.label: 'Gross Weight'
@Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
@DefaultAggregation: #NONE
mara.brgew as MaterialGrossWeight,
@EndUserText.label: 'Net Weight'
@Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
@DefaultAggregation: #NONE
mara.ntgew as MaterialNetWeight,
@EndUserText.label: 'Weight Unit'
@Semantics.unitOfMeasure: true
@ObjectModel.foreignKey.association: '_WeightUnit'
mara.gewei as MaterialWeightUnit, _WeightUnit, mara.mfrnr as MaterialManufacturerNumber,
mara.mfrpn as MaterialManufacturerPartNumber,
@EndUserText.label: 'Storage Condition'
@ObjectModel.text.association: '_StorCond'
mara.raube as StorageCondition, _StorCond,
@EndUserText.label: 'Product Hierarchy'
mara.prdha as ProductHierarchy }
BASIC/FACT
注意,在选择列表中,我们没有为字段添加任何语义(annotation),因为这是一个BASIC视图,而且我们在创建模型的FACT。语义annotation会在COMPOSITE视图中应用。
VDM可以根据需要由多个BASIC/FACT视图组成。
@AbapCatalog.sqlViewName: 'ZBFACDOCAXX'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Universal Journal Entry, Basic'
@VDM.viewType: #BASIC
@Analytics.dataCategory: #FACT
@Analytics.dataExtraction.enabled: true define view ZBF_ACDOCA_XX as select from acdoca {
rbukrs as CompCode,
gjahr as FiscalYear,
poper as Period,
racct as GLAccount,
matnr as Material,
werks as Plant, // UOMs - Currencies
runit as UOM,
rhcur as CCCurr, // Measures
msl as Quantity,
hsl as AmtCC
}
COMPOSITE/CUBE
@AbapCatalog.sqlViewName: 'ZCCACDOCAXX'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Universal Journal Entry, Composite'
@VDM.viewType: #COMPOSITE
@Analytics.dataCategory: #CUBE
@Analytics.dataExtraction.enabled: true define view ZCC_ACDOCA_XX as select from ZBF_ACDOCA_XX association [0..1] to I_Material as _Mat on $projection.Material = _Mat.Material
association [0..1] to I_Plant as _Plant on $projection.Plant = _Plant.Plant
association [0..1] to I_CompanyCode as _CompCode on $projection.CompCode = _CompCode.CompanyCode {
@ObjectModel.foreignKey.association: '_CompCode'
CompCode, _CompCode,
FiscalYear,
Period,
GLAccount,
@ObjectModel.foreignKey.association: '_Mat'
Material, _Mat,
@ObjectModel.foreignKey.association: '_Plant'
Plant, _Plant,
//UOMs - Currencies
@Semantics.unitOfMeasure: true
@EndUserText.label: 'Base UOM'
UOM,
@Semantics.currencyCode: true
@EndUserText.label: 'Comp. Code Curr.'
CCCurr,
//Measures
@DefaultAggregation: #SUM
@EndUserText.label: 'Quantity'
@Semantics.quantity.unitOfMeasure: 'UOM'
Quantity,
@DefaultAggregation: #SUM
@EndUserText.label: 'Amount CC'
@Semantics.amount.currencyCode: 'CCCurr'
AmtCC
}
CONSUMPTION
@AbapCatalog.sqlViewName: 'ZCCACDOCAXXQ001'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Universal Journal Entry, Query'
@VDM.viewType: #CONSUMPTION
@Analytics.query: true
define view ZCC_ACDOCA_XX_Q001 as select from ZCC_ACDOCA_XX {
//ZCC_ACDOCA_XX
@Consumption.filter: {mandatory: false, selectionType: #SINGLE, multipleSelections: true}
CompCode,
FiscalYear,
Period,
GLAccount,
Material,
@AnalyticsDetails.query.display: #KEY_TEXT
@AnalyticsDetails.query.axis: #ROWS
Plant, //UOMs - Currencies
UOM,
CCCurr, //Measures
Quantity,
AmtCC }
优势
VDM的主要优势之一是只要构建一次即可在多种前端工具中消费:
- BOBJ
- WEBI
- Analysis for Office
- Crystal
- OLAP
- Lumira Discovery (even though it's being discontinued in favor of Analytics Cloud)
- Analytics Cloud
- ALV Grid
- OData services
- Fiori
此外,因为CDS视图存在于应用层,我们可以通过PFCG借力于既有的ABAP安全模型。对于CDS视图安全,我们可以使用Data Control Language (DCL)来控制。有关DCL的更多信息,可以看以下文章:
一旦你学会了如何用CDS视图建模,开发VDM的速度会变得很快,可以借此为用户提供快速、高效的报表解决方案。
性能
关于性能,VDM在高容量高聚合的场景的表现是惊人的。我构建了一个采购订单视图,从header到计划行,有许多主数据的join。计划行表有大概2500万数据,数据时间跨越了8年。
当运行没有过滤的汇总报表时,列上有3个度量而下钻时只有年度(8行,3列,24个数据点),报表在1-2秒内完成查询!
现在,使用和上面相同的数据模型,当我运行一个非常详细的报表时,查询到计划行项目,获取许多不同的属性,下钻大概30行,获取整个月份的数据。这时报表大概花了10分钟来运行。为什么?因为它实时进行了我在模型中定义过的所有join和计算,处理上十分密集。
上面的场景十分适合采用BW的持久化解决方案,它可以进行任何计算和数据连接。报表运行时,数据持久化为ADSO,可以简单地基于选择条件获取数据,不需要任何附加处理,预期的查询时间将短至亚秒级。
本文翻译自以下文章:
S/4 Embedded Analytics – The Virtual Data Model
参考文章: