使用ABAP CDS views 創建一個分析模型


參考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進入。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM