实施基本 SELECT 语句
学习目标
描述 ABAP SQL 的基本功能。
从数据库中读取单个值。
基本 ABAP SQL 语法
结构化查询语言 (SQL)

所有关系数据库系统都使用结构化查询语言 (SQL) 的变式来允许您使用它们。标准 SQL 由三个主要组件组成:
数据操作语言 (DML)
DML 包含您在数据库表中处理信息所需的语句。这些是 INSERT(用于添加新记录)、 SELECT (用于读取数据)、 UPDATE (更改现有记录)和 DELETE (用于删除表内容)。这些语句以 ABAP SQL 的形式反映在 ABAP 中。
数据定义语言 (DDL)
数据定义语言为您提供一些命令,使您能够创建和删除数据库表和其他数据库部件,例如方案、表索引和视图。在 SAP 系统中,使用 ADT 的专用编辑器执行这些任务,而不是使用 ABAP 程序执行这些任务。一个示例是我们之前看到的数据库表定义编辑器。
数据控制语言 (DCL)
SQL 中使用数据控制语言来限制特定用户对数据库中数据的访问。它不在 ABAP 的经典表单中使用,因为数据库级别的用户与最终用户一对一不对应。因此,ABAP 有自己的权限概念。
ABAP SQL 体系结构

过去,SAP 系统必须支持一系列数据库平台,并且每个平台对 SQL 标准的实施略有不同。这意味着每个平台都需要略有不同的命令来实现特定任务。为避免 ABAP 代码特定于数据库,SAP 发明了最初调用的 ABAP SQL 或 Open SQL。
注意
开放式 SQL 和 ABAP SQL 的名称更改还说明,自版本 7.53 起,ABAP 仅支持 SAP HANA 作为 DBMS。
ABAP SQL 是在 ABAP 级别实施并集成到 ABAP 语言中的 SQL 命令的抽象集。在运行时,ABAP SQL 将转换为数据库能够理解的 SQL 变式。此变式称为本地 SQL,以将其与集成到 ABAP 中的 ABAP SQL 变式区分开来。从 ABAP SQL 到本地 SQL 的转换在数据库接口中进行,ABAP 系统的组件由常规部分和数据库特定库组成。
尽管较新的 ABAP 版本仅支持 SAP HANA 作为 DBMS,但 SAP 仍保留 ABAP SQL 的概念和数据库接口。这是由于以下原因:
体系结构兼容性
ABAP SQL 和数据库接口是系统架构不可或缺的一部分。
代码兼容性
先前 SAP 产品(包括客户特定开发)中的 ABAP SQL 编码应在现代、仅 SAP HANA 的 ABAP 环境中无副作用运行。
数据库接口的任务
数据库接口不仅翻译语句;还负责 ABAP 特定任务,例如自动客户端处理。
从数据库中读取单个记录
ABAP 中的 SELECT 语句

要从数据库中读取数据,请使用 SELECT 语句。
在 ABAP SQL 中编写 SELECT 语句时,语法检查会将您编写的内容与表和视图的定义进行比较。如果尝试对不存在的表、视图或字段进行寻址,则会出现语法错误。
SELECT 语句的基本语法包含多个部分,称为子句,并始终遵循"ABAP 中的 SELECT 语句"图中的模式。SELECT 语句最重要的子句如下:
FROM
在 SELECT 语句的 FROM 子句中,指定要从中读取的数据源。可以是数据库表或视图。特殊的 SQL 技术允许您在同一 SELECT 语句中组合来自多个源的数据。
FIELDS
在 SELECT 语句的 FIELDS 子句中,列出要读取的数据库表的列。列表中的列必须用逗号分隔。如果要读取整个表行,可以指定 FIELDS * 而不是列清单。但是,请注意,这可能导致数据库的工作比只读取所需的列要多。
WHERE
在 WHERE 子句中,可以指定一个条件,用于描述将读取表的哪些行。例如,条件 WHERE carrier_id = 'LH' 表示仅读取那些行(其中列 CARRIER_D 包含值 LH)。
WHERE 子句可以包含通过 AND 和 OR 运算符链接的多个条件。例如,WHERE carrier_id = 'LH' 和 connection_id = '0400' 将返回转机 LH 0400 的数据。也可以使用 NOT 否定条件。WHERE 子句是唯一可选的子句。但是,请注意,如果没有 WHERE 子句,您将从表中读取所有数据,或者如果该表具有客户端字段 - 属于用户登录客户端的所有数据。不含 WHERE 子句的 SELECT 会导致严重的性能问题,应避免这种情况。
INTO
INTO 子句指定 ABAP 程序中数据应放置到的变量。这通常是结构或内部表,理想情况下,其组件顺序应与 FIELDS 子句中的列清单相同。
注意
您将在 ABAP 中看到其他形式的 SQL 语法。这些版本较旧,已保留以确保兼容性。您应该使用现代语法,因为它提供的功能和特征远远超过旧形式。

"示例 1:读取单个记录的单个字段"一图说明了从数据库中读取单个值的 SELECT 语句。FROM 子句告诉我们 语句从数据库表 /DMO/CONNECTION 中读取。选项 SINGLE(在关键字 SELECT 之后)表示仅读取一行(单个记录)。通过为关键字段 carrier_id 和 connection_id 提供关键过滤器值,在 WHERE 子句中识别此行。请记住,数据库接口将在剩余关键字段客户端上添加过滤器。
FIELDS 子句仅列出表的一列:列 AIRPORT_FROM_ID。
INTO 子句必须与 语句的其余部分相匹配。在我们的示例中,将变量 airport_from_id 指定为目标对象,即与表字段 airport_from_id 类型相同的标量数据对象。
注意
at 符号 (@) 将 airport_from_id 标识为 ABAP 数据对象的名称。在 ABAP SQL 语句中使用的所有变量和常量都是必需的。例如,如果数据对象和表字段具有相同的名称,则需要避免歧义。

"示例 2:读取单个记录的多个字段"一图说明了从数据库的同一记录中读取两个值的 SELECT 语句。
此时,FIELDS 子句列出表的两列:列 AIRPORT_FROM_ID 和列 AIRPORT_TO_ID。
为了与此相匹配,INTO 子句将变量 airport_from_id 和 airport_to_id 指定为目标对象。它们用逗号隔开,并用一对括号括起来,以明确它们共同构成 SELECT 语句的目标。

实施 SELECT 语句时,必须始终考虑可能没有结果,因为数据库表根本不包含任何数据,或者它不包含满足 WHERE 子句中条件的任何行。在上图的示例中,数据库表不包含 carrier_id = 'XX' 且 connection_id = '1234' 的行。
ABAP SQL 使用系统字段 SY-SUBRC 指示语句执行成功或失败。系统字段 SY-SUBRC 属于整数类型。初始值 0 始终表示执行成功。如果在 SELECT 语句之后,SY-SUBRC 包含值 4,则表示数据库返回空结果。
如果数据库返回空结果,ABAP SQL 不会涉及 INTO 后的目标变量!
特别是,如果出现错误,不会初始化目标变量。

建议您在每个 SELECT 语句后立即评估系统字段 sy-subrc 的内容。
试用:SELECT 示例
与本课程的第一个练习一样,创建一个实施接口 IF_OO_ADT_CLASSRUN 的新全局类。
将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:
DATA airport_from_id TYPE /DMO/airport_from_id.
DATA airport_to_id TYPE /DMO/airport_to_id.
DATA airports TYPE TABLE OF /DMO/airport_from_id.
* Example 1: Single field from Single Record
**********************************************************************
SELECT SINGLE
FROM /dmo/connection
FIELDS airport_from_id
WHERE carrier_id = 'LH'
AND connection_id = '0400'
INTO @airport_from_id.
out->write( `----------` ).
out->write( `Example 1:` ).
out->write( |Flight LH 400 departs from { airport_from_id }.| ).
* Example 2: Multiple Fields from Single Record
**********************************************************************
SELECT SINGLE
FROM /dmo/connection
FIELDS airport_from_id, airport_to_id
WHERE carrier_id = 'LH'
AND connection_id = '0400'
INTO ( @airport_from_id, @airport_to_id ).
out->write( `----------` ).
out->write( `Example 2:` ).
out->write( |Flight LH 400 flies from { airport_from_id } to { airport_to_id }| ).
* Example 3: Empty Result and sy-subrc
**********************************************************************
SELECT SINGLE
FROM /dmo/connection
FIELDS airport_from_id
WHERE carrier_id = 'XX'
AND connection_id = '1234'
INTO @airport_from_id.
IF sy-subrc = 0.
out->write( `----------` ).
out->write( `Example 3:` ).
out->write( |Flight XX 1234 departs from { airport_from_id }.| ).
ELSE.
out->write( `----------` ).
out->write( `Example 3:` ).
out->write( |There is no flight XX 1234, but still airport_from_id = { airport_from_id }!| ).
ENDIF.
按 CTRL + F3 激活类,按 F9 将其作为控制台应用执行。
分析控制台输出。调试程序,使用源代码熟悉概念。
从数据库表中读取数据
在本练习中,使用出发机场和目标机场的属性扩展本地类,并从数据库表中读取这些属性的值。
模板:
/LRN/CL_S4D400_CLS_CONSTRUCTOR(全局类)
解决方案:
/LRN/CL_S4D400_DBS_SELECT(全局类)
任务 1: 复制模板
复制模板类。或者,复制上一练习的解决方案。
步骤
将类
/LRN/CL_S4D400_CLS_CONSTRUCTOR复制到您自己的包中的类(建议名称:ZCL_##_SELECT,其中 ## 代表组编号)。打开全局类
/LRN/CL_S4D400_CLS_CONSTRUCTOR的源代码。将项目浏览器视图与编辑器链接。
在项目浏览器视图中,右键单击类
/LRN/CL_S4D400_CLS_CONSTRUCTOR以打开上下文菜单。从上下文菜单中,选择复制 ....
在包字段中输入包的名称。在名称字段中,输入名称
ZCL_##_SELECT,其中 ## 代表组编号。调整描述,然后选择下一步。
确认传输请求并选择完成。
任务 2: 声明附加属性
使用专用实例属性 airport_from_id 和 airport_to_id 扩展局部类 lcl_connection。将新属性的一些输出添加到方法 get_output 的实施中。
注意
尚未填充新属性。为了填充它们,我们将在本练习的下一个任务之一中向构造函数添加 SELECT 语句。
步骤
切换到局部类
lcl_connection。在全局类中,选择局部类型。
将以下专用属性添加到类定义:
属性
属性名称 |
范围 |
数据类型 |
|---|---|---|
|
实例 |
/DMO/AIRPORT_FROM_ID |
|
实例 |
/DMO/AIRPORT_TO_ID |
如下调整代码:
PRIVATE SECTION.
DATA carrier_id TYPE /dmo/carrier_id.
DATA connection_id TYPE /dmo/connection_id.
DATA airport_from_id TYPE /dmo/airport_from_id.
DATA airport_to_id TYPE /dmo/airport_to_id.
ENDCLASS.
扩展方法
get_output的实施。将更多字符串模板附加到返回参数r_output。将新属性作为表达式嵌入到字符串模板中。导航到方法
get_output的实施。如下调整代码:
APPEND |--------------------------------| TO r_output.
APPEND |Carrier: { carrier_id }| TO r_output.
APPEND |Connection: { connection_id }| TO r_output.
APPEND |Departure: { airport_from_id }| TO r_output.
APPEND |Destination: { airport_to_id }| TO r_output.
激活此类。执行并分析控制台输出。
按
Ctrl + F3激活类。按
F9键运行类。
任务 3: 分析数据库表
分析数据库表 /DMO/CONNECTION 的定义。
步骤
打开包含数据库表
/DMO/CONNECTION定义的开发对象。从 eclipse 工具栏中,选择打开 ABAP 开发对象或按
Ctrl + Shift + A。在输入字段中,输入
/dmo/con作为搜索字符串。在匹配项目列表中,单击 /DMO/CONNECTION(数据库表)并选择确定。
打开数据库表 /DMO/CONNECTION 的元素信息 (F2)。
单击关键字
/dmo/connection后的define table并按F2显示元素信息。
任务 4: 从数据库读取数据
在本地类 lcl_connection 的方法**构造函数**中,实现一个 SELECT 语句,该语句从数据库表 /DMO/CONNECTION 中读取新属性的值。
步骤
返回到全局类中的局部类
lcl_connection。在 Eclipse 的编辑器视图中,打开 ZCL_##_SELECT 标签。
在全局类中,选择局部类型。
导航到方法**
构造**函数的实施。搜索代码行
METHOD constructor.。在
ENDIF.语句之后,添加一个从数据库表/DMO/CONNECTION中读取单个记录的 SELECT 语句。如下调整代码:
IF i_carrier_id IS INITIAL OR i_connection_id IS INITIAL.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
SELECT SINGLE
FROM /dmo/connection
实施 FIELDS 子句。读取表字段
airport_from_id和airport_to_id。
提示
使用自动完成(Ctrl + 空格)输入字段名称。
在
FROM /DMO/CONNECTION后输入FIELDS。在空白之后,按 **
Ctrl + 空格**并选择 airport_from_id。在逗号和空格后再次按 **
Ctrl + 空格**键,然后选择 airport_to_id。完整的 FIELDS 子句应如下所示:
FIELDS airport_from_id, airport_to_id
实施 WHERE 条件。使用导入参数
i_carrier_id 和 i_****connection_id的值限制数据库表的所有关键字段(客户端字段除外)。不要忘记使用前缀 @ 转义参数。
提示
使用自动完成(Ctrl + 空格)输入元素名称和参数名称。
在 FIELDS 子句后添加以下代码:
WHERE carrier_id = @i_carrier_id
AND connection_id = @i_connection_id
实现 INTO 子句。将 SELECT 结果存储在属性
airport_from_id和airport_to_id中。不要忘记使用前缀 @ 转义属性。
提示
使用自动完成(Ctrl + 空格)输入属性名称。
如下调整代码:
WHERE carrier_id = @i_carrier_id
AND connection_id = @i_connection_id
INTO ( @airport_from_id, @airport_to_id ).
完整的 SELECT 语句应如下所示:
SELECT SINGLE
FROM /dmo/connection
FIELDS airport_from_id, airport_to_id
WHERE carrier_id = @i_carrier_id
AND connection_id = @i_connection_id
INTO ( @airport_from_id, @airport_to_id ).
在 SELECT 语句之后实施错误处理。检查系统字段
sy-subrc的内容。如果不等于零,则引发异常CX_ABAP_INVALID_VALUE。在 SELECT 语句后添加以下代码:
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
激活此类。执行并分析控制台输出。检查新属性的输出是否显示数据。
按
Ctrl + F3激活类。按
F9键运行类。