在 CDS 视图中处理数据集
学习目标
描述在 ABAP CDS 中处理数据集的技术。
在 ABAP CDS 视图中使用 DISTINCT 附加项。
在 CDS 视图中包含汇总和分组。
在 CDS 视图中使用连接和联合。
概览

ABAP SQL 提供了各种 SQL 技术来操作和组合数据集。ABAP CDS 中是否也支持它们?如果它们不同,会有什么区别?
以下是最重要的 SQL 技术的列表:
ORDER BY
ABAP CDS 中不支持用于排序结果集的 ORDER BY 子句。
DISTINCT
DISTINCT 附加项用于从结果集中删除重复条目。它在 ABAP CDS 中受支持,但在关键字 SELECT 后面使用 DISTINCT,而不是在字段列表的开头。
聚合和 GROUP BY
聚合用于从整个数据集派生值,例如记录数、最大值或平均值。聚合通常与 GROUP BY 子句结合使用,该子句在为每个组单独执行聚合之前定义记录组。并非在 ABAP SQL 中可用的所有聚合在 CDS 视图中也受支持,但最重要的聚合(例如 COUNT、MIN、MAX、SUM 和 AVG)可用。语法方面也存在细微差异。例如,在使用 AVG 计算平均值时,必须指定结果类型。在 ABAP SQL 中,这只是一个选项。
连接
连接用于组合与连接条件匹配的不同结果集中的记录。ABAP SQL 和 ABAP CDS 支持相同的连接类型。同样,存在一些细微的语法差异。访问字段时,必须指定数据源,ABAP CDS 使用不同的字段选择器。
UNION
UNION 语句用于联合来自不同结果集的记录。在 CDS 视图的定义中使用 UNION 时,特殊规则适用于键值定义和视图元素的类型、名称和注释。
让我们来看一下每种技术的示例。
ABAP CDS 中的不同结果

附加项 DISTINCT 允许您从 SELECT 语句的结果集中删除重复项。在 ABAP SQL 中,它位于字段列表的开头。在 CDS 视图定义中,DISTINCT 始终直接位于 SELECT 语句之后。它不是花括号中元素列表的一部分。
ABAP CDS 中的聚合

该示例显示了最常用的聚合函数及其在 ABAP SQL 和 CDS 视图定义中的使用方式。请注意,在集合函数 AVG() 括号内的 abap.fltp 附加指定结果的数值类型。AS 附加在 ABAP CDS 中是必需的。在 ABAP SQL 中,这是可选的,缺省情况下使用类型 FLTP。

如果 SELECT 语句的元素列表混合包含集合函数和其它元素(如列、表达式等),则 SQL 的语法需要 GROUP BY 子句。GROUP BY 子句必须列出聚合之外的字段列表中出现的数据源的所有字段。
该示例显示,在 ABAP SQL 和 ABAP CDS 中定义 GROUP BY 子句的方式没有区别。
提示
CDS 数据定义的编辑器提供快速修复以生成 GROUP BY 子句。当元素列表较长或包含复杂表达式时,此功能特别有用。对元素列表进行更改后,将提供另一个快速修复,允许您调整 GROUP BY 子句。
试用:CDS 视图定义中的聚合
创建不含参考数据对象的新 CDS 数据定义。
删除整个 SELECT 语句,包括元素列表和花括号。
复制以下代码片段并将其插入到 as 之后:
select from /DMO/I_Connection_R
{
key AirlineID,
key ConnectionID,
concat_with_space(
concat_with_space(
concat_with_space(
concat(
concat( AirlineID,
ConnectionID ),
': ' ),
DepartureAirport, 1 ),
'->', 1 ),
DestinationAirport, 1 ) as Description
// ,
//count( distinct _Flight.FlightDate ) as NumberOfFlights,
//min( _Flight.FlightDate ) as FirstFlightDate,
//sum( _Flight.OccupiedSeats ) as TotalOccupiedSeats,
//avg( _Flight.OccupiedSeats ) as AverageOccupation
}
激活数据定义,打开 数据预览 工具并分析输出。
从元素列表的聚合中移除注释标记。请注意,语法错误 的错误图标 。缺少 GROUP BY 子句 包括灯泡。
选择错误图标以调用快速修复 添加 GROUP BY 子句 。请注意,生成的 GROUP BY 子句包含字段 DepartureAirport 和 DestinationAirport ,因为这些字段用于计算视图元素 描述 。
请注意,对于元素列表中的最后一个元素, 仅允许使用附加项 "as" 的语法错误 函数 AVG 。
从元素列表中移除最后一个元素,并将其替换为以下代码:
avg( _Flight.OccupiedSeats as abap.dec(15,2)) as AverageOccupation
激活数据定义,打开 数据预览 工具并分析输出。
ABAP CDS 中的连接和联合

在 SQL 中,联接是将两个数据源组合成一个结果集,由两个数据源的列组成。行由连接类型和连接条件(也称为 ON 条件)确定。
ABAP CDS 和 ABAP SQL 支持相同的连接类型,其中包括 INNER JOIN、LEFT OUTER JOIN 和 RIGHT OUTER 连接。有关其他受支持的联接类型,请参阅 ABAP 关键字文档。
在 ABAP SQL 和 ABAP CDS 中,连接的语法非常相似,但并不相同。让我们看一个示例。
在 ABAP CDS 中,句点符号 (.) 用作字段名称和数据源名称之间的分隔符,或者如果为此数据源定义了别名,则用作数据源别名。在此 ABAP SQL 中遵循 SQL 标准。在 ABAP SQL 中,代字号 (~) 用于此目的。原因是,在 ABAP 中,句点符号已用作语句分隔符。
其他语法差异涉及字段列表或元素列表中字段的使用。在 CDS 视图实体中,所有字段的数据源名称均为必填。在 ABAP SQL 中,仅当不同数据源中存在同名字段时才需要源名称。在示例中,字段 carrier_id 就是这种情况,因为两个数据源中都存在具有该名称的字段。

UNION 将 CDS 视图实体的多个 SELECT 语句的结果集行合并到一个结果集中。如果没有 ADD 附加项,则在合并后会移除全部重复项。
作为 UNION 或 UNION ALL 的前提条件,结果集必须具有相同数量的元素,并且出现在结果集的相同位置的元素对必须具有兼容的数据类型。此前提条件在 ABAP SQL 和 ABAP CDS 中相同。
此外,在 CDS 视图定义中使用 UNION 或 UNION ALL 时,以下规则适用:
元素名称
对于元素列表中的每一列,直接元素名称或别名必须匹配。
关键要素
所有元素列表的关键元素必须匹配。否则,将出现语法检查错误。
元素注释
只有第一个 SELECT 语句的元素列表才能定义元素注释。在所有后续分支中,元素注释被禁止。
为避免冲突,当 CDS 视图定义包含 UNION 或 UNION ALL 时,注释 @Metadata.ignorePropagatedAnnotations: true 是必需的。
试用:CDS 视图定义中的连接和联合
创建不含参考数据对象的新 CDS 数据定义。
删除整个 SELECT 语句,包括元素列表和花括号。
复制以下代码片段并将其插入到 as 之后:
select from /dmo/customer as c
inner join /dmo/travel as t
on c.customer_id = t.customer_id
{
key c.customer_id as id,
key 'C' as type,
concat_with_space( c.last_name, c.first_name, 1 ) as name,
@EndUserText.label: 'Number of Bookings'
count( distinct t.travel_id ) as TravelsCount
}
group by
c.customer_id,
c.last_name,
c.first_name
union all
select from /dmo/agency as a
inner join /dmo/travel as t
on a.agency_id = t.agency_id
{
key a.agency_id,
// key 'A' as type,
a.name,
@EndUserText.label: 'Number of Bookings'
count( distinct t.travel_id) as TravelsCount
}
group by
a.agency_id,
a.name
分析视图定义。它包含两个 SELECT 的 UNION ALL。第一个返回客户,每个客户都有他们预订的差旅数量。第二个 SELECT 返回旅行社,每个旅行社都有在该旅行社预订的旅行数量。由于语法错误,无法激活数据定义。
请注意,语法错误 联合中选择列表的字段数必须相同, 并通过从第二个 SELECT 的元素 类型 中移除注释标记来解决它。
请注意,在第二个 SELECT 的此分支中不允许注释 ,并通过从第二个 SELECT 中移除 @endUserText.label . 来解决此问题。
注意语法错误 分支 2 的键值定义必须与分支 1 的键值定义匹配 ,并通过在第二个 SELECT 中的元素 类型 前面添加 键值 来解决它。
提示
此错误消息当前仅显示在问题视图中,而不显示在编辑器视图中。
当所有语法错误都消失后,激活数据定义,打开 数据预览 工具并分析输出。
在 CDS 视图中处理数据集
您想要进一步分析员工和部门数据。为此,在 CDS 视图实体中使用 DISTINCT 附加项、聚合、分组和连接。
模板:
不适用
解决方案:
/LRN/C_DEPARTMENT_QRY(数据定义)
任务 1: 使用 DISTINCT
定义新的 CDS 视图实体(建议名称:Z##_C_DepartmentQuery),该实体返回至少分配了一个员工的部门的标识和描述。作为数据源,将视图实体用于具有功能但没有参数的员工数据(建议名称为 Z##_C_EmployeeQuery)。确保每个部门仅列出一次。
注意
如果尚未完成上一练习,则可以让新视图实体从视图实体 /LRN/C_Employee_Fnc 中读取。
步骤
在您自己的包中,创建新的数据定义(建议名称:
Z##_C_DEPARTMENTQUERY,其中##是您的组编号)。指定 CDS 视图实体Z##_C_EmployeeQuery作为参考对象,然后选择定义视图实体模板以生成定义语句、某些标准注释和元素列表。在项目浏览器视图中,右键单击数据定义
Z##_C_EMPLOYEEQUERY以打开上下文菜单。从上下文菜单中,选择新建数据定义。
确认包字段包含包的名称,并且参考对象字段包含数据库表定义的名称。
在名称字段中,输入 CDS 视图实体的名称(
Z##_C_DepartmentQuery,其中 ## 是您的组编号)。在描述字段中输入**
部门(查询)**,然后选择下一步。确认传输请求,然后选择下一步。
警告
确保尚未选择完成。如果执行此操作,则无法选择要使用的模板。
从模板列表中,选择定义视图实体,然后选择完成。
应用源代码格式设置。
从 eclipse 菜单中选择 源代码→格式 。或者,选择
Shift + F1。编辑新数据定义的元素列表。移除除
DepartmentID和DepartmentDescription之外的所有元素。如下调整代码:
define view entity Z##_C_DepartmentQuery
as select from Z##_C_EmployeeQuery
{
DepartmentId,
DepartmentDescription
}
激活数据定义并在数据预览工具中显示查询结果。
按
Ctrl + F3激活开发对象。按
F8调用数据预览工具。编辑数据定义并将 DISTINCT 附加项添加到 SELECT 语句。
如下调整代码:
define view entity Z##_C_DepartmentQuery
as select distinct from Z##_C_EmployeeQuery
{
DepartmentId,
DepartmentDescription
}
激活数据定义并再次显示查询结果。
按
Ctrl + F3激活开发对象。按
F8调用数据预览工具。
任务 2: 使用聚合和分组
对于每个部门,计算分配到该部门的员工的平均所属公司和总年薪。
步骤
从数据定义中移除 DISTINCT 附加项。
如下调整代码:
define view entity Z##_C_DepartmentQuery
as select from Z##_C_EmployeeQuery
{
DepartmentId,
DepartmentDescription
}
在元素列表的末尾,添加新的视图元素(建议名称:
AverageAssociation)。将聚合函数 AVG 与参数CompanyAffiliation一起使用。如下调整代码:
define view entity Z##_C_DepartmentQuery
as select from Z##_C_EmployeeQuery
{
DepartmentId,
DepartmentDescription,
avg( CompanyAffiliation ) as AverageAffiliation
}
确保结果与源视图实体的
CompanyDependation元素具有相同的类型。将光标置于
CompanyAffiliation,然后按F2显示代码元素信息。CompanyDepiliation的类型为abap.dec(11,1)。如下调整代码:
define view entity Z##_C_DepartmentQuery
as select from Z##_C_EmployeeQuery
{
DepartmentId,
DepartmentDescription,
avg( CompanyAffiliation as abap.dec(11,1) ) as AverageAffiliation
}
添加新的视图元素(建议名称:
TotalSalary)。使用聚合函数 SUM 和参数AnnualSalaryConverted。如下调整代码:
define view entity Z##_C_DepartmentQuery
as select from Z##_C_EmployeeQuery
{
DepartmentId,
DepartmentDescription,
avg( CompanyAffiliation as abap.dec(11,1) ) as AverageAffiliation,
sum( AnnualSalaryConverted ) as TotalSalary
}
将货币代码添加到元素列表,并使用 @Semantics.amount.currencyCode 注释金额字段。
如下调整代码:
define view entity Z##_C_DepartmentQuery
as select from Z##_C_EmployeeQuery
{
DepartmentId,
DepartmentDescription,
avg( CompanyAffiliation as abap.dec(11,1) ) as AverageAffiliation,
@Semantics.amount.currencyCode: 'CurrencyCodeUSD'
sum( AnnualSalaryConverted ) as TotalSalary,
CurrencyCodeUSD
}
使用快速修复生成 GROUP BY 子句。
将光标置于
select上并按Ctrl + 1调用快速修复。或者,选择该代码行左侧的带有灯泡的错误图标。从可用快速修复列表中,选择添加 GROUP BY 子句。
应用快速修复后,代码应如下所示:
define view entity Z##_C_DepartmentQuery
as select from Z##_C_EmployeeQuery
{
DepartmentId,
DepartmentDescription,
avg( CompanyAffiliation as abap.dec(11,1) ) as AverageAffiliation,
@Semantics.amount.currencyCode: 'CurrencyCodeUSD'
sum( AnnualSalaryConverted ) as TotalSalary,
CurrencyCodeUSD
}
group by
DepartmentId,
DepartmentDescription,
CurrencyCodeUSD
激活数据定义并再次显示查询结果。
按
Ctrl + F3激活开发对象。按
F8调用数据预览工具。
任务 3: 供应输入参数
将数据源替换为提供输入参数的视图(建议名称为 Z##_C_EmployeeQueryP)。要提供参数,请将相同的参数添加到新视图并将其转发到源视图。
注意
如果您尚未完成上一练习,请改为从 CDS 视图实体 /LRN/C_Employee_Par 中读取。
步骤
在 FROM 子句中,替换数据源并使用含参数的视图实体。使用代码完整化插入数据源的完整签名,即 CDS 视图实体及其参数。
在 FROM 子句中,将
Z##_C_EmployeeQuery替换为Z##_C_EmployeeQueryP。如果使用/LRN/C_Employee_Fnc,请将其替换为/LRN/C_Employee_Paar。将光标放置在实体名称之后,然后按 **
Ctrl + 空格**键调用代码完成。按
Shift + Enter插入具有完整签名的视图实体。代码完整化应按如下方式调整代码:
define view entity Z##_C_DepartmentQuery
as select from Z##_C_EmployeeQueryP( p_target_curr: , p_date: )
为视图定义两个输入参数。使用数据源中使用的相同参数名称、类型和注释。
提示
如果从数据源复制参数定义,最容易。
按住
Strg键并在 FROM 后选择视图名称以导航到数据源。选择
as select之前的代码,包括 WITH PARAMETERS 附加,然后按Ctrl + C将参数定义复制到剪贴板。返回到数据定义并在
as select之前插入参数定义。代码现在应如下所示:
define view entity Z##_C_DepartmentQuery
with parameters
p_target_curr : /dmo/currency_code,
@EndUserText.label: 'Date of evaluation'
@Environment.systemField: #SYSTEM_DATE
p_date : abap.dats
as select from Z##_C_EmployeeQueryP( p_target_curr: , p_date: )
使用新参数作为数据源的输入。
如下调整代码:
define view entity Z##_C_DepartmentQuery
with parameters
p_target_curr : /dmo/currency_code,
@EndUserText.label: 'Date of evaluation'
@Environment.systemField: #SYSTEM_DATE
p_date : abap.dats
as select from Z##_C_EmployeeQueryP(
p_target_curr: $parameters.p_target_curr,
p_date: $parameters.p_date )
在元素列表中,调整货币代码元素的名称,即移除美元后缀。
按如下所示调整代码:
{
DepartmentId,
DepartmentDescription,
avg( CompanyAffiliation as abap.dec(11,1) ) as AverageAffiliation,
@Semantics.amount.currencyCode: 'CurrencyCode'
sum( AnnualSalaryConverted ) as TotalSalary,
CurrencyCode
}
group by
DepartmentId,
DepartmentDescription,
CurrencyCode
激活并测试数据定义。
按
Ctrl + F3激活开发对象。按
F8调用数据预览工具。为
P_TARGET_CURR参数使用不同的值。
任务 4: 使用外部联接
您想要查看所有部门,甚至是未分配任何员工的部门。为此,使用当前数据源的外部连接和部门数据的视图实体(建议名称为 Z##_R_Department)。
注意
如果尚未完成关联练习,请改用 CDS 视图实体 /LRN/C_Department_Rel。
步骤
在视图实体
Z##_C_DepartmentQuery中,调整 FROM 子句。在 子句末尾,为部门数据添加 CDS 视图实体的右外连接。如下调整代码:
as select
from Z##_C_EmployeeQueryP(
p_target_curr: $parameters.p_target_curr,
p_date: $parameters.p_date )
right outer join Z##_R_Department
定义两个数据源的别名(建议名称:
e表示员工,d表示部门)并添加 ON 条件。如下调整代码:
as select
from Z##_C_EmployeeQueryP(
p_target_curr: $parameters.p_target_curr,
p_date: $parameters.p_date ) as e
right outer join Z##_R_Department as d
on e.DepartmentId = d.Id
在元素清单中,从部门视图实体而不是员工视图实体中读取部门标识和部门描述,并将部门标识设置为关键元素。
如下调整代码:
{
d.Id,
d.Description,
avg( CompanyAffiliation as abap.dec(11,1) ) as AverageAffiliation,
@Semantics.amount.currencyCode: 'CurrencyCode'
sum( AnnualSalaryConverted ) as TotalSalary,
CurrencyCode
}
移除 GROUP BY 子句并使用快速修复功能重新生成它。
如下调整代码:
group by
d.Id,
d.Description,
CurrencyCode
在语法检查请求的位置添加表别名作为前缀。
如下调整代码:
{
d.Id,
d.Description,
avg( e.CompanyAffiliation as abap.dec(11,1) ) as AverageAffiliation,
@Semantics.amount.currencyCode: 'CurrencyCode'
sum( e.AnnualSalaryConverted ) as TotalSalary,
e.CurrencyCode
}
group by
d.Id,
d.Description,
e.CurrencyCode
激活并测试数据定义。
按
Ctrl + F3激活开发对象。按
F8调用数据预览工具。