在 ABAP SQL 中使用特殊的内置函数

学习目标

  • 处理数据库中的日期、时间和时间戳。

  • 使用内置转换函数。

用于处理日期、时间和时戳的功能

如果要在数据库字段中存储日期信息,可以从两种类型中进行选择:

  • DATS 存储在 ABAP 中处理的日期。这意味着,日期以 'YYYYMMDD' 格式存储为长度为 8 的字符。

  • DATN 将信息存储在数据库的日期类型中。使用 ABAP SQL 从数据库中读取信息时,会自动将其转换回 8 位格式。

类型 TIMS 和 TIMN 之间存在相同的差异。

如果要在数据库字段中存储时戳信息,可以从以下三种类型中进行选择:

  • TIMESTAMP 将信息存储为不带小数的数字,其中数字的数字表示年、月、日、小时、分钟和秒。

  • TIMESTAMPL 与 TIMESTAMP 相同,但另外存储最多 7 位小数,其精度为 100 纳秒。

  • UTCLONG 与 TIMESTAMPL 具有相同的精度。

日期、时间和时戳的 SQL 函数

ABAP SQL 为日期、时间和时戳信息提供了大量的内置函数。存在用于检查字段内容是否有效、计算函数或从输入参数中提取特定信息的函数的函数。此处列出了其中一些功能。可在 ABAP 文档中找到完整清单。

其中一些功能特定于类型。

其他功能是通用的。他们可以处理不同类型的输入。例如,功能 IS_VALID 可以检查日期、时间点或时戳的有效性。它接受两种日期格式(DATS 和 DATN)和两种时间格式(TIMS 和 TIMN)。但是,对于时戳,仅支持类型 UTCLONG。

在某些情况下,您可以在类型特定的功能和通用功能之间进行选择。例如,对于类型为 DATS 的字段,可以使用 DATS_IS_VALID() 或 IS_VALID()。您应该更喜欢较新的通用功能。

注意

通用函数目前不支持 TIMESTAMP 和 TIMESTAMPL 类型的时戳。对于这些类型,必须使用以 TSTMP_ 或 TSTMPL_ 开头的类型特定功能。

以下是具有日期类信息的通用函数的使用示例。与大多数其它 SQL 表达式一样,您也可以在 WHERE 子句中使用这些函数。在此,我们将选择限制为超过 10 天的差旅。

注意

函数 IS_VALID() 不是真正的布尔函数。它返回整数值 1(表示 true)和 0(表示 false)。

试用:日期处理

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

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

SELECT FROM /dmo/travel
FIELDS begin_date,
end_date,
is_valid( begin_date  )              AS valid,
add_days( begin_date, 7 )            AS add_7_days,
add_months(  begin_date, 3 )         AS add_3_months,
days_between( begin_date, end_date ) AS duration,
weekday(  begin_date  )              AS weekday,
extract_month(  begin_date )         AS month,
dayname(  begin_date )               AS day_name
WHERE customer_id = '000001'
AND days_between( begin_date, end_date ) > 10
INTO TABLE @DATA(result).
out->write(
EXPORTING
data   = result
name   = 'RESULT'
).
  1. 选择 CTRL + F3 激活类,然后选择 F9 将其作为控制台应用执行。

  2. 分析控制台输出。

  3. 将光标置于其中一个日期处理功能上(例如 工作日 ),然后选择 F1 打开文档。

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

转换函数

一些 ABAP SQL 转换函数

并非所有类型转换都可以由 CAST 表达式处理。对于某些转换,ABAP SQL 提供特定转换函数,如下图所示。

例如,SQL 函数 DATS_FROM_DATN 执行从 DATN(数据库特定的日期表示)到 DATS(ABAP 特定字符型日期格式)的转换。SQL 函数 DATS_TO_DATN() 在另一个方向执行转换。

函数 TSTMP_TO_DATS() 和 TSTMP_TO_TIMS() 分别将时戳转换为日期字段和时间字段。请注意,这些功能仅适用于技术格式 DEC( 15, 0) 的时戳。其他时戳格式需要转换,然后才能用作这些函数的输入。函数 DATS_TIMS_TO_TSTMP 将日期和时间信息合并到一个时戳中。

特殊转换函数组允许您在不同的计量单位和货币之间进行转换。函数 UNIT_CONVERSION() 将源单位中的数量转换为目标单位,例如,以千米为单位的距离到以英里为单位的相同距离。函数 CURRENCY_CONVERSION() 将一种货币的金额转换为在特定报表日期以其他货币计的相同金额。

注意

为使 CURRENCY_CONVERSION() 正常工作,时间相关汇率必须在系统的数据库中可用。有关如何执行此操作的详细信息,请参阅 SAP 系统文档。

示例:时戳转换的 SQL 功能

此示例从表 /DMO/TRAVEL(字段 LASTCHANGEDAT)读取 UTC 时戳,并将其转换为时区 'EST' 中的日期和时间。

时戳以类型 TIMESTAMPL 存储在数据库表中,即 7 位小数。这意味着必须将其转换为不带小数的短时戳,以使其与 SQL 函数的输入参数 TSTMP 兼容。还需要转换文字 'EST' 以便与输入参数 TZONE 兼容。

注意

世界时间和东部标准时间之间有 4 小时的时间转换。

示例:单位转换的 SQL 函数

在此示例中,航班连接的距离信息转换为英里 (MI),而在数据库中,信息以公里 (KM) 存储。

由于表字段 DISTANCE 是使用类型 INT4 而不是类型 QUAN 定义的,因此必须对其进行转换以匹配输入参数 QUANTITY 的类型。

必须同时转换文字 'MI',因为它是 CHAR(2) 类型而不是 'UNIT' 类型。

注意

示例仅提供 UNIT_CONVERSION( ) 的三个强制参数。可在 ABAP 文档中找到可选参数及其含义的列表。

在此示例中,差旅的总价格将转换为目标货币欧元。为函数 CURRENCY_CONVERSION 提供金额(字段 TOTAL_PRICE)、源货币(字段 CURRENCY_CODE)、target_currency(文字 'EUR')和汇率日期(ABAP 变量 TODAY)。在 SELECT 语句之前,使用系统日期在方法调用中填充变量 TODAY。

注意

示例仅提供 CURRENCY_CONVERSION( ) 的四个强制参数。可在 ABAP 文档中找到可选参数及其含义的列表。

试用:转换功能

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

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

SELECT FROM /dmo/travel
FIELDS lastchangedat,
CAST( lastchangedat AS DEC( 15,0 ) ) AS latstchangedat_short,
tstmp_to_dats( tstmp = CAST( lastchangedat AS DEC( 15,0 ) ),
tzone = CAST( 'EST' AS CHAR( 6 ) )
) AS date_est,
tstmp_to_tims( tstmp = CAST( lastchangedat AS DEC( 15,0 ) ),
tzone = CAST( 'EST' AS CHAR( 6 ) )
) AS time_est
WHERE customer_id = '000001'
INTO TABLE @DATA(result_date_time).
out->write(
EXPORTING
data   = result_date_time
name   = 'RESULT_DATE_TIME'
).
*********************************************************************
DATA(today) = cl_abap_context_info=>get_system_date(  ).
SELECT FROM /dmo/travel
FIELDS total_price,
currency_code,
currency_conversion( amount             = total_price,
source_currency    = currency_code,
target_currency    = 'EUR',
exchange_rate_date = @today
) AS total_price_EUR
WHERE customer_id = '000001' AND currency_code <> 'EUR'
INTO TABLE @DATA(result_currency).
out->write(
EXPORTING
data   = result_currency
name   = 'RESULT__CURRENCY'
).
**********************************************************************
SELECT FROM /dmo/connection
FIELDS distance,
distance_unit,
unit_conversion( quantity = CAST( distance AS QUAN ),
source_unit = distance_unit,
target_unit = CAST( 'MI' AS UNIT ) )  AS distance_MI
WHERE airport_from_id = 'FRA'
INTO TABLE @DATA(result_unit).
out->write(
EXPORTING
data   = result_unit
name   = 'RESULT_UNIT'
).
  1. 选择 CTRL + F3 激活类,然后选择 F9 将其作为控制台应用执行。

  2. 分析控制台输出。

在 ABAP SQL 中使用特殊的内置函数

在本练习中,将航班持续时间和货币转换的计算向下推送到数据库。

模板:

  • /LRN/CL_S4D401_DBS_PUSH_DOWN(全局类)

解决方案:

  • /LRN/CL_S4D401_DBS_FUNCTIONS(全局类)

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

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

步骤

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

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

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

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

  5. 选择下一步

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

  7. 激活复制。

  8. Ctrl + F3 激活类。

任务 2: 使用时戳计算

在本地类 LCL_PASSENGER_FLIGHT 中,调整 CLASS_CONSTRUCTOR 方法的实施。将航班持续时间计算移动到 SELECT 语句的字段列表,并将结果存储在内部表 connections_buffer 的**持续时间**列中。

步骤

  1. 在局部类 LCL_PASSENGER_FLIGHT 中,导航到 CLASS_CONSTRUCTOR 方法的实施。

  2. 照常执行该步骤。

  3. 在 SELECT 语句字段列表的末尾,添加对合适的 SQL 函数的调用,该函数计算两个时间戳之间的差值(以秒为单位)。

  4. 如下调整代码:

SELECT
FROM /lrn/connection AS c
LEFT OUTER JOIN /lrn/airport AS f
ON c~airport_from_id = f~airport_id
LEFT OUTER JOIN /lrn/airport AS t
ON c~airport_to_id = t~airport_id
FIELDS carrier_id, connection_id,
airport_from_id, airport_to_id,
departure_time, arrival_time,
f~timzone AS timezone_from,
t~timzone AS timezone_to,
tstmp_seconds_between( )
INTO TABLE @connections_buffer.
  1. 作为第一个参数(参数 tstmp1),调用合适的 SQL 函数,该函数根据当前日期、启运时间和启运机场的时区计算时戳。

提示

必须将函数方法 cl_abap_context_info=>get_system_date 的调用移动到 SELECT 语句之前,以便能够访问 SELECT 语句中的当前日期。

  1. 如下调整代码:

DATA(today) = cl_abap_context_info=>get_system_date( ).
SELECT
FROM /lrn/connection AS c
LEFT OUTER JOIN /lrn/airport AS f
ON c~airport_from_id = f~airport_id
LEFT OUTER JOIN /lrn/airport AS t
ON c~airport_to_id = t~airport_id
FIELDS carrier_id, connection_id,
airport_from_id, airport_to_id,
departure_time, arrival_time,
f~timzone AS timezone_from,
t~timzone AS timezone_to,
tstmp_seconds_between(
tstmp1 = dats_tims_to_tstmp(
date  = @today,
time  = c~departure_time,
tzone = f~timzone ),
)
INTO TABLE @connections_buffer.
  1. 调用相同的 SQL 函数来计算与第二个参数对应的到达时戳(参数 tstmp2)。

  2. 如下调整代码:

DATA(today) = cl_abap_context_info=>get_system_date( ).
SELECT
FROM /lrn/connection AS c
LEFT OUTER JOIN /lrn/airport AS f
ON c~airport_from_id = f~airport_id
LEFT OUTER JOIN /lrn/airport AS t
ON c~airport_to_id = t~airport_id
FIELDS carrier_id, connection_id,
airport_from_id, airport_to_id,
departure_time, arrival_time,
f~timzone AS timezone_from,
t~timzone AS timezone_to,
tstmp_seconds_between(
tstmp1 = dats_tims_to_tstmp(
date  = @today,
time  = c~departure_time,
tzone = f~timzone ),
tstmp2 = dats_tims_to_tstmp(
date  = @today,
time  = c~arrival_time,
tzone = t~timzone )
)
INTO TABLE @connections_buffer.
  1. 因为我们希望将持续时间(以分钟为单位)而不是以秒为单位,因此将秒数除以 60。

提示

使用以整数形式返回除数结果的适当算术 SQL 函数。

  1. 如下调整代码:

DATA(today) = cl_abap_context_info=>get_system_date( ).
SELECT
FROM /lrn/connection AS c
LEFT OUTER JOIN /lrn/airport AS f
ON c~airport_from_id = f~airport_id
LEFT OUTER JOIN /lrn/airport AS t
ON c~airport_to_id = t~airport_id
FIELDS carrier_id, connection_id,
airport_from_id, airport_to_id,
departure_time, arrival_time,
f~timzone AS timezone_from,
t~timzone AS timezone_to,
div(
tstmp_seconds_between(
tstmp1 = dats_tims_to_tstmp(
date  = @today,
time  = c~departure_time,
tzone = f~timzone ),
tstmp2 = dats_tims_to_tstmp(
date  = @today,
time  = c~arrival_time,
tzone = t~timzone )
),
60 )
INTO TABLE @connections_buffer.
  1. 最后,为完整表达式提供别名(建议名称:持续时间

  2. 如下调整代码:

DATA(today) = cl_abap_context_info=>get_system_date( ).
SELECT
FROM /lrn/connection AS c
LEFT OUTER JOIN /lrn/airport AS f
ON c~airport_from_id = f~airport_id
LEFT OUTER JOIN /lrn/airport AS t
ON c~airport_to_id = t~airport_id
FIELDS carrier_id, connection_id,
airport_from_id, airport_to_id,
departure_time, arrival_time,
f~timzone AS timezone_from,
t~timzone AS timezone_to,
div(
tstmp_seconds_between(
tstmp1 = dats_tims_to_tstmp(
date  = @today,
time  = c~departure_time,
tzone = f~timzone ),
tstmp2 = dats_tims_to_tstmp(
date = @today,
time = c~arrival_time,
tzone = t~timzone )
),
60 ) AS duration
INTO TABLE @connections_buffer.
  1. 现在您已成功将计算移动到 SELECT 语句中,移除或注释内部表 connections_buffer 的完整循环。

  2. 标记 LOOP 语句、ENDLOOP 语句和中间的所有代码行,然后按 Ctrl + < 在每行代码前插入注释字符。

任务 3: 换算货币

在局部类 LCL_PASSENGER_FLIGHT 中,调整方法 CONSTRUCTORGET_FLIGHTS_BY_CARRIER 的实施。将货币转换移动到 SELECT 语句的字段列表中。

步骤

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

  2. 照常执行该步骤。

  3. 找到将航班价格转换为以属性**货币**存储的货币代码的 方法调用 。对其进行分析,然后在代码行的开头添加注释符号。

  4. 通过调用全局类 cl_exchange_rates 的方法 convert_to_local_currency 完成货币转换。

  5. 要注释方法的调用,请标记 TRY 语句、ENDTRY 语句以及它们之间的所有代码行,然后按 Ctrl + <

  6. 由于**价格属性不再通过 cl_exchange_rates=>convert_to_local_currency 方法调用填充,因此将 flight_raw 结构中的价格组件值直接分配到价格**属性。在刚刚注释掉的方法调用之后直接添加此分配。

注意

在接下来的步骤中,您将确保 flight_raw-price 包含转换的航班价格。

  1. 如下调整代码:

*  TRY.
*    cl_exchange_rates=>convert_to_local_currency(
*      EXPORTING
*        date              = me->flight_date
*        foreign_amount    = flight_raw-price
*        foreign_currency  = flight_raw-currency_code
*        local_currency    = currency
*      IMPORTING
*        local_amount      = me->price ).
*  CATCH cx_exchange_rates.
*    CLEAR price.
*  ENDTRY.
price = flight_raw-price.
  1. 向上滚动到 SELECT SINGLE 语句。在字段清单中,将表字段 PRICE 替换为货币转换的 SQL 功能调用。

  2. 如下调整代码:

SELECT SINGLE
FROM /lrn/passflight
FIELDS plane_type_id,
seats_max, seats_occupied,
seats_max - seats_occupied AS seats_free,
*           price,
currency_conversion( ) AS 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. 提供 SQL 函数的四个强制参数。作为汇率日期,使用航班日期(表字段 flight_date)作为金额和源货币,使用表字段**价格和****货币,并使用属性货币**作为目标货币。

  2. 如下调整代码:

SELECT SINGLE
FROM /lrn/passflight
FIELDS plane_type_id,
seats_max, seats_occupied,
seats_max - seats_occupied AS seats_free,
*           price,
currency_conversion(
amount             = price,
source_currency    = currency_code,
target_currency    = @currency,
exchange_rate_date = flight_date
) AS 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. 为可选参数 on_error 提供合适的值,以获得与之前相同的行为。

提示

请参阅 SQL 函数的文档,了解参数的可能值及其含义。

  1. 您可以在全局类 SQL_CURRENCY_CONVERSION 中找到作为结构化常量 c_on_error 组件的允许值。

  2. 如下调整代码:

SELECT SINGLE
FROM /lrn/passflight
FIELDS plane_type_id,
seats_max, seats_occupied,
seats_max - seats_occupied AS seats_free,
*           price,
currency_conversion(
amount             = price,
source_currency    = currency_code,
target_currency    = @currency,
exchange_rate_date = flight_date,
on_error           =
@sql_currency_conversion=>c_on_error-set_to_null
) AS 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. 在字段列表中,将表字段 currency_code 替换为**货币**属性。

  2. 按如下方式调整代码:

SELECT SINGLE
FROM /lrn/passflight
FIELDS plane_type_id,
seats_max, seats_occupied,
seats_max - seats_occupied AS seats_free,
*           price,
currency_conversion(
amount             = price,
source_currency    = currency_code,
target_currency    = @currency,
exchange_rate_date = flight_date,
on_error           =
@sql_currency_conversion=>c_on_error-set_to_null
) AS price,
*           currency_code
@currency AS 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. 转到方法 GET_FLIGHTS_BY_CARRIER 的实施并以相同方式调整 SELECT 语句。

  2. 将字段列表的最后两个元素复制到剪贴板(pricecurrency_code)。

  3. 转到方法 GET_FLIGHTS_BY_CARRIER 的实施。

  4. 在 SELECT 语句的字段列表中,将最后两个字段(pricecurrency_code)替换为剪贴板的内容。

  5. 如下调整代码:

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,
currency_conversion(
amount             = price,
source_currency    = currency_code,
target_currency    = @currency,
exchange_rate_date = flight_date,
on_error           =
@sql_currency_conversion=>c_on_error-set_to_null
) AS price,
@currency AS currency_code
WHERE carrier_id = @i_carrier_id
INTO TABLE @flights_buffer.
  1. 以控制台应用形式激活并测试全局类。

  2. Ctrl + F3

  3. F9