2022年9月

 

本文阐述了ABAP CDS association的概念,并且展示了在CDS视图中和SQL语句中写路径表达式(Path Expression)代码的方法。我也会解释如何在CDS asociation中指定inner join——默认情况下是left outer join,以及如何为association添加过滤。

 

对于CDS的相关开发,SAP希望我们使用association而不是join,因为association更加接近“概念思维”。基本上,association本身不是join,它只是有关join连接可能性的元数据,它会按需成为join。真实的join会在路径表达式使用association的时候被创建。

 一个简单的CDS association例子,它看起来和left outer join没区别:

@AbapCatalog.sqlViewName: 'ZCDS_ASSOC11'defineview zcds_assoc1 as select from scarr assca
association
[0..1] to spfli as_spflion sca.carrid =_spfli.carrid
{
* }

  在本文中,我会通过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连接。

使用自开发程序来处理业务逻辑时,处理过程通常是个黑箱,业务顾问和业务用户不知道程序的具体运行方式,要依赖文档和频繁的沟通来确认实际情况。

BRFplus可以通过配置的方式实现业务逻辑,使得业务人员把业务逻辑的实现掌握在自己手中,此外,跟踪(tracing)功能的存在使得业务逻辑应用的执行情况也变得清晰可见。

 

本文链接:https://www.cnblogs.com/hhelibeb/p/9556478.html

目的

跟踪模式有以下用处:

  • 有助于找到BRF+应用运行结果与预期不一致的原因。
  • 统计各规则的使用情况,从而理解规则调整的影响和风险。
  • 统计输出结果的分布情况。

跟踪信息可以帮助人们进一步理解业务的实际执行情况,确定哪些场景是常见的、哪些是偶然的甚至永不出现的,从而进一步优化业务逻辑实现。

实现

虽然跟踪模式可以服务业务,但是因为BRF+应用需要通过ABAP代码来调用,所以实现部分会是和ABAP相关的内容。

我创建了一个简单的BRF+应用,其功能是根据输入的采购订单编号,到数据库表EKKO中查询采购组和采购订单类型,根据这两个字段的组合,来决定是否需要审批。涉及到2个表达式,1个是数据库查找(DB lookup),还有一个是决策表(decision table)

调用

ABAP调用代码,

REPORTztest_brf3.PARAMETERS: p_ebeln TYPEebeln.START-OF-SELECTION.*获取function实例
DATA(lo_fuction) =CAST cl_fdt_function(
cl_fdt_factory
=>if_fdt_factory~get_instance(
)
->get_function( '005056A4CCA61ED8AAF183894A92CC2B') ).*获取context实例 DATA(lo_context) =CAST cl_fdt_context(
lo_fuction
->if_fdt_function~get_process_context( ) ).*将将采购订单号输入到context lo_context->if_fdt_context~set_value(
: iv_name
= 'EBELN' ia_value =p_ebeln ) .*处理,获取结果和跟踪数据 lo_fuction->if_fdt_function~process(EXPORTING io_context =lo_context
iv_trace_mode
= if_fdt_constants=>gc_trace_mode_leanIMPORTING eo_result = DATA(lo_result)
eo_trace
= DATA(lo_trace) ).

开发者们可以在编程中使用各种动态技术,比如RTTS,比如通过动态的类创建和多态来实现功能的平滑扩展。BRF+开发中也存在一些动态手段。本文将介绍3种不同场景下的动态实践方式。其中第一种是纯配置的,第二和第三种是编程相关的。

 

本文链接:https://www.cnblogs.com/hhelibeb/p/9571707.html

我的BRF+教程系列:https://www.cnblogs.com/hhelibeb/tag/BRFplus/

 

1,动态表达式(Dynamic Expression)

动态表达式是一种特殊类型的表达式,它可以用来实现对其它表达式的动态调用。动态表达式的应用场景之一是,有很多个表达式可以供用户选择,这些表达式使用基本一样的上下文对象,你不希望为每个表达式创建一个函数(因为创建它们的过程太繁琐),而是希望在一个统一的入口函数去调用。

1)选项

  • 被调用的表达式(Called Expression):选择一个表达式,将它分配给动态表达式。被分配的表达式会在运行期间被动态表达式调用。注意,虽然被称为被调用的表达式,但是它并不是被动态调用的表达式,它的返回结果才是被动态调用的表达式的ID。Called Expression可以处理动态表达式的上下文,结果中必须包含一个表达式的ID,该表达式的结果会被传回给动态表达式。
  • 结果数据对象(Result Data Object:)结果数据对象可以使用任何种类的对象。然而,很重要的一点是确保第二个被调用的表达式的结果数据对象和动态表达式的结果数据对象是兼容的。

2)限制

动态表达式可以提供灵活性,但它也有缺陷,因为动态表达式类型的特质,使得某些大部分可以应用在其它BRF+表达式上的检查无法应用在动态表达式上面。所以这可能导致一些设计缺陷无法被检查出来,只有在运行期间才会暴露。此外,动态表达式是不支持代码生成模式的,只能使用解释模式,这使得它的性能不太好,在对时间要求高或数据负载量大的时候不建议使用它。

3)例子

实际操作部分比较简单,读完上面的文字之后应该可以很顺利地出来,这里就不再一图一图地贴了。可以参考官方文档

2,动态创建决策表(Decision Table)

通过使用ABAP代码,可以动态地创建BRFplus中的decisiong table和function等对象。

(本节的内容基本来自于Create decision table & it’s entries dynamically in BRF + Workbench through API

1)前提

要读懂本节,需要两方面的知识,

  1. 基本的ABAP面向对象知识。
  2. 基本的BRF+知识。

2)需求

通过一个简单的report程序来创建决策表和它的条目。

3)使用的接口

  1. IF_FDT_FACTORY
  2. IF_FDT_DECISION_TABLE
  3. IF_FDT_FUNCTION

4)创建应用

在工作台中创建一个应用,接下来将会在这个应用中动态地创建决策表。

5)复制代码

把以下代码粘贴到自定义程序中,代码的具体意义写在注释中,注意ID要替换成你自己创建的BRF+应用的ID

DATA: lo_factory        TYPE REF TOif_fdt_factory,
lt_message
TYPE if_fdt_types=>t_message,
lv_message
TYPE string,
lv_boolean
TYPEabap_bool,
lo_element
TYPE REF TOif_fdt_element,
lo_table
TYPE REF TOif_fdt_table,
lo_structure
TYPE REF TOif_fdt_structure,
lv_element1_id
TYPE if_fdt_types=>id,
lv_element2_id
TYPE if_fdt_types=>id,
lv_element3_id
TYPE if_fdt_types=>id,
lv_structure_id
TYPE if_fdt_types=>id,
lo_constant
TYPE REF TOif_fdt_constant,
ls_element
TYPE if_fdt_structure=>s_element,
lts_element
TYPE if_fdt_structure=>ts_element,
lv_string
TYPE string,
ls_range
TYPE if_fdt_decision_table=>s_range,
ls_table_data
TYPE if_fdt_decision_table=>s_table_data,
lo_decision_table
TYPE REF TOif_fdt_decision_table,
lo_function
TYPE REF TOif_fdt_function,
lo_context
TYPE REF TOif_fdt_context,
lts_context_id
TYPE if_fdt_types=>ts_object_id,
lts_table_data
TYPE if_fdt_decision_table=>ts_table_data,
lts_column
TYPE if_fdt_decision_table=>ts_column,
lv_actv_failed
TYPEabap_bool,
lx_fdt
TYPE REF TOcx_fdt,
lv_dt_id
TYPE if_fdt_types=>id,
ls_column
LIKE LINE OFlts_column.FIELD-SYMBOLS: <ls_message> TYPE if_fdt_types=>s_message,<lv_value> TYPEany.*获取FDT工厂实例,使用上面创建的应用IDlo_factory= cl_fdt_factory=>if_fdt_factory~get_instance('005056A4CCA61ED8AA9AAF84A7712616' ).

*创建数据对象lo_element ?= lo_factory->get_data_object(
iv_data_object_type
= if_fdt_constants=>gc_data_object_type_element ).

lo_element
->if_fdt_transaction~enqueue( ).

lo_element
->if_fdt_admin_data~set_name( 'IV_VAR1').

lo_element
->set_element_type( if_fdt_constants=>gc_element_type_text ).

lo_element
->if_fdt_transaction~activate(IMPORTINGet_message=lt_message
ev_activation_failed
=lv_boolean ).IF lv_boolean EQabap_true.*如果激活失败,需要处理 lo_element->if_fdt_transaction~dequeue( ).ELSE.

lo_element
->if_fdt_transaction~save( ).

lo_element
->if_fdt_transaction~dequeue( ).*通常需要把ID单独存下来,以便后续操作lv_element1_id= lo_element->mv_id.

ls_element
-position = 1.

ls_element
-element_id =lv_element1_id.APPEND ls_element TOlts_element.ENDIF.INSERT lv_element1_id INTO TABLElts_context_id.*创建另一个元素 lo_element ?= lo_factory->get_data_object(
iv_data_object_type
= if_fdt_constants=>gc_data_object_type_element ).

lo_element
->if_fdt_transaction~enqueue( ).

lo_element
->if_fdt_admin_data~set_name( 'IV_VAR2').*设置元素类型(可以搜索if_fdt_constants=>gc_element_type_* 得到可用元素类型列表 lo_element->set_element_type( if_fdt_constants=>gc_element_type_text ).

lo_element
->if_fdt_transaction~activate(IMPORTINGet_message=lt_message
ev_activation_failed
=lv_boolean ).IF lv_boolean EQabap_true. lo_element->if_fdt_transaction~dequeue( ).ELSE.

lo_element
->if_fdt_transaction~save( ).

lo_element
->if_fdt_transaction~dequeue( ). lv_element2_id= lo_element->mv_id.

ls_element
-position = 2.

ls_element
-element_id =lv_element2_id.APPEND ls_element TOlts_element.ENDIF.INSERT lv_element2_id INTO TABLElts_context_id.*创建结果数据元素 lo_element ?= lo_factory->get_data_object(
iv_data_object_type
= if_fdt_constants=>gc_data_object_type_element ).

lo_element
->if_fdt_transaction~enqueue( ).

lo_element
->if_fdt_admin_data~set_name( 'EV_RESULT').

lo_element
->set_element_type( if_fdt_constants=>gc_element_type_text ).

lo_element
->if_fdt_transaction~activate(IMPORTINGet_message=lt_message
ev_activation_failed
=lv_boolean ).IF lv_boolean EQabap_true. lo_element->if_fdt_transaction~dequeue( ).ELSE.

lo_element
->if_fdt_transaction~save( ).

lo_element
->if_fdt_transaction~dequeue( ). lv_element3_id= lo_element->mv_id.

ls_element
-position = 3.

ls_element
-element_id =lv_element3_id.APPEND ls_element TOlts_element.ENDIF.INSERT lv_element3_id INTO TABLElts_context_id.*填充第1列元素ls_column-col_no = 1.

ls_column
-object_id =lv_element1_id.

ls_column
-is_result =abap_false.INSERT ls_column INTO TABLElts_column.* 填充第2列元素
ls_column-col_no = 2.
ls_column
-object_id =lv_element2_id.
ls_column
-is_result =abap_false.

INSERT ls_column INTO TABLElts_column.

*填充结果列元素
ls_column
-col_no = 3.
ls_column
-object_id =lv_element3_id.
ls_column
-is_result =abap_true.

INSERT ls_column INTO TABLElts_column.

*创建并设置决策表表达式
lo_decision_table ?
= lo_factory->get_expression( iv_expression_type_id= if_fdt_constants=>gc_exty_decision_table ).

*对表达式加锁.
lo_decision_table
->if_fdt_transaction~enqueue( abap_true ).
*设置表列
lo_decision_table
->set_columns( its_column =lts_column ).
lo_decision_table
->if_fdt_admin_data~set_name( 'DT_TEST' ). "user defined name. DT_TEST is the decision table Name

*使用工厂对象创建一个函数实例.
lo_function ?= lo_factory->get_function( ).

*对函数加锁.
lo_function
->if_fdt_transaction~enqueue( ).

*设置函数上下文对象.
lo_function
->set_context_data_objects( lts_context_id ).
lo_function
->if_fdt_admin_data~set_name( 'FN_TEST' ). "自定义函数名

*设置函数根表达式.
lo_function
->set_expression( lo_decision_table->mv_id ).

*设置单元格(1,1)的条件
ls_table_data
-row_no = 1.
ls_table_data
-col_no = 1.
ls_range
-position = 1.
ls_range
-sign = if_fdt_range=>gc_sign_include.
ls_range
-option = if_fdt_range=>gc_option_equal.
CREATE DATA ls_range-r_low_value TYPE if_fdt_types=>element_text.

ASSIGN ls_range-r_low_value->* TO <lv_value>.<lv_value> = 'MOURI'.

INSERT ls_range INTO TABLE ls_table_data-ts_range.

INSERT ls_table_data INTO TABLElts_table_data.

CLEARls_table_data. .

*设置单元格(1,2)的条件
ls_table_data
-row_no = 1.
ls_table_data
-col_no = 2.
ls_range
-position = 1.
ls_range
-sign = if_fdt_range=>gc_sign_include.
ls_range
-option = if_fdt_range=>gc_option_equal.

CREATE DATA ls_range-r_low_value TYPE if_fdt_types=>element_text.

ASSIGN ls_range-r_low_value->* TO <lv_value>.<lv_value> = 'TECH'.

INSERT ls_range INTO TABLE ls_table_data-ts_range.

INSERT ls_table_data INTO TABLElts_table_data.

CLEARls_table_data.**在单元格(1, 3)得到结果
ls_table_data
-row_no = 1. ls_table_data-col_no = 3.

CREATE DATA ls_table_data-r_value TYPE if_fdt_types=>element_text.

ASSIGN ls_table_data-r_value->* TO <lv_value>.<lv_value> = 'MOURITECH'.

INSERT ls_table_data INTO TABLElts_table_data.

CLEARls_table_data.

*设置完全的表数据.
lo_decision_table
->set_table_data( its_data =lts_table_data ).

*保存并激活.
lo_function
->if_fdt_transaction~activate(
  EXPORTING iv_deep        =abap_true
  IMPORTING et_message       =lt_message
        ev_activation_failed
=lv_actv_failed ).

*如果成功,先保存对象。无论成功失败,释放全部锁

IF lv_actv_failed EQabap_true.

  lo_function
->if_fdt_transaction~dequeue( iv_deep =abap_true ).

  WRITE : / 'Deep activation failed'.

  
LOOP AT lt_message ASSIGNING <ls_message>.

    MESSAGE ID <ls_message>-msgid TYPE <ls_message>-msgty NUMBER <ls_message>-msgno
      WITH <ls_message>-msgv1 <ls_message>-msgv2 <ls_message>-msgv3 <ls_message>-msgv4INTOlv_message.
    WRITE: / 'Reason : -',lv_message.

  ENDLOOP.

ELSE.

  TRY.
   lv_dt_id
= lo_decision_table->mv_id.
   lo_function
->if_fdt_transaction~save( iv_deep =abap_true ).

   WRITE : 'The ID of the decision table created is:',lv_dt_id. .

  CATCH cx_fdt INTOlx_fdt.

   WRITE : / 'Save failed with exception'.
   
LOOP AT lx_fdt->mt_message ASSIGNING <ls_message>.
    
WRITE :/ <ls_message>-text.
   ENDLOOP.

  ENDTRY.
  lo_function
->if_fdt_transaction~dequeue( iv_deep =abap_true ).

ENDIF.

S/4 HANA 1809 已经在上月发布,随之而来的是ABAP 7.53。

本文是更新文档中ABAP SQL的部分的翻译。

本次更新的内容较多,主要内容包括:Open SQL更名为ABAP SQL;新函数和表达式;限制移除;table buffer增强;更严格的语法检查规则等。

 

译者水平有限,如有错误之处,请评论指出。

 

本文链接:https://www.cnblogs.com/hhelibeb/p/9848373.html

1,Open SQL的新名字:ABAP SQL

Open SQL已经被更名为ABAP SQL。这个重命名反映出ABAP SQL的某些部分目前只支持特定的数据库平台(SAP HANA数据库),已经不再是全平台独立的了。

2,层次函数

层次函数(Hierarchy functions)是可以在查询中被指定为数据源的表函数(table function)。

3,辅助连接

在一个以SAP HANA为主数据库的ABAP应用服务器上,表DBCON里的辅助连接也应该是一个SAP HANA数据库。除了CONNECTION附加项以外,它也对ABAP SQL和NATIVE SQL生效。(ADBC和EXCE SQL)。

不再需要使用Database Shared Libraries (DBSL),而应使用SAP HANA Smart Data Access (SDA)。在SDA中,辅助数据库使用特殊限定名或使用虚拟表从SAP HANA数据库中寻址。如果将SAP HANA数据库是ABAP服务器主数据库,ABAP程序可以通过标准连接使用这些名字。只能通过AMDP或者Native SQL来使用这一功能。

4,关系表达式 IS INITIAL

可以在SQL条件中使用表达式 IS [NOT] INITIAL 来比较运算数和它们的类型初始值。

如果使用了该表达式,语法检查会以7.53版本的严格模式运行。

(译注:从文档来看,7.53版本的严格模式规则和7.52版本是相同的。)

5,日期/时间函数

ABAP SQL现增加了对以下日期/时间函数的支持:

  • 时间戳函数
    • TSTMP_IS_VALID
    • TSTMP_CURRENT_UTCTIMESTAMP
    • TSTMP_SECONDS_BETWEEN
    • TSTMP_ADD_SECONDS

 如果使用了这些函数,语法检查会以7.53版本的严格模式运行。

6,发布公用表表达式的Associations

可以通过公用表表达式(common table expression,以下简称CTE)访问CDS视图,现在可以使用语句WITH的WITH ASSOCIATIONS附加项来发布这些视图的association,以便在当前WITH语句的路径表达式中使用。附加项REDIRECT TO也可以用于替换前CTE或当前CTE发布的association的目标数据源。

如果使用了该附加项,语法检查会以7.53版本的严格模式运行。

7,SELECT列表中的数字

此前,在SELECT查询中只能使用INT4类型范围内的值。现在,可以使用长度为31的数字,当其不在INT4类型范围内时,会被解释为DEC类型。

8,CAST增强

现在可以通过CAST表达式把INT1, INT2, INT3, INT4和INT8转换为DEC了。

9,INSERT语句子查询中的Client操作

现在,INSERT语句的附加项USING CLIENT可以在子查询中指定了。这意味着在插入操作中指定的目标表的client可以不同于子查询数据源的client。

如果没有在子查询中指定USING CLIENT,自动client操作会应用当前的client ID。在7.53之前,使用USING CLIENT指定的client ID也会在子查询中使用。

现在子查询的FROM子句可以访问使用INSERT语句填充的数据库表或经典视图,这意味着可以从一个client复制数据到另一个client。

使用这一特性时,语法检查会以7.53版本的严格模式运行。

10,以子查询为数据源的MODIFY

在ABAP SQL的写语句MODIFY里,可以在FROM关键字后面使用一个加上括号的SELECT subquery_clauses来实现以子查询作为数据源。子查询的数据结果集的行,会直接在数据库中插入或更新到目标表里。不再需要把数据从数据库传输到ABAP应用服务器了。

使用这一特性时,语法检查会以7.53版本的严格模式运行。

11,USING CLIENT和会话变量client

如果在一个ABAP SQL读语句中,存在对一个特定于客户端的CDS视图的多查询,并且该CDS视图中使用了annotation:@ClientHandling.algorithm:#SESSION_VARIABLE的话,多个查询中的会话变量client(相当于SAP HANA数据库中的ABAP特定会话变量CDS_CLIENT)必须设为同一值。如果为其中某项查询设定了不同的值,则会发生运行时错误SAPSQL_DIFFERENT_CLIENT_VALUES。这种情况会在使用了WITH语句或者UNION语句时发生。

12,表缓存中的null值

现在表缓存支持真null值了,null值不再被转换为类型初始值。在访问缓存时,会产生和直接访问数据库时一样的结果。不再存在相应的限制。这会影响到以下方面:

  • 对于包含null值的关系表达式,现在在缓存中进行比较的时候,结果也是unknown了(除非表达式是IS [NOT] INITIAL)。
  • IS [NOT] NULL不再绕过缓存。
  • 访问被缓存的CDS视图。当缓存被访问时,会产生和直接访问数据库相同的结果。null值通常由outer join或某些表达式如case表达式产生。已经不再存在只有不产生null值的CDS视图才能被缓存的限制。

13,限制移除

  • 对于某些SQL表达式和函数,ABAP SQL的读语句不再绕过缓存。
  • 在不需要识别单行或者generic range的条件里,当某列指定在比较或者BETWEEN的右侧时,ABAP SQL读语句不再绕过缓存。前提是两个运算数都是数字类型的、并且不是DF16_DEC类型或者DF34_DEC,或者都是字符类型的,或者都是都是RAW类型、且具有相同长度。

14,弱检查

在ABAP SQL语句里使用了不被全部数据库平台支持的特性时,不会再产生语法检查警告,而是产生扩展程序检查警告。

15,新检查

如果以内表作为SELECT语句的数据源时,内表需要被传递给数据库表,会产生一个语法警告。可以使用pragma ##itab_db_select来隐藏这个语法检查警告。

16,程序调用中的替换服务

CL_OSQL_REPLACE中的方法ACTIVATE_REPLACEMENT有了新参数FLG_SURVIVE_SUBMIT,允许在被调用的程序中进行重定向。

17,GROUP BY附加项GROUPING SETS

在一个SELECT语句中,可以使用GROUP BY附加项GROUPING SETS了。附加项GROUPING SETS可以在一个SELECT语句下进行多个分组聚合。也可以在一个语句中通过对相同的SELECT使用不同的GROUP BY子句分组、并且使用UNION来实现相同的功能。后者易出错并且对数据库来说更难优化。相比之下,GROUPING SETS附加项也使得解释和维护SELECT语句变得更简单。

18,聚合函数GROUPING

现在可以在SELECT语句中使用GROUPING函数。带有聚合函数GROUPING的聚合表达式在GROUP BY子句中担任分组集GROUPING SETS的分组函数。分组函数GROUPING可以区分出在结果集中的指定的列是否被聚合。只能在使用了包含GROUPING SETS附加项的GROUP BY子句的情况下使用该函数。

19,语法规则的更严格检查

过去在语法检查的严格模式的某些检查规则,现在在非严格模式下也会有效。在非严格模式下,违反这些规则会产生语法检查警告,在多数情况下,会导致程序运行期间产生运行时错误。

  • 在访问视图时,键字段必须位于开始处。
  • 在访问关联了CDS role的CDS entity时,不能使用附加项USING CLIENTCLIENT SPECIFIED。
  • 即使在使用路径表达式时,附加项CLIENT SPECIFIED也只能用于特定于客户端的数据源。
  • 指定列时,对于包含include结构的数据库表,必须使用组件的实际名称,而不是ABAP Dictionary中定义的任何组的名称。
  • 使用关键字AS定义的SELECT列表的别名最多可包含30个字符。ORDER BY后也不允许使用超过30个字符的备用列名。
  • LCHR和LRAW类型的列只有在与相应长度字段一起读取时才能在查询中读取。
  • 对于SELECT中的FOR ALL ENTRIES
    • 数据源的列与内表列之间进行比较且它们的类型为p时,小数位必须匹配。 
    • 在ORDER BY之后使用PRIMARY KEY指定的主键的所有列也必须出现在SELECT列表中。
    • 只能为具有基本行类型的内表指定伪组件table_line。
  • 对于聚合函数之外、在HAVING后指定的列,必须使用GROUP BY分组。这也适用于在使用了HAVING子句的时候直接在SELECT列表中指定、但没有在GROUP BY后指定的列。
  • 如果SELECT列表指定为*,HAVING子句只能同GROUP BY子句一起使用。
  • 如果ORDER BY后使用了别名,这个名字必须是唯一的,也不可以和没有别名的列的名字相同。
  • 在into后指定的工作区wa的字段少于SELECT列表中的显式字段。
  • 在SELECT列表中显式指定的字段无法被赋给into子句中相应的结构工作区wa里的字段,或者无法赋给由括号包围、逗号分隔的数据对象。
  • 在LIKE的右侧,不能指定长度是左侧字段2倍以上的字符文本或常量。
  • 使用UPDATE FROM或MODIFY FROM访问所有字段都是键字段的投影视图
  • 使用INSERT FROM, UPDATE FROM,或者MODIFY FROM创建写入流时指定于通用的引用(A reference that is too general)。
  • 在语句UPDATE中,列只能出现在单个更新表达式(update expression)的左端。