在 ABAP SQL 中执行计算和字符串处理

学习目标

  • 对数据库执行计算。

  • 对数据库执行字符串处理。

数学表达式

在 ABAP SQL 中,算术表达式可用作字段列表的元素。

ABAP SQL 支持四个基本运算符、中括号和三个不同的算术方法(整数、小数和浮点)。中间结果的值范围和精度高度取决于所使用的算术。

算术派生自表达式中使用的操作数:

  • 如果表达式仅包含整数操作数,则表达式为整数表达式,结果也属于整数类型。

  • 只要表达式包含小数类型(字典类型 DEC、QUAN、CURR、ABAP 类型 p)的操作数,表达式结果的类型为 DEC。

  • 如果所有操作数都具有二进制浮点类型(字典类型 FLTP,ABAP 类型 f),则表达式为浮点表达式,结果为 FLTP 类型。

存在以下重要限制:

  • 仅允许在浮点表达式中使用除法运算符(/)。

  • 无法在同一表达式中使用浮点类型和其他数值类型。

提示

通过使用 CAST() 将类型转换为 FLTP,可以在浮点表达式中使用任何数值操作数。

示例:ABAP SQL 中的某些算术表达式

此示例显示 ABAP SQL SELECT 语句的字段列表中算术表达式的两个示例。

字段 SEATS_AVAILABLE 通过从该航班上的座位总数(表字段 SEATS_MAX)中减去已占用座位(表字段 SEATS_OCCUPIED)的数量计算得出。由于两个操作数的类型均为 integer,因此此表达式的结果也是整数。

字段 PERCENTAGE_FLTP 将占用座位的百分比计算为 0 到 100 之间的数字。为此,需要用文字 100 乘以已占用座位数,并将结果除以总座位数。由于除法运算符仅在浮点表达式中可用,因此必须将所有操作数转换为类型 FLTP。因此,结果也是类型 FLTP,缺省情况下以科学表示法显示。

注意

数字文字 100 的类型为整数,因此也必须转换为类型 FLTP。或者,可以定义值为 100.0 的类型为 f 的常量,并在 SQL 表达式中使用此常量。

试用:算术表达式

  1. 创建实施接口 IF_OO_ADT_CLASSRUN 的新全局类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

SELECT FROM /dmo/flight
FIELDS seats_max,
seats_occupied,
seats_max - seats_occupied           AS seats_avaliable,
(   CAST( seats_occupied AS FLTP )
* CAST( 100 AS FLTP )
) / CAST(  seats_max AS FLTP )       AS percentage_fltp
WHERE carrier_id = 'LH' AND connection_id = '0400'
INTO TABLE @DATA(result).
out->write(
EXPORTING
data   = result
name   = 'RESULT'
).
  1. 选择 CTRL + F3 激活类,然后选择 F9 将其作为控制台应用执行。

  2. 分析控制台输出。

  3. 尝试您自己的算术表达式以熟悉语法。

数字函数

ABAP SQL 提供各种内置函数来补充算术表达式的计算。可在 ABAP 语言文档中找到完整列表。

一些重要的数字 SQL 函数如下:

  • 函数 DIV( ) 执行整数除法(无余数),并且仅允许使用整数值作为输入。

  • 函数 MOD( ) 返回此类除法的整数余数。

  • 函数 DIVISION() 允许除浮点值之外的任何数字输入。小数结果的精度由 函数的第三个参数指定。

  • 函数 ROUND() 允许您将数值显式舍入到给定位置。

示例:在 ABAP SQL 中使用数字函数

该示例说明了 ABAP SQL 中数字函数的使用。函数 DIV() 允许您以整数形式计算百分比值,而函数 DIVISION() 以小数值形式返回结果。

请注意,DIV() 削减余数,以使 48.78787…变为 48。另一方面,DIVISION() 使用商业舍入舍入到指定的小数位数,以便 48.787878…变为 48.79 而不是 48.78。

试用:数字函数

  1. 创建实施接口 IF_OO_ADT_CLASSRUN 的新全局类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

SELECT FROM /dmo/flight
FIELDS seats_max,
seats_occupied,
(   CAST( seats_occupied AS FLTP )
* CAST( 100 AS FLTP )
) / CAST(  seats_max AS FLTP )                  AS percentage_fltp,
div( seats_occupied * 100 , seats_max )         AS percentage_int,
division(  seats_occupied * 100, seats_max, 2 ) AS percentage_dec
WHERE carrier_id    = 'LH'
AND connection_id = '0400'
INTO TABLE @DATA(result).
out->write(
EXPORTING
data   = result
name   = 'RESULT'
).
  1. 选择 CTRL + F3 激活类,然后选择 F9 将其作为控制台应用执行。

  2. 分析控制台输出。

  3. 将光标置于其中一个数字函数(例如, 部门 )上,然后选择 F1 打开文档。

  4. 在文档中查找其他数字函数,并尝试它们熟悉其可能性。

字符串处理

在 ABAP 中,运算符 && 允许您将类似字符的参数合并为一个长字符串。您也可以在 ABAP SQL 中使用此运算符在数据库中执行连接操作。在第一个示例中,客户的称谓、名字和姓氏组合为全名。在第二个示例中,将街道、邮政编码和城市名称合并到客户的地址。由于 && 不添加任何类型的分隔符,必须手动添加空格。

字符串处理函数

ABAP SQL 还提供了大量用于字符串处理的内置函数。可在 ABAP 语言文档中找到完整列表。

ABAP SQL 中的一些重要字符串函数如下:

  • 函数 CONCAT( ) 连接两个字符串(不含空格)。

  • 函数 CONCAT_WITH_SPACE() 执行相同操作,但在中间插入指定数量的空格。

  • 函数 UPPER() 和 LOWER() 将参数转换为大写/小写。

  • 函数 INITCAP() 与 LOWER() 类似,但将每个单词的第一个字母转换为大写。

  • 函数 LEFT() 从给定参数中提取前 n 个字符。RIGHT()相同,但从右侧开始。

  • 函数 SUBSTRING() 从给定位置开始提取给定数量的字符。

该示例通过连接 street、postal_code 和 city 来构造客户的地址。它以两种不同的方式执行此操作:一次使用并置运算符 &&,一次使用嵌套 sql 函数。

注意

运算符 && 忽略尾随空格。它仅在文字 ' ' 中保留空白。

此示例说明了函数 UPPER( )、LOWER( ) 和 INITCAP( ) 的影响。请注意,在运输公司名称中,功能转换 'GmbH' 的方式有何不同。

此示例说明如何使用字符串处理函数从日期字段中提取年、月和日信息。

注意

请记住,日期字段的内部格式为 YYYYMMDD。第一列中的连字符属于日期字段的输出格式(ISO 格式)。它们不以内部格式显示。将日期字段转换为 CHAR( 8) 类型后,内部格式变为可见。这可以在第二列中看到。

试用:字符串处理

  1. 创建实施接口 IF_OO_ADT_CLASSRUN 的新全局类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

SELECT FROM /dmo/customer
FIELDS customer_id,
street && ',' && ' ' && postal_code && ' ' && city   AS address_expr,
concat( street,
concat_with_space(  ',',
concat_with_space( postal_code,
upper(  city ),
1
),
1
)
) AS address_func
WHERE country_code = 'ES'
INTO TABLE @DATA(result_concat).
out->write(
EXPORTING
data   = result_concat
name   = 'RESULT_CONCAT'
).
**********************************************************************
SELECT FROM /dmo/carrier
FIELDS carrier_id,
name,
upper( name )   AS name_upper,
lower( name )   AS name_lower,
initcap( name ) AS name_initcap
WHERE carrier_id = 'SR'
INTO TABLE @DATA(result_transform).
out->write(
EXPORTING
data   = result_transform
name   = 'RESULT_TRANSLATE'
).
**********************************************************************
SELECT FROM /dmo/flight
FIELDS flight_date,
cast( flight_date as char( 8 ) )  AS flight_date_raw,
left( flight_Date, 4   )          AS year,
right(  flight_date, 2 )          AS day,
substring(  flight_date, 5, 2 )   AS month
WHERE carrier_id = 'LH'
AND connection_id = '0400'
INTO TABLE @DATA(result_substring).
out->write(
EXPORTING
data   = result_substring
name   = 'RESULT_SUBSTRING'
).
  1. 选择 CTRL + F3 激活类,然后选择 F9 将其作为控制台应用执行。

  2. 分析控制台输出。

  3. 在 SELECT 语句的 FIELDS 列表中浏览表达式和函数,以熟悉语法和函数。

在数据库上执行数据处理

在本练习中,将可用座位的计算以及航空公司标识和航空公司名称的连接向下推送到数据库。

模板:

  • /LRN/CL_S4D401_DBS_NESTED_JOIN(全局类)

解决方案:

  • /LRN/CL_S4D401_DBS_PUSH_DOWN(全局类)

任务 1: 复制模板(可选)

复制模板类 /LRN/CL_S4D401_DBS_NESTED_JOIN。如果已完成上一练习,则可以跳过此任务并继续编辑类 ZCL_##_SOLUTION

步骤

  1. 将类 /LRN/CL_S4D401_DBS_NESTED_JOIN 复制到您自己的包中的类(建议名称:ZCL_##_SOLUTION,其中 ## 代表组编号)。

  2. 在项目浏览器中,右键单击类 /LRN/CL_S4D401_DBS_NESTED_JOIN 以打开上下文菜单。

  3. 从上下文菜单中,选择复制....

  4. 在包字段中输入的名称。在名称字段中,输入 ZCL_##_SOLUTION,其中 ## 代表组编号。

  5. 选择下一步

  6. 确认传输请求并选择完成

  7. 激活复制。

  8. Ctrl + F3 激活类。

任务 2: 下推计算

在本地类 LCL_PASSENGER_FLIGHT 中,使用组件 seats_free 扩展静态属性 flights_buffer 的行类型。调整方法 GET_FLIGHTS_BY_CARRIER 的实施并将可用座位的计算添加到 SELECT 语句。然后调整 CONSTRUCTOR 方法的实施。将计算添加到 SELECT SINGLE 语句并使用计算结果填充实例属性 seats_free

步骤

  1. 在局部类 LCL_PASSENGER_FLIGHT 中,导航到结构类型 ST_FLIGHTS_BUFFER 的定义。

  2. 例如,可以在编辑器中打开本地类型标签,然后搜索 BEGIN OF st_flights_buffer

  3. 或者,您可以在左侧的概要视图中展开 ZCL_##_SOLUTIONLCL_PASSENGER_FLIGHT,然后选择 ST_FLIGHTS_BUFFER

  4. 在组件 seats_occuped 之后,添加类型与实例属性 **seats_**free 相同的新组件 seats_free

  5. 如下调整代码:

TYPES: BEGIN OF st_flights_buffer,
carrier_id     TYPE /lrn/passflight-carrier_id,
connection_id  TYPE /lrn/passflight-connection_id,
flight_date    TYPE /lrn/passflight-flight_date,
plane_type_id  TYPE /lrn/passflight-plane_type_id,
seats_max      TYPE /lrn/passflight-seats_max,
seats_occupied TYPE /lrn/passflight-seats_occupied,
seats_free     TYPE i,
price          TYPE /lrn/passflight-price,
currency_code  TYPE /lrn/passflight-currency_code,
END OF st_flights_buffer.
  1. 导航至方法 GET_FLIGHTS_BY_CARRIER 的实施,并将可用座位的计算添加到 SELECT 语句的字段清单中。

警告

确保计算与结构类型 ST_FLIGHTS_BUFFER 的新组件位于同一位置。或者,您可以在 INTO 子句中使用附加项 CORRESPONDING FIELDS OF。

  1. 如下调整代码:

SELECT
FROM /lrn/passflight
FIELDS carrier_id, connection_id, flight_date,
plane_type_id, seats_max, seats_occupied,
seats_max - seats_occupied AS seats_free,
price, currency_code
WHERE carrier_id = @i_carrier_id
INTO TABLE @flights_buffer.
  1. 导航到方法 CONSTRUCTOR 的实施,并将可用座位的计算添加到 SELECT SINGLE 语句的字段列表中。

  2. 如下调整代码:

SELECT SINGLE
FROM /lrn/passflight
FIELDS plane_type_id,
seats_max, seats_occupied,
seats_max - seats_occupied AS seats_free,
price, currency_code
WHERE carrier_id    = @i_carrier_id
AND connection_id = @i_connection_id
AND flight_date   = @i_flight_date
INTO CORRESPONDING FIELDS OF @flight_raw.
  1. 向下滚动到实例属性 seats_free 的值分配。将赋值运算符右侧的算术表达式替换为 flight_raw-seats_free 的内容。

  2. 如下调整代码:

planetype = flight_raw-plane_type_id.
seats_max = flight_raw-seats_max.
seats_occ = flight_raw-seats_occupied.
*  seats_free = flight_raw-seats_max - flight_raw-seats_occupied.
seats_free = flight_raw-seats_free.

任务 3: 下推字符串连接

在局部类 LCL_CARRIER 中,调整 CONSTRUCTOR 方法的实施。将航空公司标识和航空公司名称的连接移动到 SELECT SINGLE 语句的字段列表,并将结果存储在实例属性**名称**中。

步骤

  1. 在局部类 LCL_CARRIER 中,导航至 CONSTRUCTOR 方法的实施。

  2. 照常执行该步骤。

  3. 找到**名称**属性的值分配。对其进行分析,然后在代码行的开头添加注释符号。

  4. 向下滚动到语句 name = carrier_id && && name.

  5. 将光标置于此代码行中的任意位置,然后按 Ctrl + <

  6. 在 SELECT SINGLE 语句的字段列表中,将数据库表字段 NAME 替换为适当字符串处理函数的调用,以连接由单个空格分隔的航空公司的标识和名称。

注意

也可以在 SELECT SINGLE 语句的字段列表中使用 carrier_id && ' ' && name。

  1. 如下调整代码:

SELECT SINGLE
FROM /lrn/carrier
*    FIELDS  name, currency_code
FIELDS concat_with_space( carrier_id, name, 1 ), currency_code
WHERE carrier_id = @i_carrier_id
INTO ( @me->name, @me->currency_code ).
  1. 以控制台应用形式激活并测试全局类。

  2. Ctrl + F3

  3. F9