ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SAP SCDO配置指南:为自建表实现标准变更记录

SAP SCDO配置指南:为自建表实现标准变更记录

1. 项目概述:为什么要在自建表上启用更改记录?

在SAP的日常开发与运维中,我们经常会创建大量的自定义表(Z表或Y表)来满足特定的业务需求。这些表可能存储着客户主数据扩展信息、业务单据的补充字段,或是复杂的配置参数。随着系统使用时间的增长,一个现实问题逐渐浮现:“谁在什么时候修改了这条数据?具体改了哪些字段?”对于关键业务数据,追溯其变更历史不仅是审计合规的硬性要求,更是排查问题、分析业务操作行为的宝贵线索。

SAP标准表(如物料主数据、销售订单表头)的变更记录,通常由系统标准机制(如物料凭证、单据流)或更改指针(Change Pointer)自动管理。但对于我们自建的表,SAP并不会自动为我们记录每一次的增删改操作。这时,就需要我们手动为其“赋能”,而SCDO(SAP Change Document Object)正是实现这一功能的官方、标准且强大的工具。

简单来说,这个项目就是为你的自建表“安装”一个“行车记录仪”。通过配置SCDO,你可以将每一次对自建表数据的修改(通过SM30、BAPI、程序直接UPDATE等),都像标准表一样,详细地记录到SAP标准的更改凭证表CDHDR(抬头)和CDPOS(行项目)中。这不仅使得数据变更可追溯,还能无缝对接SAP的审计信息系统(Audit Information System, AIS)或其他需要分析变更历史的报表。

2. 核心概念与前置准备

在动手配置之前,必须理解几个核心概念,这决定了后续配置的成败。

2.1 SCDO、CDHDR与CDPOS的关系

你可以把这三者理解为一个完整的记录系统:

  • SCDO (Change Document Object):这是配置层。它定义了一个“监控对象”,告诉SAP:“我要监控哪张表(或哪组表),监控哪些字段,以及如何生成变更记录的描述文本”。它本身不存储数据,而是生成变更记录的“规则引擎”。
  • CDHDR (Change Document Header):这是记录抬头表。每当发生一次变更(可能涉及多条记录),就会在这里生成一条抬头记录。关键字段包括:变更凭证号(CHANGENR)、对象类型(OBJECTCLAS,对应SCDO中的对象类)、对象ID(OBJECTID,通常是表的关键字段组合)、用户名(USERNAME)、日期时间(UDATE,UTIME)、事务码(TCODE)等。
  • CDPOS (Change Document Position):这是记录行项目表。它详细记录了每次变更中,具体哪个字段从什么旧值改成了什么新值。关键字段包括:对应的CHANGENR、表名(TABNAME)、字段名(FNAME)、旧值(VALUE_OLD)、新值(VALUE_NEW)等。

工作流程:当程序调用SCDO生成的函数模块来写日志时,SCDO根据配置的规则,将变更信息结构化,然后向CDHDR插入一条抬头,并向CDPOS插入若干条行项目。

2.2 确定你的“变更对象”

这是最关键的一步。你需要明确:

  1. 监控哪张表?通常是你的自建透明表(Transparent Table)。也可以是簇表(Cluster Table)或池表(Pool Table),但透明表最为常见。
  2. 如何唯一标识一条记录?OBJECTID的构成。它通常是表的主键字段(如客户扩展表的CLIENTKUNNR)拼接而成的字符串。SCDO要求你定义一个最大长度为70字符的OBJECTID
  3. 监控哪些字段?不是所有字段都需要记录。通常只记录业务关键字段,技术字段(如创建者、创建时间)或频繁更新的非关键字段可以排除,以避免产生过多“噪音”日志。

2.3 事务码导航

整个配置过程主要涉及以下事务码:

  • SCDO:核心配置事务码,用于创建和管理变更凭证对象。
  • SE11:数据字典,用于查看表结构,确认字段名和数据类型。
  • SM30:通用表维护,常用于测试自建表的维护及触发变更记录。
  • SE16N:通用数据浏览器,用于查询CDHDRCDPOS表,验证变更记录是否成功生成。

3. SCDO配置全流程详解

假设我们有一张自建表ZSD_CUST_EXT,用于存储销售客户的扩展信息,关键字段为MANDT(客户端)、KUNNR(客户编号)。我们需要监控ZNAME(客户简称)和ZREGION(所属区域)这两个字段的变更。

3.1 步骤一:创建变更凭证对象

  1. 执行事务码SCDO
  2. 在初始界面,输入你想要创建的变更对象名称。名称应以ZY开头,例如ZSD_CUST_CHG
  3. 点击“创建”按钮。
  4. 系统进入维护界面,通常包含以下几个标签页,我们需要逐一配置:

a. 基本数据 (Basic Data)

  • 对象类 (Object Class):这是一个命名空间,用于分组。对于自建对象,通常使用ZSD(根据模块自定义)或简单的ZCHANGE。这个值会存入CDHDR-OBJECTCLAS
  • 对象ID (Object ID):点击“对象ID”字段旁的输入帮助(F4),系统会弹出对话框让你定义OBJECTID的结构。
    • 在对话框中,你需要将表的关键字段拖拽到右侧的“组件”区域。例如,将MANDTKUNNR拖过去。
    • 系统会自动生成一个OBJECTID的示例,如800 00010001(假设MANDT=800,KUNNR=00010001)。
    • 你需要检查生成的OBJECTID长度是否超过70字符。对于客户编号这类固定长度字段,通常没问题。如果超长,可能需要考虑使用哈希或缩写。

b. 更改凭证 (Change Documents)这是核心配置页签。

  1. 创建更改凭证:勾选此项,表示为此对象激活更改凭证。
  2. 表 (Table):输入你要监控的表名,即ZSD_CUST_EXT
  3. 更改类型 (Change Type):通常选择U(更新)。I(插入)和D(删除)也可以勾选,但实践中,插入和删除操作有时通过U类型也能覆盖(例如,从初始状态变为有值可视为更新)。
  4. 字段选择
    • 点击“字段选择”按钮,系统会列出ZSD_CUST_EXT的所有字段。
    • 在这里,你需要显式地选择需要监控的字段。只勾选ZNAMEZREGION务必不要勾选所有字段,尤其是MANDT,KUNNR(关键字段变化意味着另一条记录),以及ERNAM,ERDAT,AENAM,AEDAT等系统自动填充的字段。
    • 对于每个选中的字段,你还可以在“文本”列维护一个简短的描述,这个描述会出现在一些标准报表中,方便识别。

c. 文本 (Texts)这里配置如何生成变更记录的描述文本,该文本会存储在CDHDR-CHANGETEXT字段中。

  • 更改文本对象 (Change Text Object):可以复用对象类,如ZSD
  • 更改文本ID (Change Text ID):定义一个ID,如001
  • 文本 (Text):这是重点。你需要编写一段文本模板,使用&符号引用字段。例如:客户 &KUNNR& 的扩展信息被修改系统在写日志时,会用实际的关键字段值替换&KUNNR&,生成如“客户 00010001 的扩展信息被修改”的文本。

d. 生成器 (Generator)这是最后一步,也是将配置“编译”成可执行代码的一步。

  1. 切换到“生成器”标签页。
  2. 点击“生成”按钮。
  3. 系统会弹出对话框,提示将生成一个函数组和一系列函数模块。输入一个以Z开头的函数组名,例如ZSD_CHG_DOC,然后执行。
  4. 生成成功后,系统会创建以下关键函数模块:
    • ZSD_CUST_CHG_WRITE_DOCUMENT核心函数,用于在程序中调用以写入变更记录。它需要传入旧表数据(OLD_TAB)和新表数据(NEW_TAB)进行比对。
    • ZSD_CUST_CHG_*等其他辅助函数。

重要提示:生成操作会覆盖同名的函数组。如果之前已生成并做过修改,请务必做好备份。生成后,务必使用SE37检查生成的函数模块,特别是WRITE_DOCUMENT的接口参数。

3.2 步骤二:在数据维护程序中集成写日志逻辑

仅仅配置SCDO,数据变更时并不会自动记录。你必须在修改自建表数据的程序点,显式调用上一步生成的WRITE_DOCUMENT函数模块。

最常见的场景是在表维护对话框(SM30)中启用。这通常通过维护表的表维护生成器(SE54)来实现,在维护事件(EVENT)中编写代码。

以SM30维护为例的集成步骤:

  1. 执行事务码SE54,输入表名ZSD_CUST_EXT,选择“表维护生成器”。
  2. 进入后,找到“事件”相关的配置(不同SAP版本路径可能不同,通常在“环境”->“事件”或类似菜单下)。
  3. 我们需要在05(保存前)06(保存后)事件中编写ABAP代码。通常在05(保存前)事件中获取修改前的旧数据(OLD_TAB),在06(保存后)获取新数据(NEW_TAB)并调用写日志函数。
    • 事件 05 (Before Saving Data):
      DATA: lt_old_tab TYPE TABLE OF zsd_cust_ext. FIELD-SYMBOLS: <fs_old_tab> TYPE STANDARD TABLE. ASSIGN (‘(SAPLSDAT)ZSD_CUST_EXT_OLD[]’) TO <fs_old_tab>. IF sy-subrc = 0. lt_old_tab = <fs_old_tab>. ENDIF. EXPORT lt_old_tab TO MEMORY ID ‘ZSD_CUST_EXT_OLD’.
      这段代码将SM30内部存储的旧数据暂存到ABAP内存中。
    • 事件 06 (After Saving Data):
      DATA: lt_old_tab TYPE TABLE OF zsd_cust_ext, lt_new_tab TYPE TABLE OF zsd_cust_ext. FIELD-SYMBOLS: <fs_new_tab> TYPE STANDARD TABLE. “ 从内存中取出旧数据 IMPORT lt_old_tab FROM MEMORY ID ‘ZSD_CUST_EXT_OLD’. FREE MEMORY ID ‘ZSD_CUST_EXT_OLD’. “ 获取新数据 ASSIGN (‘(SAPLSDAT)ZSD_CUST_EXT[]’) TO <fs_new_tab>. IF sy-subrc = 0. lt_new_tab = <fs_new_tab>. ENDIF. “ 调用SCDO生成的函数写日志 IF lt_old_tab IS NOT INITIAL AND lt_new_tab IS NOT INITIAL. CALL FUNCTION ‘ZSD_CUST_CHG_WRITE_DOCUMENT’ EXPORTING objectid = “这里需要拼接OBJECTID,例如基于第一条记录的关键字 tcode = sy-tcode utime = sy-uzeit udate = sy-datum username = sy-uname object_change_indicator = ‘U’ “更新模式 n_objectid = “同上,新对象的ID(通常与旧ID相同) n_utime = sy-uzeit n_udate = sy-datum TABLES old_tab = lt_old_tab new_tab = lt_new_tab. ENDIF.

      注意:上述代码是概念性示例。在实际开发中,OBJECTID的拼接逻辑需要根据你的表结构来写,通常循环LT_OLD_TAB,为每条记录的关键字段拼接成一个字符串。TCODE,UDATE,UTIME,USERNAME等参数通常直接传递SY系统变量即可。强烈建议直接参考SCDO生成函数模块的示例代码(在SE37中查看函数文档或示例程序)

对于BAPI或自定义报表更新:如果你是通过BAPI(如BAPI_*_CHANGE)或自己写的ABAP报表来更新ZSD_CUST_EXT表,那么你需要在更新语句(UPDATE,MODIFY,DELETE执行成功后,立即调用ZSD_CUST_CHG_WRITE_DOCUMENT函数。你需要自行在程序中保留更新前的数据镜像(OLD_TAB)用于比较。

4. 测试、验证与问题排查

配置和集成完成后,必须进行严格的测试。

4.1 测试流程

  1. 准备数据:使用SE16N或SM30,在ZSD_CUST_EXT中创建或选择一条测试记录(如KUNNR = ‘TEST001’)。
  2. 执行修改:通过SM30进入该表的维护视图,修改ZNAMEZREGION字段的值,然后保存。
  3. 查询日志
    • 打开SE16N,输入表名CDHDR
    • 设置筛选条件:OBJECTCLAS=ZSD(你在SCDO中配置的对象类),OBJECTIDLIKE%TEST001%(你的测试客户编号)。
    • 执行查询,你应该能看到一条新的变更凭证记录。记录下CHANGENR
    • 再查询表CDPOS,筛选条件CHANGENR等于上一步找到的凭证号,TABNAME=ZSD_CUST_EXT。你应该能看到具体的字段变更详情,包括FNAME(字段名)、VALUE_OLD(旧值)和VALUE_NEW(新值)。

4.2 常见问题与排查技巧

即使按照步骤操作,也可能会遇到各种问题。以下是我在多次实践中总结的“避坑指南”:

问题1:SM30保存后,CDHDR/CDPOS表中没有记录。

  • 排查思路
    1. 检查事件代码是否生效:在SE54的事件中,你的代码是否被正确保存并激活?可以在事件代码中设置断点或添加MESSAGE语句调试。
    2. 检查函数模块调用WRITE_DOCUMENT函数是否被成功调用?检查SY-SUBRC。最常见的原因是传入的OBJECTID为空或格式不正确。
    3. 检查数据对比:传入函数的OLD_TABNEW_TAB内表是否有数据?是否真的发生了字段值的变化?如果旧值和新值完全相同,SCDO可能不会生成记录。
    4. 检查SCDO字段选择:确认在SCDO配置中,你修改的字段确实被勾选为“监控字段”。漏选是最低级的错误,但也最常见。

问题2:变更记录生成了,但CDPOS中的VALUE_OLD和VALUE_NEW都是空的。

  • 原因与解决:这通常是因为字段的数据类型或长度问题。CDPOS表的VALUE_OLDVALUE_NEWCHAR(255)类型。
    • 超长字段:如果你的自建表字段长度超过255(如长文本LRAW类型),SCDO无法直接存储。需要在SCDO配置中,为该字段启用“文本更改凭证”(在字段选择界面有相关选项),这样变更内容会记录到CDTXT表中,而非CDPOS
    • 非字符类型:对于数值、日期、时间等类型,SCDO会将其转换为字符型存储。通常没问题,但需确保转换无误。

问题3:一次修改多条记录,只生成了一条CDHDR记录,这正常吗?

  • 解答:这取决于你调用WRITE_DOCUMENT函数的方式。该函数有一个TABLES参数OLD_TABNEW_TAB。如果你将多条记录的旧数据和新数据分别放在两个内表中,一次性传入函数,那么系统通常会为这一批变更生成一条CDHDR记录(一个CHANGENR),但会在CDPOS中为每条记录、每个变更的字段生成对应的行项目。这是正常且高效的行为。如果你想为每条记录生成独立的抬头,则需要循环每条记录单独调用函数。

问题4:自建表结构变更(增删字段)后,SCDO需要调整吗?

  • 解答必须调整!如果你在表ZSD_CUST_EXT中新增了一个需要监控的字段ZNEW_FIELD,你必须:
    1. 用SCDO重新进入你的变更对象ZSD_CUST_CHG
    2. 在“更改凭证”标签页,重新进行“字段选择”,勾选上新字段。
    3. 最重要的一步:重新进入“生成器”标签页,再次点击“生成”。这会更新函数模块ZSD_CUST_CHG_WRITE_DOCUMENT的逻辑,使其能识别和比较新字段。

    警告:重新生成会覆盖函数组。如果你在生成的函数组里做过自定义修改,务必在生成前备份或合并代码。

5. 高级应用与性能考量

对于大型、高频更新的自建表,启用更改记录需要谨慎考虑性能影响。

5.1 选择性记录与归档策略

  • 字段精选:只记录真正需要审计和追踪的关键业务字段。避免记录时间戳、计数器等频繁变动的技术字段。
  • 条件记录:你可以在调用WRITE_DOCUMENT的函数之前,在ABAP程序中加入判断逻辑。例如,只有特定用户、特定操作或字段值变化超过某个阈值时才写日志。
  • 定期归档CDHDRCDPOS表会随着时间推移急剧增长。需要建立定期归档(Archiving)作业,将历史变更数据从生产系统移至归档存储,以保障在线系统的性能。SAP提供了标准的归档对象CHANGEDOC来归档变更凭证。

5.2 在BTE、BADI或增强中集成

除了SM30和自定义报表,你的自建表可能通过BTE(Business Transaction Events)、BADI(Business Add-Ins)或其他增强点来更新。集成原则是相同的:在数据成功更新后,立即调用写日志函数。你需要在该增强点的实现代码中,获取更新前后的数据镜像,然后调用你的ZSD_CUST_CHG_WRITE_DOCUMENT函数。

例如,如果你在销售订单的SAVEBADI (BADI_ORDER_SAVE)中更新了自建表,那么就在该BADI的SAVE_DOCUMENT方法中,在COMMIT WORK之前,加入写变更记录的代码。

5.3 查询与分析变更记录

生成记录后,如何有效利用?

  • 标准报表:SAP提供了RSSCD100等标准报表来查询变更凭证。
  • 自定义报表:你可以直接连接CDHDRCDPOS表,根据OBJECTCLASOBJECTID(或TABNAME和关键字段值)编写定制化的审计报表。结合USR02(用户主数据)、TSTCT(事务码文本)表,可以展示更友好的信息,如用户名、事务码描述等。
  • 审计信息系统 (AIS):如果变更记录配置得当,可以无缝集成到SAP AIS中,满足严格的合规审计要求。

为自建表启用更改记录,初看是一套繁琐的配置,但一旦搭建完成,它将成为系统数据治理和运维中不可或缺的“黑匣子”。它带来的数据透明度、可追溯性和安全性,在应对关键问题排查和合规审查时,价值远超前期投入的精力。关键在于理解SCDO的工作原理,谨慎选择监控字段,并在正确的数据更新点牢固地集成写日志调用。

返回列表