SAP ABAP调用BAPI时COMMIT WORK AND WAIT未按照预期同步提交问题分析

目录

背景:

BAPI的执行原理:

更新进程的处理流程:

V1更新和V2更新区别:

COMMIT WORK AND WAIT不生效的原因:

标准BAPI事务模型规范:

如何解决:

1:分组处理

2:SET UPDATE LOCAL TASK

3:使用时间戳+WHILE

 4:使用模式为 U 或 V 的 ENQUEUE_XXXX 功能模块

5:增强


背景:

在做ABAP开发时,经常会有连续调用BAPI的需求,比如先创建销售订单,再依据销售订单创建交货单,再对交货单进行过账等类似的一连串调用,这种类似的场景往往需要前一步操作的数据完全写入数据库才能进行一下个步骤,但是数据写入底表是需要时间的,如果一些业务数据比较复杂,可能在调用下一个BAPI时会因为数据尚未写入底表而导致BAPI报出单据不存在等类似的错误消息(如果BAPI是以异步提交方式处理),项目实施中往往能看到不同的处理方式,比如WAIT UP TO XXX SECONDS之类的操作,但并不推荐这么去做,因为往往会浪费一些不必要的时间,本文将结合笔者自身经验来分析这个问题,提供一些不同的解决方式,根据实际情况进行选择。


BAPI的执行原理:

大部分BAPI执行的大概过程如下:

:报表程序中调用BAPI;

②③④:BAPI内部经过一系列检查处理后,将需要更新提交的数据统一注册到更新进程LUW中;

如果BAPI本身比较规范的话,一般会有类似COMMIT_WORK以及COMMIT_WORK_AND_WAIT等类似的传入参数,如果我们需要BAPI调用完之后要立即对生成的单据进行后续操作,那么我们最好是通过参数使其内部以同步方式提交,或者使其内部不要提交,由调用者来决定是否已同步方式提交(AND WAIT附加项决定了更新进程是否以同步模式提交),避免BAPI内部异步提交COMMIT WORK(没有AND WAIT)。

在碰到COMMIT WORK语句后,之前注册的所有更新函数将按照顺序依次执行⑦⑧⑨⑩,并且BAPI中产生的锁会一并带到更新进程中,优先执行V1更新,V1更新执行完毕后,数据库进行提交,并且释放所有的锁,V2进程不会再有加锁的操作,V2执行完毕后,整个数据库提交过程完毕,如果更新进程中出错,则会自动触发回滚,并收到一条Dump快件;

如果是以同步提交(COMMIT WORK AND WAIT)触发更新,则程序会在更新进程执行完毕后才返回至序,如果是以异步方式(COMMIT WORK)触发更新,则程序不会等待更新进程执行完毕,立即进行后续处理,这种时候如果要对该BAPI产生的单据进行下一步处理,则可能会出现单据不存在,或者锁定报错;

某些BAPI中的更新进程中甚至会有上图中的处理流程,将一部分数据处理用IN BACKGROUD TASK的方式去触发异步事务性函数(tRFC)处理,尽管这种方式已被SAP标记为过时的方式,但是仍然有一些BAPI中采用了这种方式,比如报工BAPI:BAPI_PRODORDCONF_CREATE_TT,其中针对于货物移动的处理就是采用的这种方式,使用IN BACKGROUND TASK触发的事务会立即以异步方式在独立进程中执行,所以AND WAIT不会对其生效。


更新进程的处理流程:

官方说明:

After the transaction closes, the dialog work process closes the VBHDR entry (the beginning of the update for the update request), and searches for an update server for the U1 update. This described in more detail in Update Dispatching with Load-Balancing.

The update server distributes the tasks to an update work process. This processes the V1 modules of the update request, triggers a COMMIT to the database, and releases the R/3 locks on the update request (see The SAP Lock Concept). Then the work process searches for an update server for the U2 update, if the U2 update modules exist.

These are forwarded from a U2 update server to a U2 work process that processes the U2 modules, and triggers a COMMIT on the database.

The following graphic displays this process from the view of various work processes. It also displays the time at which changes to the database are made.

官方说明:

The U1 modules are processed by transmitting the contents of the update table VBMOD and VBDATA to the application tables of the database. The changes are actually in the desired tables in the database only at the end of the database LUW in which it occurs. The SAP locks are released and, if V2 update modules exist, the V2 update is started. This is similar to the V1 update with the exception that there are no locks that have to released and no search for a process for further processing.


V1更新和V2更新区别:

V1 and V2 Update Modules | SAP Help Portalicon-default.png?t=N7T8https://help.sap.com/docs/SAP_NETWEAVER_700/10970dcb6c531014af68b7c1d32e9eab/e5de86f335cd11d3acb00000e83539c3.html更新函数模块有两种类型,SAP系统区分了主要、时间关键型(V1)和辅助、非时间关键型(V2)更新模块。该系统还支持对定期使用的功能模块进行集体运行

这种区别允许系统在不太关键的更改之前处理关键的数据库更改。

  • V1 模块包含关键或主要更改;这些影响在 SAP 系统中具有控制功能的对象,例如订单创建或物料库存更改。

  • V2 模块包含不太重要的二次更改。这些是纯粹的统计更新,例如结果计算。

V1 模块在同一应用程序服务器上的单个更新工作进程中连续处理。这意味着它们属于同一个数据库 LUW,并且可以反转。此外,V1 更新是在创建更新的事务的 SAP 锁下执行的(请参阅 SAP 锁概念 )。这确保了数据保持一致;无法同时更改要更新的对象。

所有 V2 更新都在单独的 LUW 中执行,而不是在创建它们的事务的锁定下进行。如果 SAP System 包含 V2 更新的工作流程,则这些更新仅在此工作流程中执行。如果不是这种情况,则 V2 组件由 V1 更新过程处理。

更新的所有 V1 模块都必须在 V2 模块之前处理。

简而言之,V1更新比V2更新优先级更高,V1更新在被触发时,可以根据是否附加AND WAIT来决定是同步更新还是异步更新,并且如果V1和V2更新同时被注册时,V2的同步与否和V1保持一致(如果V1被同步提交触发,则V2也会同步提交);如果仅注册了V2更新,则V2更新始终以异步方式执行(即使通过SET UPDATE LOCAL TASK指定了本地更新,或者使用了COMMIT WORK AND WAIT)。


COMMIT WORK AND WAIT不生效的原因:

知道了BAPI以及更新进程的原理,那同步提交不生效的原因就变得清晰了,大概有以下几种情况:

  • BAPI没有NO_COMMIT以及COMMIT_WORK_AND_WAIT等类似的参数,直接在内部执行了COMMIT WORK语句(没有附加AND WAIT),所以已经注册的SAP LUW将会被异步执行更新,这种时候我们调用BAPI之后写的COMMIT WORK AND WAIT语句针对的提交则是BAPI内部COMMIT WORK语句之后,到我们主程序COMMIT WORK AND WAIT之间产生的数据库更新语句,通常情况下是没有东西可更新的,所以调用BAPI之后的同步提交将不会是我们预期的效果,类似的BAPI有BAPI_MATERIAL_SAVEDATA,BAPI_ENTRYSHEET_CREATE 和 BAPI_PO_RESET_RELEASE等。
  • BAPI本身提供了NO_COMMIT参数,但是调用时未传入该参数,导致BAPI内部仍然以异步方式进行了提交,故调用侧的同步提交不生效,原因同上,类似的BAPI有BAPI_PO_RELEASE,CO_SE_PRODORD_OPR_CREATE等。
  • BAPI在更新进程中触发了新的异步远程更新进程,如报工BAPI:BAPI_PRODORDCONF_CREATE_TT,COMMIT WORK AND WAIT只会等到IN UPDATE TASK注册的更新进程处理完毕,并不会等待IN BACKGROUND TASK注册的更新进程。
  • BAPI内部注册的更新进程均为V2更新,不包含V1更新,这种情况,即使通过SET UPDATE LOCAL TASK设置了本地更新,或者以同步提交进行触发,更新进程均以异步方式触发。

标准BAPI事务模型规范:

BAPI 事务模型必须为用户提供明确的事务控制权。因此,如果同时调用多个 BAPI,则调用方可以自行决定何时执行 COMMIT WORK(或者,视情况而定,执行 ROLLBACK WORK)。这意味着 BAPI 本身不能(通常)执行 COMMIT WORK 命令。
以下限制适用于将多个 BAPI 合并到一个 LUW 中:

  • 如果实例是由写入 BAPI 创建、修改或删除的,则读取 BAPI 只有在发生 COMMIT WORK 时才能访问最新数据。
  • 不能在一个 LUW 中的同一实例上进行两次写入访问。例如,不能先在同一 LUW 中创建然后更改对象。但是,您可以在 LUW 中创建同一对象类型的多个实例。

尽管 SAP 提供的所有 BAPI 都应遵循此事务模型,但也有例外,并不是所有BAPI都会按照这个规范去操作,所以碰到这种情况,则需要按照实际情况去分析处理。


如何解决:

解决该问题的方式有很多种,其中最不推荐的则是WAIT UP TO XXX SECONDS的方式,尽管高版本中可以选择WAIT UP TO '0.1' SECONDS的方式,但对于一些强迫症开发者来说,多余浪费的0.01秒都是不能容忍的,所以当BAPI本身包含NO_COMMIT或者COMMIT_WORK_AND_WAIT这些参数时,我们最好使用这些参数来抑制BAPI内部提交,由调用者通过BAPI_TRANSACTION_COMMITWORK(WAIT = 'X')或者COMMIT WORK AND WAIT来触发同步提交,如果没有这些参数,BAPI内部始终以COMMIT WORK异步提交时,则可以有以下几种替代方式来处理异步提交问题。

至于判断BAPI内部是否直接异步提交最简单的办法就是打个断点,观察执行时断点是否会停留在该语句位置。

1:分组处理

比如针对一批数据的每一条,都需要调用完创建BAPI之后再调用修改BAPI对其修改,则可以先统一进行创建处理,再进行修改处理,通常当所有单据创建完之后,最先创建的单据应该早已提交更新完毕,示例如下:

改造前:

...
LOOP AT objects.
  CALL FUNCTION 'BAPI_OBJECT_CREATE'.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    IMPORTING
      WAIT = 'X'.
  CALL FUNCTION 'BAPI_OBJECT_CHANGE'.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    IMPORTING
      WAIT = ' '.
  ENDIF.
ENDLOOP.

改造后:

...
LOOP AT objects.
  CALL FUNCTION 'BAPI_OBJECT_CREATE'.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    IMPORTING
      WAIT = ' '.
ENDLOOP.
LOOP AT objects.
  IF object not exists.
    APPEND object TO object_work_list
  ELSE.
    CALL FUNCTION 'BAPI_OBJECT_CHANGE'.
    CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
      IMPORTING
        WAIT = ' '.
  ENDIF.
ENDLOOP.
Reprocess the objects in object_work_list and wait

2:SET UPDATE LOCAL TASK

SET UPDATE LOCAL TASK会忽略IN UPDATE TASK附加项,使BAPI中数据库更新的过程以本地更新的方式在当前会话内部执行,而非注册到VB更新进程中,并且COMMIT WORK会隐式的添加同步提交,但每次COMMIT WORK之后,本地更新会被禁用,所以需要的时候要在每次调用BAPI之前执行SET UPDATE LOCAL TASK语句来开启本地更新。

该方式仅在BAPI的更新进程包含V1更新时(或者同时包含V1和V2)适用,如果BAPI的更新进程仅包含V2更新或者V1更新中又嵌套了异步RFC更新,则不适用该方式,例如BAPI_PRODORDCONF_CREATE_TT就不适合这种解决方式。


3:使用时间戳+WHILE

通过下面的代码,可以将等待时间减少到系统真正提交所消耗的时间 ,何时提交完成何时退出循环(最大设置等待时间内),从而不浪费任何额外的时间。

DATA: BEGIN OF ls_time,
        start   TYPE timestampl,
        now     TYPE timestampl,
        elapsed TYPE tzntstmpl,         " 目前已运行时间
        limit   TYPE tzntstmpl VALUE 3, " 最大等待时间
      END OF ls_time.

GET TIME STAMP FIELD ls_time-start.

WHILE ls_time-elapsed < ls_time-limit.

  SELECT SINGLE * INTO @DATA(LS_XXX) FROM XXXX WHERE ...
 
  IF LS_XXX IS NOT INITIAL.
     EXIT.
  ENDIF.

  GET TIME STAMP FIELD ls_time-now.

  ls_time-elapsed = cl_abap_tstmp=>subtract(
    tstmp1 = ls_time-now
    tstmp2 = ls_time-start
  ).

ENDWHILE. " elapsed time

 4:使用模式为 U 或 V 的 ENQUEUE_XXXX 功能模块

在调查这个问题时看到的一个方法,当您使用适当的功能模块并使用模式 U、V(或 W)时,它将检查锁是否冲突。此处的 SAP 帮助示例:使用锁定模式 U、V 和 W - SAP 锁定概念 - SAP 库中对此进行了详细说明。这里没有提到的是 _WAIT 参数。将此参数设置为 (abap_true 或 'X') 将使函数调用等待预定时间以释放锁。美妙之处在于,这不仅适用于您设置锁定的标准模式,而且还适用于碰撞检查。

CALL FUNCTION 'ENQUEUE_EVVBAKE'
  EXPORTING
    mode_vbak      = 'V'              " Lock mode for table VBAK
    vbeln          = l_sales_order    " 02th enqueue argument
    _wait          = abap_true
  EXCEPTIONS
    foreign_lock   = 1
    system_failure = 2
    others         = 3.

这种方法的好处是无需手动实现等待逻辑,如果首次检查锁冲突失败,则默认循环5次,每次等待1秒,由配置参数文件决定,主要参数如下:

enq/client/delay_max:控制最长等待时间,默认5秒;

enq/client/delay_max_refine:控制每秒重试次数,默认1次,最多100次;

enq/client/delay_jitter:防止多用户同时请求导致死锁的偏移量设置;

虽然可以通过配置文件去改变每秒循环次数,但由于此种方式在默认情况下等同于循环等待1秒的操作,并且wait本身就是隐式提交的过程,所以不推荐该方式处理。


5:增强

如果以上方式都不能满足需求,则可以考虑将BAPI复制出来,找到其中控制异步提交的逻辑,通过增强来添加同步提交控制参数,使得BAPI内部以同步方式提交,一个案例如下:SAP CO11N BAPI_PRODORDCONF_CREATE_TT连续报工异步更新导致COGI解决方案-优快云博客icon-default.png?t=N7T8https://blog.youkuaiyun.com/DeveloperMrMeng/article/details/139811212?spm=1001.2014.3001.5501SAP 报工BAPI中的 UPDATA TASK 和 BACKGROUND TASK_abap in backround task 和in update task的区别-优快云博客icon-default.png?t=N7T8https://blog.youkuaiyun.com/DeveloperMrMeng/article/details/140174352?spm=1001.2014.3001.5501


参考链接:

Updates in the SAP System (BC-CST-UP) | SAP Help Portalicon-default.png?t=N7T8https://help.sap.com/docs/SAP_NETWEAVER_701/6d9bbcd26c4b101488b4a6a282b09136/5f6f8337dd34ca76e10000009b38f8cf.html?locale=en-US&q=V2%20Update%28Asynchronous%20Update%29


以上,如有不对,欢迎指正。

<think>好的,我现在需要帮助用户解决在SAP ABAP中使用BAPI删除VK12标记的问题。首先,我需要理解用户的具体需求。VK12通常用于维护销售价格条件,而用户提到的“删除VK12标记”可能指的是删除通过VK12维护的条件记录或相关数据。不过,用户明确提到使用BAPI,所以需要找到对应的BAPI函数。 根据用户提供的引用内容,引用[2]提到了VK11/VK12/VK13的保存增强,这可能与条件记录的维护有关。但用户的问题是关于删除操作,所以可能需要查找相关的BAPI,比如BAPI_PRICES_CONDITIONS或类似的函数。 首先,我需要确认是否有标准的BAPI可以删除条件记录。通常,条件记录的维护可能使用BAPI_PRICES_CONDITIONS,这个BAPI可能支持创建、修改和删除操作。删除操作通常通过设置删除标志或指定操作类型来实现。例如,在调用BAPI,可能需要传递一个操作类型参数,如'D'表示删除。 接下来,我需要检查这个BAPI的参数结构。例如,在调用BAPI_PRICES_CONDITIONS,需要填充条件表头信息(如应用领域、条件类型、物料号、客户等)以及条件记录的有效期。同,在条件项中可能需要设置删除标记,比如在结构体里有一个DELETE_FLAG字段,或者通过特定的操作类型参数来指示删除。 另外,用户提到的引用[1]中关于BAPI_CONTRACT_CREATE的问题,虽然不直接相关,但提示了在使用BAPI可能会遇到dump的情况,因此在编写代码需要注意参数的正确性和异常处理。 现在,我需要回忆或查找BAPI_PRICES_CONDITIONS的使用方法。该BAPI通常需要传递一个条件表头(如KONH结构)、条件项(KONP结构),以及可能的控制参数。在删除,可能需要设置DELETE_X字段为'X',或者在调用指定操作模式为删除。 例如,在调用BAPI,可能需要在IM_CONDITION结构中的OPERATION字段设置为'D',或者在表参数中设置DELETE_FLAG为'X'。此外,还需要确保传递了足够的关键字段来唯一标识要删除的条件记录,比如条件类型(KSCHL)、应用(KAPPL)、物料(MATNR)、客户(KUNNR)等。 可能的操作步骤包括: 1. 确定要删除的条件记录的关键字段。 2. 调用BAPI_PRICES_CONDITIONS,设置删除标志或操作类型为删除。 3. 执行BAPI后检查返回消息,确认删除是否成功。 4. 如果必要,调用BAPI_TRANSACTION_COMMIT提交事务。 需要注意的是,删除操作可能受到权限和业务规则的限制,因此需要确保用户有相应的权限,并且在业务上允许删除该条件记录。 此外,可能还需要考虑条件记录的有效期,因为某些系统可能不允许直接删除,而是通过设置有效期结束日期来间接实现。这种情况下,可能需要使用修改操作来更新有效期,而不是直接删除。 如果BAPI_PRICES_CONDITIONS不支持直接删除,可能需要查找其他BAPI,比如CONDITION_DELETE或类似函数。或者,可能需要使用RFC调用函数模块PRICING_CONDITION_DELETE,但需要确认是否存在这样的标准函数。 总结可能的步骤: - 使用BAPI_PRICES_CONDITIONS,设置删除标志。 - 填充必要的关键字段。 - 处理返回消息。 - 提交事务。 示例代码可能如下: 调用BAPI_PRICES_CONDITIONS,传递条件表头信息,并在条件项中设置DELETE_FLAG为'X'。然后调用BAPI_TRANSACTION_COMMIT提交更改。 但需要验证这些步骤是否正确,是否有遗漏的参数或步骤,比如是否需要先调用BAPI_PRICES_CONDITIONS的GETDETAIL来获取现有记录,或者是否需要处理条件表的多条目情况。 另外,用户可能需要处理增强或自定义检查,比如引用[2]提到的价格等级检查,但在删除操作中这些可能不适用,但需要注意系统是否有删除的增强限制。 最后,确保在代码中加入错误处理,例如检查BAPI返回的消息,如果存在错误则回滚事务,否则提交。</think>在SAP ABAP中删除VK12维护的条件记录标记,通常需要使用标准BAPI或函数模块实现。以下是详细操作说明及示例代码: ### 操作步骤 1. **确定条件记录关键字段** 需明确要删除的条件记录参数,包括: - 条件表 (`KOTAB`) - 应用领域 (`KAPPL`),如销售用"V" - 条件类型 (`KSCHL`),如PR00 - 物料号 (`MATNR`) - 客户编号 (`KUNNR`) - 有效期起止日期 (`DATAB`, `DATBI`) 2. **使用BAPI_PRICES_CONDITIONS** 该BAPI支持条件记录的增删改操作,通过`OPERATION`字段控制操作类型: ```abap DATA: lt_condition TYPE TABLE OF bapicondct, lt_return TYPE TABLE OF bapiret2. " 设置删除操作标识 APPEND VALUE #( operation = 'D' ) TO lt_condition. " 调用BAPI执行删除 CALL FUNCTION 'BAPI_PRICES_CONDITIONS' EXPORTING condition_header = ls_header " 填充KAPPL/KSCHL/MATNR等关键字段 TABLES condition_items = lt_condition return = lt_return. " 检查执行结果 READ TABLE lt_return WITH KEY type = 'E' TRANSPORTING NO FIELDS. IF sy-subrc = 0. CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'. ELSE. CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'. ENDIF. ``` 3. **替代方案:函数模块PRICING_CONDITION_DELETE** 直接调用底层函数删除条件记录: ```abap DATA: lv_knumh TYPE knumh. " 条件记录编号 " 通过条件编号执行删除 CALL FUNCTION 'PRICING_CONDITION_DELETE' EXPORTING knumh = lv_knumh EXCEPTIONS no_authority = 1 not_found = 2 OTHERS = 3. IF sy-subrc = 0. COMMIT WORK. ELSE. ROLLBACK WORK. ENDIF. ``` ### 关键注意事项 1. **权限检查** 删除操作需要授权对象`V_KNVV_VKO`和`V_KNVV_VTW`[^2] 2. **有效性控制** 若条件记录已被业务单据引用,系统可能阻止删除 3. **增强检查** 需确认是否实施了用户出口或BADI增强,如`USEREXIT_PRICING_CHECK`可能包含删除限制逻辑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeveloperMrMeng

觉得有用的佛系投币哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值