ABAP RFC SQL 模糊查询与动态区间条件实战解析

关于 ABAP OPEN SQL 的动态 WHERE 条件和 Dynamic Selections ABAP动态查询技术解析 本文介绍了ABAP中实现灵活数据库查询的动态选择技术,通过FREE_SELECTIONS函数组实现用户自定义查询条件。核心内容包括: 传统固定WHERE条件的局限性,无法满足业务多变需求 Dynamic Selections机制的工作原理,使用FREE_SELECTIONS_INIT初始化、FREE_SELECTIONS_DIALOG交互 完整Demo程序演示如何构建通用表数据浏览器: 用户输入表名后校验权限 调用动态选择函数生成条件 动态拼接WHERE子句执行查询 使用CL_SA 阅读详情

1. 为什么RFC接口的查询条件不能太“死板”?

大家好,我是老韩,在SAP和外围系统集成的坑里摸爬滚打了十几年。今天想和大家聊聊一个看似基础,但实际开发中几乎每个接口都会遇到的“老大难”问题:在ABAP RFC接口里,如何优雅地处理用户五花八门的查询条件?

想象一下这个场景:你写了一个RFC函数ZSAP_GET_SALES_ORDER,用来给外围的.NET或Java系统提供销售订单查询服务。接口里定义了一堆输入参数,比如订单号、客户编号、创建日期范围、金额范围等等。理想很丰满,用户在前端界面输入精确的条件,你后端直接拼接到WHERE子句里,完美。

但现实很骨感。用户可能只想查“包含‘ABC’的所有订单”,给你传个ABC;也可能只想查“2024年9月之后的订单”,只填一个开始日期;甚至可能啥也不填,就想看看最近的所有数据。如果你的SQL语句还是傻乎乎地写WHERE VBELN = @IV_VBELN,那用户输入空值或者模糊值的时候,要么查不到数据,要么就报错。

所以,一个健壮的RFC接口,其查询逻辑必须足够“聪明”和“灵活”。核心就在于两点:对字符串字段支持模糊匹配,以及对区间字段(日期、数字)支持动态的、容错的范围处理。这不仅仅是提升用户体验,更是减少前后端扯皮、降低运维成本的关键。今天,我就结合一个真实的案例,把这块的实战经验掰开揉碎了讲给你听。

2. 字符串模糊查询:一招鲜,吃遍天

我们先从最简单的字符串模糊查询说起。在ABAP Open SQL中,实现模糊查询的语法是LIKE搭配通配符%(代表任意多个字符)和_(代表单个字符)。这和其他数据库(如MySQL、SQL Server)是类似的。

2.1 基础操作:如何拼接出模糊查询条件

假设我们有一个物料描述MAKTX字段需要支持模糊查询。用户传入了参数IV_MAKTX。最直接的做法就是在ABAP代码里,给参数前后加上%。

DATA: lv_maktx_pattern TYPE string.

IF iv_maktx IS NOT INITIAL.
  CONCATENATE '%' iv_maktx '%' INTO lv_maktx_pattern.
ENDIF.

SELECT * FROM makt
  INTO TABLE @DATA(lt_makt)
  WHERE spras = @sy-langu
    AND maktx LIKE @lv_maktx_pattern.

这段代码的逻辑很清晰:如果用户输入了内容,就构造一个前后带%的模式串,然后用于LIKE查询。如果用户没输入(INITIAL),理论上我们应该跳过这个条件,否则LIKE '%%'会匹配所有行,虽然语法正确,但可能带来不必要的性能开销。更常见的做法是,在构建动态WHERE条件时,如果查询字段为空,就直接不添加该条件。

但是,这里有一个新手非常容易踩的坑! 你可能会想,我直接在传入的参数上拼接%行不行?比如:

CONCATENATE '%' iv_maktx '%' INTO iv_maktx. " 错误示范!"

这是绝对错误的! 这会直接修改导入参数IV_MAKTX的值,破坏了参数的原始数据,如果后续还有其他逻辑要用到这个参数,就会出错。所以,务必使用一个本地变量(如lv_maktx_pattern)来接收拼接后的结果。

2.2 实战优化:处理多个模糊查询字段

在一个实际的RFC接口中,往往有多个字段需要支持模糊查询。像原始文章例子里的ZTYPE(类型)、ZSPNUMBER(单号)、VIN(车架号)等等。如果每个字段都写一遍IF...CONCATENATE...,代码会显得非常冗余。

我们可以采用一种更简洁的批量处理方式。虽然ABAP没有特别优雅的语法糖,但我们可以利用循环和动态赋值的思想来让代码更整洁。不过,为了清晰和可维护性,像原始文章那样显式地处理每一个字段,反而是更推荐的做法,尤其是字段数量不多的时候。代码一目了然,方便后续维护者理解。

* 假设所有需要模糊查询的字段都是STRING类型
DATA: lv_ztype_pattern TYPE string,
      lv_zspnumber_pattern TYPE string,
      lv_vin_pattern TYPE string.

* 为每个非空的输入参数构造模糊查询模式
IF ztype IS NOT INITIAL.
  CONCATENATE '%' ztype '%' INTO lv_ztype_pattern.
ELSE.
  lv_ztype_pattern = '%'. " 或者后续在WHERE中跳过该条件
ENDIF.

IF zspnumber IS NOT INITIAL.
  CONCATENATE '%' zspnumber '%' INTO lv_zspnumber_pattern.
ELSE.
  lv_zspnumber_pattern = '%'.
ENDIF.

IF vin IS NOT INITIAL.
  CONCATENATE '%' vin '%' INTO lv_vin_pattern.
ELSE.
  lv_vin_pattern = '%'.
ENDIF.

SELECT * FROM ztable
  INTO TABLE @ot_list
  WHERE ztype LIKE @lv_ztype_pattern
    AND zspnumber LIKE @lv_zspnumber_pattern
    AND vin LIKE @lv_vin_pattern.
    " ... 其他条件

这里有个小技巧:当输入参数为空时,我们将模式串设为%。这样LIKE '%'会匹配该字段的所有值,在逻辑上等同于“忽略此过滤条件”。这是一种简单的实现方式。但在性能敏感的查询中,特别是表数据量很大时,过多的LIKE '%'条件可能会对优化器造成干扰。更优的方案是使用动态WHERE子句,只在参数非空时才添加相应的LIKE条件。

3. 动态区间条件处理:日期与数字的“智能”适配

比起模糊查询,区间条件(BETWEEN ... AND ...)的处理更需要“智能”。因为用户输入的可能性更多样:可能不填、可能只填开始值、可能只填结束值、也可能都填。我们的程序需要自动适配所有这些情况,构造出一个合法的、有意义的BETWEEN条件。

3.1 日期区间处理的三种场景与代码实现

日期区间是业务查询中最常见的。我们假设有两个输入参数:IV_DATE_FROM和IV_DATE_TO,类型是CHAR8(格式YYYYMMDD)。下面是必须处理的三种情况:

场景一:用户两个日期都没输入。 这意味着用户想查询所有日期的数据。在SQL中,我们需要提供一个“无限大”的区间。在SAP中,通常用'00010101'代表最小日期,'99991231'代表最大日期。但更常见的做法是,如果字段是日期类型,可以直接用'00010101'和'99991231'。在ABAP SQL中,BETWEEN是包含端点的,所以这个区间覆盖了所有可能日期。

IF iv_date_from IS INITIAL AND iv_date_to IS INITIAL.
  lv_sql_date_from = '00010101'.
  lv_sql_date_to   = '99991231'.
ENDIF.

场景二:用户只输入了开始日期(或只输入了结束日期)。 比如用户只填了20240930,通常他的意思是“查询2024年9月30日之后的数据”或“查询2024年9月30日当天的数据”。为了简化逻辑,一种常见的、不会引起歧义的处理方式是:将单值查询视为查询某一特定时间点。即,将开始和结束都设为同一个值。

IF iv_date_from IS NOT INITIAL AND iv_date_to IS INITIAL.
  lv_sql_date_from = iv_date_from.
  lv_sql_date_to   = iv_date_from. " 结束日期等于开始日期
ENDIF.

IF iv_date_from IS INITIAL AND iv_date_to IS NOT INITIAL.
  lv_sql_date_from = iv_date_to. " 开始日期等于结束日期
  lv_sql_date_to   = iv_date_to.
ENDIF.

这样,BETWEEN '20240930' AND '20240930' 就会精确匹配日期为20240930的记录。如果用户本意是“大于等于20240930”,那么前端应该在传参时就把IV_DATE_TO设为一个很大的值(如99991231),或者后端采用不同的逻辑(如将单值作为开始,结束设为最大值)。这需要前后端约定一致。

场景三:用户输入了完整的开始和结束日期。 这是最理想的情况,直接使用即可。

IF iv_date_from IS NOT INITIAL AND iv_date_to IS NOT INITIAL.
  lv_sql_date_from = iv_date_from.
  lv_sql_date_to   = iv_date_to.
ENDIF.

将以上逻辑整合,就是原始文章中展示的代码块。这种IF-ELSEIF的瀑布流判断,覆盖了所有输入组合,确保了lv_sql_date_from和lv_sql_date_to最终都有合理的值,可以直接用于BETWEEN子句。

3.2 数字区间处理:原理相同,注意初始值

数字区间(比如金额、数量)的处理逻辑与日期区间完全一致。同样是处理空值、单值、双值三种情况。唯一需要特别注意的,就是数字类型的“空值”或“无效值”用什么来表示。

在ABAP中,数字类型字段(如INT4, DEC, J_1BEXBASE)的初始值是0。所以,我们通常用0来判断用户是否输入了值。

* 假设 ZMONEY_FROM, ZMONEY_TO 是类型为 J_1BEXBASE(金额)的输入参数
DATA: lv_money_from TYPE j_1bbase,
      lv_money_to   TYPE j_1bbase.

IF zmoney_from = 0 AND zmoney_to = 0.
  " 都没输入,给一个极大的范围
  lv_money_from = '-99999999'.
  lv_money_to   = '999999999'.
ELSEIF zmoney_from <> 0 AND zmoney_to = 0.
  " 只输入了开始金额,视为精确查询该金额点
  lv_money_from = zmoney_from.
  lv_money_to   = zmoney_from.
ELSEIF zmoney_from = 0 AND zmoney_to <> 0.
  " 只输入了结束金额,视为精确查询该金额点
  lv_money_from = zmoney_to.
  lv_money_to   = zmoney_to.
ELSE.
  " 两个金额都输入了
  lv_money_from = zmoney_from.
  lv_money_to   = zmoney_to.
ENDIF.

这里我用了-99999999到999999999作为默认范围,你需要根据实际业务中金额的可能取值范围来定义这个“极大”和“极小”值,确保能覆盖所有有效业务数据。

3.3 边界情况与陷阱提醒

在实际开发中,还有一些细节需要注意:

  1. 参数校验:对于日期,虽然我们用了CHAR8,但在拼接SQL前,最好能校验一下格式是否合法(比如月份是否在01-12,日期是否有效)。原始文章中的C#代码就做了这个工作。在ABAP中,你可以用函数DATE_CHECK_PLAUSIBILITY或者尝试用CONVERT DATE来转换,如果转换失败则视为非法输入。
  2. 顺序问题:务必保证lv_sql_date_from <= lv_sql_date_to。虽然我们的逻辑(单值时设为相等)已经保证了这一点,但如果前端传递了20240930到20240101这样的反向区间,你的程序应该如何处理?是自动交换两者,还是报错?这需要和业务方明确规则。一个健壮的程序可以增加一个判断:
    IF lv_sql_date_from > lv_sql_date_to.
      DATA(lv_temp) = lv_sql_date_from.
      lv_sql_date_from = lv_sql_date_to.
      lv_sql_date_to = lv_temp.
    ENDIF.
    
  3. 性能考量:BETWEEN是包含边界的。如果你的日期字段上建有索引,BETWEEN通常能很好地利用索引。但如果你用了LIKE '%'这种匹配所有的条件,可能会使索引失效,全表扫描。因此,在动态构建WHERE条件时,对于明确不需要过滤的字段,最好的做法是不要将其放入WHERE子句中,而不是使用LIKE '%'或BETWEEN 极小值 AND 极大值。

4. 完整RFC函数示例与逐行解析

下面,我们结合模糊查询和动态区间,来看一个增强版的完整RFC函数示例。这个函数ZSAP_LIGHT_ADS_GET用于查询一个名为ZTLIGHT_ADS的自定义表,它几乎囊括了前面讲到的所有技术点。

FUNCTION zsap_light_ads_get.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     VALUE(ZTYPE)        TYPE CHAR10 OPTIONAL
*"     VALUE(ZSPNUMBER)    TYPE CHAR20 OPTIONAL
*"     VALUE(ZFPNUMBER)    TYPE CHAR50 OPTIONAL
*"     VALUE(VIN)          TYPE CHAR20 OPTIONAL
*"     VALUE(ZFPTYPE)      TYPE CHAR50 OPTIONAL
*"     VALUE(TTNAME)       TYPE CHAR50 OPTIONAL
*"     VALUE(SHNUMBER)     TYPE CHAR30 OPTIONAL
*"     VALUE(ZEMAIL)       TYPE CHAR50 OPTIONAL
*"     VALUE(ZBZ)          TYPE CHAR50 OPTIONAL
*"     VALUE(ZDATE_FROM)   TYPE CHAR8 OPTIONAL
*"     VALUE(ZDATE_TO)     TYPE CHAR8 OPTIONAL
*"     VALUE(ZMONEY_FROM)  TYPE J_1BEXBASE OPTIONAL
*"     VALUE(ZMONEY_TO)    TYPE J_1BEXBASE OPTIONAL
*"  TABLES
*"      OT_LIST            STRUCTURE  ZTLIGHT_ADS
*"----------------------------------------------------------------------

  DATA: lv_ztype      TYPE string,
        lv_zspnumber  TYPE string,
        lv_zfpnumber  TYPE string,
        lv_vin        TYPE string,
        lv_zfptype    TYPE string,
        lv_ttname     TYPE string,
        lv_shnumber   TYPE string,
        lv_zemail     TYPE string,
        lv_zbz        TYPE string.

  DATA: lv_date_from  TYPE char8,
        lv_date_to    TYPE char8,
        lv_money_from TYPE j_1bbase,
        lv_money_to   TYPE j_1bbase.

* ------------------------------------------------------------
* 第一部分:处理所有字符串字段的模糊查询模式
* ------------------------------------------------------------
* 为每个可能为空的字符串输入参数构造LIKE模式
* 如果输入为空,则模式为'%',表示匹配所有
  lv_ztype     = COND #( WHEN ztype     IS NOT INITIAL THEN |%{ ztype }%| ELSE '%' ).
  lv_zspnumber = COND #( WHEN zspnumber IS NOT INITIAL THEN |%{ zspnumber }%| ELSE '%' ).
  lv_zfpnumber = COND #( WHEN zfpnumber IS NOT INITIAL THEN |%{ zfpnumber }%| ELSE '%' ).
  lv_vin       = COND #( WHEN vin       IS NOT INITIAL THEN |%{ vin }%| ELSE '%' ).
  lv_zfptype   = COND #( WHEN zfptype   IS NOT INITIAL THEN |%{ zfptype }%| ELSE '%' ).
  lv_ttname    = COND #( WHEN ttname    IS NOT INITIAL THEN |%{ ttname }%| ELSE '%' ).
  lv_shnumber  = COND #( WHEN shnumber  IS NOT INITIAL THEN |%{ shnumber }%| ELSE '%' ).
  lv_zemail    = COND #( WHEN zemail    IS NOT INITIAL THEN |%{ zemail }%| ELSE '%' ).
  lv_zbz       = COND #( WHEN zbz       IS NOT INITIAL THEN |%{ zbz }%| ELSE '%' ).

* ------------------------------------------------------------
* 第二部分:处理日期区间 (CHAR8格式,如20240901)
* ------------------------------------------------------------
  IF zdate_from IS INITIAL AND zdate_to IS INITIAL.
    " 场景1:都没填,查询全部日期
    lv_date_from = '00010101'.
    lv_date_to   = '99991231'.
  ELSEIF zdate_from IS NOT INITIAL AND zdate_to IS INITIAL.
    " 场景2:只填了开始日期,视为精确查询该日
    lv_date_from = zdate_from.
    lv_date_to   = zdate_from.
  ELSEIF zdate_from IS INITIAL AND zdate_to IS NOT INITIAL.
    " 场景3:只填了结束日期,视为精确查询该日
    lv_date_from = zdate_to.
    lv_date_to   = zdate_to.
  ELSE.
    " 场景4:开始和结束日期都填了
    lv_date_from = zdate_from.
    lv_date_to   = zdate_to.
    " 可选:增加一个逻辑,确保 from <= to
    IF lv_date_from > lv_date_to.
      DATA(lv_temp_date) = lv_date_from.
      lv_date_from = lv_date_to.
      lv_date_to   = lv_temp_date.
    ENDIF.
  ENDIF.

* ------------------------------------------------------------
* 第三部分:处理金额区间 (数字类型)
* ------------------------------------------------------------
  IF zmoney_from = 0 AND zmoney_to = 0.
    " 场景1:都没填,给一个极大的范围
    lv_money_from = '-99999999'.
    lv_money_to   = '999999999'.
  ELSEIF zmoney_from <> 0 AND zmoney_to = 0.
    " 场景2:只填了开始金额
    lv_money_from = zmoney_from.
    lv_money_to   = zmoney_from.
  ELSEIF zmoney_from = 0 AND zmoney_to <> 0.
    " 场景3:只填了结束金额
    lv_money_from = zmoney_to.
    lv_money_to   = zmoney_to.
  ELSE.
    " 场景4:开始和结束金额都填了
    lv_money_from = zmoney_from.
    lv_money_to   = zmoney_to.
    " 可选:确保 from <= to
    IF lv_money_from > lv_money_to.
      DATA(lv_temp_money) = lv_money_from.
      lv_money_from = lv_money_to.
      lv_money_to   = lv_temp_money.
    ENDIF.
  ENDIF.

* ------------------------------------------------------------
* 第四部分:执行最终的SQL查询
* ------------------------------------------------------------
  SELECT *
    FROM ztlight_ads
    INTO TABLE @ot_list
    WHERE ztype     LIKE @lv_ztype
      AND zspnumber LIKE @lv_zspnumber
      AND zfpnumber LIKE @lv_zfpnumber
      AND vin       LIKE @lv_vin
      AND zfptype   LIKE @lv_zfptype
      AND ttname    LIKE @lv_ttname
      AND shnumber  LIKE @lv_shnumber
      AND zemail    LIKE @lv_zemail
      AND zbz       LIKE @lv_zbz
      AND zdate    BETWEEN @lv_date_from AND @lv_date_to
      AND zmoney   BETWEEN @lv_money_from AND @lv_money_to.

ENDFUNCTION.

代码解析与亮点:

  1. 使用COND函数简化模糊查询拼接:我用了ABAP 7.4以后支持的COND函数,它比CONCATENATE语句更简洁。|%{ ztype }%|是字符串模板,效果和CONCATENATE一样。
  2. 清晰的逻辑分区:代码分为四个部分,分别处理字符串模糊、日期区间、数字区间和最终查询,结构非常清晰。
  3. 增加了边界检查:在日期和金额区间处理中,我增加了lv_date_from > lv_date_to的判断,并自动交换值。这使接口更加鲁棒,能容忍前端传递的非常规参数。
  4. 参数使用OPTIONAL:在接口定义中,所有IMPORTING参数都加了OPTIONAL关键字。这是一个好习惯,明确告诉调用者这些参数是可选的,并且在ABAP内部,未传递的OPTIONAL参数会保持其类型初始值(字符型为空,数字型为0),这正好与我们后续的IS INITIAL或= 0判断逻辑完美契合。

5. 前端校验:与非SAP系统(如C#)的协同

RFC接口是前后端分离的典范。后端(ABAP)逻辑写得再健壮,如果前端传递的数据本身就是“垃圾”,那也白搭。因此,在前端进行输入校验是至关重要的一环。原始文章中用C# WinForm程序作为例子,展示了前端应该如何准备数据。

5.1 前端校验的核心逻辑

前端校验的目标是:在调用RFC之前,确保传递给后端的数据是合法、合规、有意义的。对于我们的场景,主要校验两点:

  1. 日期格式合法性:用户可能在文本框里输入2024-13-01或abcd这样的非法日期。前端必须拦截这种输入,并给出友好提示,或者按照预定规则(如返回一个默认日期)处理后传给后端。
  2. 数字范围合理性:对于金额区间,要确保输入的是有效数字,并且开始值不大于结束值(如果业务要求的话)。

原始文章中的C#工具类MT提供了很好的示范:

  • DATE_CHECK方法:检查字符串是否为合法的yyyyMMdd格式。如果不是,弹出提示框,并返回一个默认值(如"20240930")。这里的关键是,即使校验失败,也应该返回一个合法的、可预测的值给后端,而不是让程序崩溃或传递空字符串。这个默认值需要前后端共同约定。
  • num_from和num_to方法:将字符串转换为double类型。如果转换失败(输入非数字),则返回0。这正好对应了后端zmoney_from = 0表示“未输入”的逻辑。

5.2 前后端校验的职责划分

这里就引出了一个重要的设计原则:前后端校验的侧重点不同。

  • 前端校验:侧重于用户体验和即时反馈。检查格式、必填项、简单的业务规则(如结束日期不小于开始日期)。发现错误时,立即通过对话框、标签变色等方式提示用户,阻止其进行下一步操作。
  • 后端校验:侧重于数据完整性与业务安全性。进行更彻底的检查,包括数据是否存在、用户是否有权限、是否符合复杂的业务规则等。后端校验是数据进入系统的最后一道防线,必须坚不可摧。

在我们的例子中,前端对日期格式的校验属于“用户体验”范畴。即使前端漏掉了这个校验,传了一个'20241301'给后端,后端的BETWEEN语句在SAP HANA数据库上执行时可能会因为日期转换失败而抛出异常。所以,后端也应有相应的异常处理机制(TRY...CATCH),并将数据库错误转换为友好的业务错误信息返回给前端。

5.3 更佳实践:使用正则表达式进行严格校验

原始文章的C#代码用了IsValidDate函数,通过拆分字符串来校验日期。这是一种方法,但使用正则表达式可以更简洁、更强大。例如,校验yyyyMMdd格式:

public static bool IsValidDate(string dateString)
{
    if (string.IsNullOrEmpty(dateString) || dateString.Length != 8)
        return false;
    // 正则表达式:年份0001-9999,月份01-12,日期01-31(粗略校验)
    Regex regex = new Regex(@"^(?!0000)[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$");
    return regex.IsMatch(dateString);
}

这个正则表达式能快速过滤掉明显不合法的日期(如月份13)。当然,对于像2月30日这样的非法日期,仍然需要结合DateTime.TryParseExact等方法进行最终校验。

6. 性能优化与高级技巧

当数据量变大,或者查询条件非常复杂时,基础的LIKE和BETWEEN可能会遇到性能瓶颈。这里分享几个我实践中用到的优化技巧。

6.1 慎用LIKE '%value%',尤其是字段开头的%

LIKE '%ABC%'这种模式,由于通配符在开头,数据库通常无法利用该字段上的标准B树索引,会导致全表扫描(Full Table Scan)。如果ZTLIGHT_ADS表有上百万行数据,同时有多个这样的模糊查询,性能会急剧下降。

优化建议1:尽可能使用后模糊查询(LIKE 'ABC%') 如果业务允许,鼓励用户使用“以...开头”的查询方式。这样LIKE 'ABC%'是可以利用索引的。你可以在前端界面做引导,或者在后端逻辑中,如果判断输入值没有前置%,则自动采用后模糊。

优化建议2:对于确切的模糊查询,考虑使用全文检索 如果业务必须支持中间模糊(%ABC%),且数据量巨大,性能要求高,那么就需要寻求其他技术方案。SAP HANA数据库提供了强大的全文检索(Full Text Search)功能。你可以将需要模糊查询的字段(如ZBZ备注字段)创建全文索引,然后使用CONTAINS函数进行查询,其性能远高于LIKE。

* 假设已在HANA中为ZTEXT字段创建了全文索引
SELECT * FROM zdoc_table
  INTO TABLE @DATA(lt_result)
  WHERE CONTAINS(ztext, @lv_search_term, FUZZY(0.8)) = 1.

FUZZY(0.8)表示模糊度0.8,还能实现纠错查询,这是LIKE做不到的。

6.2 使用动态WHERE条件替代固定条件

我们之前的示例在WHERE子句中写死了所有条件。即使某个字段的查询模式是%(匹配所有),条件依然存在。更好的做法是使用动态WHERE子句,只添加那些真正需要过滤的条件。

DATA: lv_where TYPE string.

lv_where = `1 = 1`. " 一个永真条件,作为起点

IF lv_ztype <> '%'. " 只有当模式不是'%'时才添加条件
  CONCATENATE lv_where `AND ZTYPE LIKE @LV_ZTYPE` INTO lv_where SEPARATED BY space.
ENDIF.

IF lv_date_from <> '00010101' OR lv_date_to <> '99991231'.
  CONCATENATE lv_where `AND ZDATE BETWEEN @LV_DATE_FROM AND @LV_DATE_TO` INTO lv_where SEPARATED BY space.
ENDIF.

" 同理处理其他条件...

DATA: lt_result TYPE TABLE OF ztlight_ads.
SELECT * FROM ztlight_ads
  INTO TABLE lt_result
  WHERE (lv_where).

注意,动态WHERE子句需要使用WHERE (lv_where)这样的语法,并且要小心SQL注入风险。确保拼接进lv_where字符串的所有变量都是经过严格校验或使用绑定变量(如示例中的@LV_ZTYPE)的。在ABAP 7.52之后,更推荐使用NEW cl_abap_dyn_sql_statement( )等类来安全地构建动态SQL。

6.3 区间查询的索引利用与分区策略

对于BETWEEN条件,如果ZDATE或ZMONEY字段上有索引,查询效率通常很高。但是,如果日期范围跨度非常大(例如从00010101到99991231),实际上也是全表扫描,索引可能不会带来太大收益。

对于按时间序列增长的表(如日志表、交易表),使用数据库分区(Partitioning)是终极优化方案。例如,将ZTLIGHT_ADS表按ZDATE字段进行范围分区(Range Partition),每个月或每个季度一个分区。当查询限定在某个日期范围内时,数据库可以只扫描相关的分区,从而极大提升性能。但这属于数据库物理设计范畴,需要DBA的参与。

7. 总结与个人经验分享

回顾一下,要让一个ABAP RFC接口的查询功能既灵活又健壮,关键在于处理好两点:模糊查询的通用性和区间条件的动态适配。核心逻辑并不复杂,就是一堆IF-ELSE判断,但细节决定成败。

我印象最深的一个坑是早期没有处理日期区间的反向输入(结束早于开始)。测试人员输入了一个反向区间,程序没做处理直接传给数据库,结果查不出数据。测试报了个Bug:“查询功能异常”。排查了半天才发现是这个问题。自那以后,我在所有区间查询的逻辑里都加上了自动交换的防护代码。

另一个经验是关于默认值的选择。对于数字区间,用0作为“未输入”的标志是没问题的。但对于日期,用'00010101'和'99991231'有时会带来意想不到的问题,比如某些数据库函数或ABAP日期计算遇到这些极值会出错。后来在一些项目中,我们改用系统当前日期的前后100年作为默认范围,或者用CL_ABAP_TSTMP=>MAXIMUM等系统常量,更加安全。

最后,再强调一下前后端配合。这份ABAP代码提供了一个非常宽容的后端接口。作为接口开发者,我们当然希望调用方(前端)能传递规范的数据。但现实是,你永远不知道对方会怎么调你。所以,把后端接口设计得足够“傻”和“健壮”,能够消化各种“垃圾输入”并返回合理结果,是减少联调痛苦和线上故障的最有效方法。把复杂的校验和转换逻辑放在后端,让前端的工作尽可能简单,这往往是团队协作中最优的选择。

LDPC码译码原理——概率公式推导 怎么说呢,一直以来都想写一下博客。近半年多来我一直在学习LDPC码译码上的东西,主要是在FPGA实现上的,所以这次想写一些关于这方面的内容,包括译码的原理、仿真以及实现。(当然是基于准循环LDPC码上实现的)不过呢,我写这个的初衷是为了让初学者理解入门用的,而不是在大牛面前班门弄斧,有错误之处欢迎大家批评指正。不知道会不会有人一开始像我一样,为了学好LDPC码,先去好好了解其它的诸如RS码或者是卷... 阅读详情

相关推荐

机械臂速成小指南(八):标准DH法实战——从连杆坐标系到运动学方程

本文详细介绍了机械臂运动学建模中的标准DH法,从连杆坐标系的建立到运动学方程的求解,提供了实用的操作步骤和代码示例。通过DH参数表和变换矩阵的构建,帮助读者快速掌握机械臂运动学建模的核心技术,并解决实际应用中的常见问题。

oo7890的博客 211

SAP ABAP 模糊查询的两种写法

CONCATENATE ‘% 变量X %’ INTO lv_maktx.直接在 select 中 的条件里写 where like “变量x”SAP 可以实现模糊查询 (但个人感觉不如直接输星号*方便)方法二:(适用于不知道字符串的内容,例如选择界面)方法一:(适用于知道字符串的内容)这种方法会导致,输入的变量不能为空。实现模糊查询有下面两种方法。在主数据已经找到以后,

月阴荒的博客 7087

电路保护设计实战:保险丝选型、I²t计算与多级协同方案

电路保护是电子系统可靠性的基石,其核心原理是在发生过流或过压等异常情况时,及时切断故障回路,防止能量持续灌入导致元器件损坏甚至安全事故。从技术价值看,优秀的保护设计能显著提升产品寿命、安全性和市场口碑,尤其在快充、电动汽车等高功率密度应用中,保护器件已成为不可或缺的安全屏障。在众多保护器件中,保险丝作为最基础的过流保护元件,其正确选型与应用直接关系到保护效果。本文聚焦于保险丝选型的实战细节,深入解析了I²t(电流平方时间积)这一关键参数,它定义了熔断所需的能量阈值,是匹配被保护半导体器件耐受能力、实现有效保

weixin_42662171的博客 309

Sql之left join(左关联)、right join(右关联)、inner join(自关联)的区别

首先来个图例: left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录  right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录 inner join(等值连接) 只返回两个表中联结字段相等的行 举例如下:  -------------------------------------------- 表A记

程序猿开发日志【学习永无止境】 7万+

拯救者R7000用户实测:用华为电脑管家11.0版搞定MatePad Pro多屏协同(附驱动与显卡避坑指南)

本文详细介绍了如何在非华为笔记本联想拯救者R7000上安装华为电脑管家11.0版,实现与MatePad Pro的多屏协同功能。从驱动优化到显卡配置,提供了全面的避坑指南和实用技巧,帮助用户解决连接失败等常见问题,提升跨设备生产力体验。

weixin_30788619的博客 398

从分立到集成:深入解析Buck电路的核心模块与设计权衡

本文深入解析Buck电路的核心模块与设计权衡,从分立到集成方案的优缺点对比。详细探讨Buck电路的高效能量转换、小型化设计及灵活电压调节能力,并结合实际案例分享分立方案中的PWM生成、反馈环路设计及保护电路优化经验,为工程师提供实用设计参考。

weixin_29306317的博客 223

SAP ABAP OPEN SQL 模糊查询的方法

更多内容关注公众号:SAP Technical 各位可以关注我的公众号:SAP Technical SAP模糊查询的方法: 其中 MAKTX_TXT 为传进来的参数 *声明一个变量 DATA: MAKTX_EXP LIKE MAKT-MAKTX. *拼接%% CONCATENATE '%'MAKTX_TXT'%' INTO MAKTX_EXP. *用like查询 SELECT ...

matinal 當冬夜漸暖 。公众号:matinal 2428

mapbox进阶,百度地图、高德地图纠偏,使用在线网络地图加载插件

本文详细介绍如何基于mapbox-gl v3.8.0版本百度地图、高德地图纠偏,使用在线网络地图加载插件,亲测可用。希望能帮助到您。一起学习,加油!加油!

我哎GIS博客 750

【ABAP】模糊查询不拼接%

模糊查询不拼接%

程卓的博客 2416

【CANoe示例分析】SOMEIPWithvCDL_16

本示例来源于官方提供的示例工程:0004_基于vCDL实现SOMEIP通信_Demo\0004_基于vCDL实现SOMEIP通信_Demo\SOMEIPWithvCDL。

jasonj333 567

车载以太网-ARP 动态 静态

车载以太网-ARP 动态 静态

天赐好车的博客 409

ABAP SELECT查询的模糊条件和字段问题

和数据库的交互是ABAP语言中很重要的一个部分,这里记录几个我遇到过的有关SELECT的问题 1.关于ABAP通配符*,+,%,_ 以及转义符#和ESCAPE 字符串操作中的通配符:*:多位字符,+:一位字符,#:转义字符 在字符串进行模糊匹配时,用关系运算符CP,如 if str CP ‘*sss’. OPEN SQL条件中通配符:%:多位字符,_:一位字符,ESCAPE:转义字符 在使用...

yiyuzz的博客 1万+

拼多多Android面试题及参考答案

Android 系统采用分层架构设计,从底层到上层依次为Linux 内核层硬件抽象层(HAL)系统运行时库层应用框架层应用层,共五层。位置:Linux 内核层与系统运行时库层之间,是“硬件与系统的隔离层”。核心作用:屏蔽硬件差异,为上层提供统一的硬件访问接口,解决 Android 开源与硬件厂商闭源的矛盾,具体功能如下:硬件能力抽象:将硬件的具体实现(如不同厂商的摄像头芯片、音频芯片)封装为标准化接口(如Camera HALAudio HAL。

大模型大数据攻城狮的专栏 1284

SAP ABAP RFC接口开发实战:如何优雅处理模糊查询与日期区间条件

本文深入探讨了SAP ABAP RFC接口开发中处理模糊查询与日期区间条件的实战技巧。通过构建通用处理模块、智能补全逻辑及动态WHERE条件构建,实现接口的健壮性与高性能。重点分析了模糊查询的索引优化与日期区间处理,并强调了异常处理与SQL注入防范,为开发稳定高效的企业级数据服务提供专业指导。

silver的专栏 864

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

ABAP开发核心:RANGE表与SELECTION-PTIONS深度解析与实战

在数据库查询与数据筛选领域,条件表达式是构建高效查询的基石,其原理在于通过标准化的结构定义比较逻辑与值范围,从而实现精确或模糊的数据检索。这一技术价值在于提升程序健壮性、优化用户体验并确保数据访问安全,广泛应用于报表开发、接口设计及权限控制等场景。本文聚焦SAP ABAP开发中的RANGE表结构及其前端组件SELECTION-OPTIONS,深入剖析其四字段协同机制与动态构建技巧,并针对模糊查询与性能优化等常见陷阱提供实战解决方案,帮助开发者掌握这一处理复杂筛选逻辑的核心工具。

weixin_34178244的博客 339

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

从C#到ABAP:跨平台数据查询的避坑指南(含日期校验与模糊匹配)

本文针对C#与SAP ABAP跨平台数据查询的常见痛点,提供了详细的避坑指南。重点解析了通过RFC接口进行数据交互时,如何正确处理日期格式校验、数字区间边界以及模糊查询参数的安全传递,确保查询的稳定与高效。

pandas7gardener的博客 214

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

利用 NATIVE SQL 实现不区分供应商名字大小写进行模糊查询

利用 NATIVE SQL 实现不区分供应商名字大小写进行模糊查询

IT小职员 768

SAP BSP实战:用ABAP在SE80中快速开发企业内部网页工具

在企业信息化环境中,如何快速交付轻量级的Web应用是IT团队常面临的挑战。基于SAP NetWeaver的BSP(Business Server Pages)技术,允许ABAP开发人员利用现有技能,在SE80中直接创建由ABAP逻辑和HTML模板构成的网页应用,并通过SICF服务发布为浏览器可访问的URL。其MVC分层模式有效分离了业务逻辑、数据访问与页面展示,特别适合物料主数据查询、审批列表、Excel数据导入等内部工具场景。与Fiori和外部Java Web相比,BSP无需额外前端工程和独立服务器,实施

weixin_33922670的博客 346

sql模糊查询

 SELECT * FROM user WHERE name LIKE %三%;将会把name为“张三”,“三脚猫”,“唐三藏”等等有“三”的全找出来;在进行数据库查询时,有完整查询和模糊查询之分。一般模糊语句如下:SELECT 字段 FROM 表 WHERE 某字段 Like 条件其中关于条件,SQL提供了四种匹配模式:1,%:表示任意0个或多个字符。

学习 探索 求生 3507

ABAP OPEN SQL 模糊查询的方法

SAP模糊查询的方法: 其中 MAKTX_TXT 为传进来的参数。

matinal 當冬夜漸暖 。公众号:matinal 695

SAP ABAP 模糊查询

where mara~ERSDA like '201911%' %:模糊查询 _ :精确查找

萌萌你社会哥 3287
上一篇: Code2Word实战:打造高效代码粘贴到Word的终极方案
下一篇: 深度学习中的warmup策略:原理、实现与优化效果
vim8coder
博客等级 码龄1天 0粉丝 1693原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值