参考blog

1,Create an analytical model based on ABAP CDS views
https://blogs.sap.com/2018/03/18/create-an-analytical-model-based-on-abap-cds-views/

2,Create a Multi-Dimensional Report from an Analytical Query
https://developers.sap.com/tutorials/s4hana-kut-create-multi-dimensional-report.html

3,Define the Analytical Query CDS View
https://blogs.sap.com/2017/02/26/step-2-define-the-analytical-query-cds-view/

4,How to create custom Fiori Multidimensional Reporting Application in S/4HANA on-premise
https://blogs.sap.com/2018/07/11/how-to-create-custom-fiori-multidimensional-reporting-application-in-s4hana-on-premise/


 

数据处理大致可以分成两大类:联机事务处理OLTP(on-line transaction processing)、联机分析处理OLAP(On-Line Analytical Processing)。
OLTP是传统的关系型数据库的主要应用,主要是基本的、日常的事务处理,例如银行交易。OLAP是数据仓库系统的主要应用,支持复杂的分析操作,侧重决策支持,并且提供直观易懂的查询结果。
https://www.cnblogs.com/beyondstorm/archive/2006/08/12/475011.html

SAP HANA将OLAP和OLTP处理结合在一个内存数据库中,事务和分析可以同时存在,这意味着可以实时访问所需的准确信息。因为我们将事务和分析世界合并在一个数据库中,所以在基于ABAP CDS(核心数据服务)构建的数据模型时,开发的方式会有一些不同。

 


 

当您需要利用聚合的优势跨不同区域(例如按时间、按位置、按负责人)公开结果时,建议使用分析模型进行报告。这些模型建立在事实(Facts )和维度(Dimensions )之上,这些视图包含用于进行详细分析和获得业务价值的基本数据。

以销售报告为例,它提供基于客户、产品、日期和销售人员的结果。事实(Fact )就是销售本身,它包含我们可以测量的价值(例如销售额和总销售额),根据客户、产品、时间和销售人员的过滤器是维度(Dimensions ),这些维度可以附加属性(Attributes )或文本(Texts )(例如客户名称、地址和产品描述),当我们将所有这些维度连接起来时,我们就有了一个多维数据集(Cube ),从而准备了一个分析模型用于消费。

在这个分析模型的基础上,我们需要构造一个查询(Query ),以我们希望向用户公开的方式调整数据。多维数据集必须能够被一些不同的查询重用和使用。
例如,使用上面的销售模型,我们可以在不同的查询中生成以下指标:

  • Sales by year quarter;
  • Sales by products with cost above $100;
  • Sales by customer located in a specific city;
  • Average of sales amount per number of sales;
  • Uplift on sales from prior year.

每个查询将实现特定的目的,并且可以针对不同的应用程序(例如报告、kpi等)进行设计。

 


 

 

CDS的主要目的之一是允许创建语义丰富的数据模型,而注释是支持这项任务的主要组件。

有许多不同领域的注解,可以通过下面的link进行查看。(注解太难了。资料太少了。)
https://help.sap.com/viewer/cc0c305d2fab47bd808adcad3ca7ee9d/7.5.9/en-US/630ce9b386b84e80bfade96779fbaeec.html

使用abap cds view来创建分析模型,需要使用到cds view的注解:Analytics、AnalyticsDetails。
Analytics 提供了对ABAP CDS视图的支持,并支持利用数据聚合来使用多维数据。
AnalyticsDetails支持通过更改聚合行为、计划或公式来计算度量,从而调整分析查询布局。只能在@Analytics.query : true的情况下使用。

如果想要声明维度、事实、聚合级别或多维数据集,必须在CDS视图的头部包含以下注释:

@Analytics.dataCategory: #VALUE

可以将 #VALUE 替换成下面的项目

  • #CUBE
  • #AGGREGATIONLEVEL
  • #DIMENSION
  • #FACT

因为Cubes必须包含至少一个可度量的属性,要将字段定义为可度量的,需要将此注释放在字段的顶部:

@DefaultAggregation: #SUM

也可以更改默认聚合,最常见的组合是使用 #SUM 进行聚合

另外需要使用以下注解

@Analytics.query: true

因为定义了聚合模式,查询必须从多维数据集中(cubes)选择数据,否则会返回错误。

 


 

 我们以下面的数据模型,进行开发。


创建各个维度的cds view。

1,创建package

2,创建cdsview



DIMENSION: Airline

@AbapCatalog.sqlViewName: 'ZDIMEAIRLINE'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Airline'

@Analytics.dataCategory: #DIMENSION

define view Z_Dimension_Airline
  as select from scarr
{
      @ObjectModel.text.element: [ 'AirlineName' ]
  key carrid   as Airline,
  
      @Semantics.text: true
      carrname as AirlineName,
      
      @Semantics.currencyCode: true
      currcode as Currency
} 

DIMENSION: Connection

@AbapCatalog.sqlViewName: 'ZDIMECONNECT'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Flight Connection'

@Analytics.dataCategory: #DIMENSION

@ObjectModel.representativeKey: 'FlightConnection'

define view Z_Dimension_Connection
  as select from spfli
  association [0..1] to Z_Dimension_Airline as _Airline on $projection.Airline = _Airline.Airline
{
      @ObjectModel.foreignKey.association: '_Airline'
  key carrid                    as Airline,

      @ObjectModel.text.element: [ 'Destination' ]
  key connid                    as FlightConnection,

      @Semantics.text: true
      concat(cityfrom,
        concat(' -> ', cityto)) as Destination,

      _Airline
} 

DIMENSION: Customer

@AbapCatalog.sqlViewName: 'ZDIMECUSTOMER'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Flight Customer'

@Analytics.dataCategory: #DIMENSION

define view Z_Dimension_Customer
  as select from scustom
  association [0..1] to I_Country as _Country on $projection.Country = _Country.Country
{
      @ObjectModel.text.element: [ 'CustomerName' ]
  key id      as Customer,

      @Semantics.text: true
      name    as CustomerName,

      @ObjectModel.foreignKey.association: '_Country'
      @Semantics.address.country: true
      country as Country,

      @Semantics.address.city: true
      city    as City,
      
      _Country
} 

DIMENSION: Travel Agency

@AbapCatalog.sqlViewName: 'ZDIMETRVAGENCY'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Travel Agency'

@Analytics.dataCategory: #DIMENSION

define view Z_Dimension_TravelAgency
  as select from stravelag
  association [0..1] to I_Country as _Country on $projection.Country = _Country.Country
{
      @ObjectModel.text.element: [ 'TravelAgencyName' ]
  key agencynum as TravelAgency,

      @Semantics.text: true
      name      as TravelAgencyName,

      @ObjectModel.foreignKey.association: '_Country'
      @Semantics.address.country: true
      country   as Country,

      @Semantics.address.city: true
      city      as City,
      
      _Country
} 

1. @AbapCatalog.sqlViewName:指定sqlview的名称,可以在se11中查询到。
2.@AbapCatalog.compiler.compareFilter:在cdsview的路径表达式中定义筛选条件的计算。
3.@AccessControl.authorizationCheck:使用DCL进行权限的检查。
4. @EndUserText.label:元素的短文本。
5. @Analytics.dataCategory: #DIMENSION:所有的维度有需要有该分类。
6. @ObjectModel.text.element:与文本名称进行关联。
7. @ObjectModel.foreignKey.association:外部属性关联,使用该注解。
8. @ObjectModel.representativeKey:组合主键,定义一个代表键。
9. @Semantics:帮助定义字段类型。

准备好所有的维度之后,创建cube。
CUBE: Flight Bookings

@AbapCatalog.sqlViewName: 'ZCUBEFLIGHTBOOK'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Flight Bookings'

@Analytics.dataCategory: #CUBE

define view Z_Cube_FlightBookings
  as select from sbook
  association [0..1] to I_CalendarDate           as _CalendarDate on  $projection.FlightDate = _CalendarDate.CalendarDate
  association [0..1] to Z_Dimension_Airline      as _Airline      on  $projection.Airline = _Airline.Airline
  association [0..1] to Z_Dimension_Connection   as _Connection   on  $projection.Airline          = _Connection.Airline
                                                                  and $projection.FlightConnection = _Connection.FlightConnection
  association [0..1] to Z_Dimension_Customer     as _Customer     on  $projection.Customer = _Customer.Customer
  association [0..1] to Z_Dimension_TravelAgency as _TravelAgency on  $projection.TravelAgency = _TravelAgency.TravelAgency
{
  /** DIMENSIONS **/

  @EndUserText.label: 'Airline'
  @ObjectModel.foreignKey.association: '_Airline'
  carrid                 as Airline,

  @EndUserText.label: 'Connection'
  @ObjectModel.foreignKey.association: '_Connection'
  connid                 as FlightConnection,

  @EndUserText.label: 'Flight Date'
  @ObjectModel.foreignKey.association: '_CalendarDate'
  fldate                 as FlightDate,

  @EndUserText.label: 'Book No.'
  bookid                 as BookNumber,

  @EndUserText.label: 'Customer'
  @ObjectModel.foreignKey.association: '_Customer'
  customid               as Customer,

  @EndUserText.label: 'Travel Agency'
  @ObjectModel.foreignKey.association: '_TravelAgency'
  agencynum              as TravelAgency,

  @EndUserText.label: 'Flight Year'
  _CalendarDate.CalendarYear,

  @EndUserText.label: 'Flight Month'
  _CalendarDate.CalendarMonth,

  @EndUserText.label: 'Customer Country'
  @ObjectModel.foreignKey.association: '_CustomerCountry'
  _Customer.Country      as CustomerCountry,

  @EndUserText.label: 'Customer City'
  _Customer.City         as CustomerCity,

  @EndUserText.label: 'Travel Agency Country'
  @ObjectModel.foreignKey.association: '_TravelAgencyCountry'
  _TravelAgency.Country  as TravelAgencyCountry,

  @EndUserText.label: 'Travel Agency Customer City'
  _TravelAgency.City     as TravelAgencyCity,

  /** MEASURES **/

  @EndUserText.label: 'Total of Bookings'
  @DefaultAggregation: #SUM
  1                      as TotalOfBookings,

  @EndUserText.label: 'Weight of Luggage'
  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'WeightUOM'
  luggweight             as WeightOfLuggage,

  @EndUserText.label: 'Weight Unit'
  @Semantics.unitOfMeasure: true
  wunit                  as WeightUOM,

  @EndUserText.label: 'Booking Price'
  @DefaultAggregation: #SUM
  @Semantics.amount.currencyCode: 'Currency'
  forcuram               as BookingPrice,

  @EndUserText.label: 'Currency'
  @Semantics.currencyCode: true
  forcurkey              as Currency,

  // Associations
  _Airline,
  _CalendarDate,
  _CalendarDate._CalendarMonth,
  _CalendarDate._CalendarYear,
  _Connection,
  _Customer,
  _Customer._Country     as _CustomerCountry,
  _TravelAgency,
  _TravelAgency._Country as _TravelAgencyCountry
} 

1. 不用必须使用cube去构造fact数据集,可以直接公开在cube中定义的表,来避免使用过多的层级。fact视图不能有连接或者关联,只包含可度量的值,如果要连接维度,要使用cube而不是fact。
2. @Analytics.dataCategory: #CUBE:所有的cube需要使用该分类。
3. @DefaultAggregation:该字段用来进行度量。
4. 所有关联都暴露在视图的底部,以便在查询使用期间提供对属性和文本的访问。

使用tcode(RSRTS_ODP_DIS)。该事务用于检查放置在分析数据模型中的关联、文本和层次结构,为每个属性提供详细的分析。
将创建的sqlviewname放入odp name中,点击执行。

 

 

消费view,进行query

QUERY: Flight Bookings

@AbapCatalog.sqlViewName: 'ZQUERYFLIGHTBOOK'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Flight Bookings'

@Analytics.query: true
@VDM.viewType: #CONSUMPTION

define view Z_Query_FlightBookings
  as select from Z_Cube_FlightBookings
{
    /** DIMENSIONS **/
    
    @AnalyticsDetails.query.display: #KEY_TEXT
    @AnalyticsDetails.query.axis: #FREE
    Airline, 
    @AnalyticsDetails.query.display: #KEY_TEXT
    @AnalyticsDetails.query.axis: #FREE
    FlightConnection, 
    @AnalyticsDetails.query.display: #KEY
    @AnalyticsDetails.query.axis: #FREE
    FlightDate, 
    @AnalyticsDetails.query.display: #KEY_TEXT
    @AnalyticsDetails.query.axis: #FREE
    Customer, 
    @AnalyticsDetails.query.display: #KEY_TEXT
    @AnalyticsDetails.query.axis: #FREE
    TravelAgency, 
    @AnalyticsDetails.query.display: #KEY
    @AnalyticsDetails.query.axis: #FREE
    CalendarYear,
    @AnalyticsDetails.query.display: #TEXT
    @AnalyticsDetails.query.axis: #FREE
    CalendarMonth,
    @AnalyticsDetails.query.display: #TEXT
    @AnalyticsDetails.query.axis: #FREE
    CustomerCountry,
    @AnalyticsDetails.query.display: #KEY
    @AnalyticsDetails.query.axis: #FREE
    CustomerCity,
    @AnalyticsDetails.query.display: #TEXT
    @AnalyticsDetails.query.axis: #FREE
    TravelAgencyCountry,
    @AnalyticsDetails.query.display: #KEY
    @AnalyticsDetails.query.axis: #FREE
    TravelAgencyCity,
    @AnalyticsDetails.query.display: #KEY
    @AnalyticsDetails.query.axis: #FREE
    Currency,
    @AnalyticsDetails.query.display: #KEY
    @AnalyticsDetails.query.axis: #FREE
    WeightUOM,
    
    /** MEASURES **/
    
    TotalOfBookings, 
    WeightOfLuggage,
    BookingPrice,
    
    @EndUserText.label: 'Average Weight Per Flight'
    @AnalyticsDetails.exceptionAggregationSteps.exceptionAggregationBehavior: #AVG
    @AnalyticsDetails.exceptionAggregationSteps.exceptionAggregationElements: [ 'Airline', 'FlightConnection', 'FlightDate' ]
    @AnalyticsDetails.query.formula: '$projection.WeightOfLuggage'
    @AnalyticsDetails.query.decimals: 0
    0 as AverageWeightPerFlight
} 

可以用一般的sql语句对view进行查询。
SELECT SUM("Quantity") FROM "MyProducts" WHERE "Product_Name" IN ('Jackets', 'Coats');

1. 所有的query需要设置@Analytics.query: true
2. @AnalyticsDetails用于进行查询,提供以下功能:
  @AnalyticsDetails.query.display: #KEY_TEXT:设置显示key还是text。i.e EmployeeName for Employee.            
  @AnalyticsDetails.query.axis:定位元素的轴
  @AnalyticsDetails.exceptionAggregationSteps.exceptionAggregationBehavior:进行例外聚合运算
  @AnalyticsDetails.exceptionAggregationSteps.exceptionAggregationElements:进行例外聚合运算时,使用的列
  @AnalyticsDetails.query.formula:计算公式
  @AnalyticsDetails.query.decimals:小数点位数

3,测试查询结果

登陆friori管理页面,建立tile与对应的target

tile

 

target

 

Semantic Object、action要与tile的值设置为一致。
URL、ID需要指定为固定的值。
参数中的,XQUERY,需要设置为上面实现的sqlviewname加前缀2C。XSYSTEM需要设置为LOCAL,我感觉是需要与segw中的service maintenance中设置的一致。
其他的一些参数可以参照最上面的blog。

之后登陆fiori launchpad点击tile进入。

 

posted on 2019-10-16 17:00  suoluo119  阅读(2045)  评论(0编辑  收藏  举报