文章目录
- 1.在表里加上日志记录字段
- 1.1 加入日志结构
- 1.2 在代码中调用记录日志通用函数
- 1.3 在SM30里面记录日志
- 1.4 缺点
- 1.5 优点
- 2.表技术设置-日志数据更改
- 2.1 RZ10或者RZ11修改系统参数
- 2.2 设置表的属性
- 2.3 查询日志
- 2.4 缺点
- 2.5 优点
- 3 SCDO文档对象
- 3.1 勾选相应字段-数据元素-更改文档
- 3.2 SCDO创建对象,生成日志函数
- 3.3 调用日志函数
- 3.3.1 表格维护生成器调用日志函数
- 3.3.2 非维护程序中调用日志函数
- 3.4 查询日志
- 3.5 缺点
- 3.6 优点
- 4.SE16N表数据更改日志
- 4.1 使用要点
- 4.2 缺点
- 4.3 优点
1.在表里加上日志记录字段
如下所示,在表里新增一个结构,记录创建和修改日志
1.1 加入日志结构
1.2 在代码中调用记录日志通用函数
在代码中调用以下通用函数记录日志:
FUNCTION zbcfm_fill_log2.
*“----------------------------------------------------------------------
"“本地接口:
*” IMPORTING
*” VALUE(IV_UPDKZ) OPTIONAL
*" CHANGING
*" REFERENCE(CS_DATA)
*"----------------------------------------------------------------------
FIELD-SYMBOLS:<fs_f> TYPE any.
DATA:lv_updkz TYPE c.
DATA:lt_callstack TYPE abap_callstack.
DATA:lv_repid TYPE char100.
"如果判断是自定义程序调用(函数除外),如果是则取SY-CPROG
"不是则判断是函数调用还是report调用
IF sy-cprog+0(1) = ‘Z’.
lv_repid = sy-cprog.
ELSE.
"获取调用程序名称
CALL FUNCTION ‘SYSTEM_CALLSTACK’
IMPORTING
callstack = lt_callstack.
READ TABLE lt_callstack TRANSPORTING NO FIELDS WITH KEY flag_system = ‘X’ .
IF sy-subrc = 0.
"函数调用
lv_repid = VALUE #( lt_callstack[ 2 ]-blockname OPTIONAL ).
ELSE.
"report调用
lv_repid = VALUE #( lt_callstack[ 2 ]-mainprogram OPTIONAL ).
ENDIF.
ENDIF.
IF lv_repid IS INITIAL.
lv_repid = sy-repid.
ENDIF.
IF iv_updkz IS INITIAL.
ASSIGN COMPONENT ‘CRDAT’ OF STRUCTURE cs_data TO <fs_f>.
IF <fs_f> IS ASSIGNED.
IF <fs_f> = ‘00000000’.
lv_updkz = ‘I’.
ELSE.
lv_updkz = ‘U’.
ENDIF.
UNASSIGN:<fs_f>.
ENDIF.
ELSE.
lv_updkz = iv_updkz.
ENDIF.
CASE lv_updkz.
WHEN ‘I’."插入
ASSIGN COMPONENT ‘CRDAT’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-datum.
ENDIF.
ASSIGN COMPONENT ‘CRTIM’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-uzeit.
ENDIF.
ASSIGN COMPONENT ‘CRNAM’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-uname.
ENDIF.
ASSIGN COMPONENT ‘CRPGM’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = lv_repid.
ENDIF.
WHEN ‘U’."更新
ASSIGN COMPONENT ‘UPDAT’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-datum.
ENDIF.
ASSIGN COMPONENT ‘UPTIM’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-uzeit.
ENDIF.
ASSIGN COMPONENT ‘UPNAM’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-uname.
ENDIF.
ASSIGN COMPONENT ‘LCDAT’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-datum.
ENDIF.
ASSIGN COMPONENT ‘LCTIM’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-uzeit.
ENDIF.
ASSIGN COMPONENT ‘LCNAM’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = sy-uname.
ENDIF.
ASSIGN COMPONENT ‘LCPGM’ OF STRUCTURE cs_data TO <fs_f>.
IF sy-subrc = 0.
<fs_f> = lv_repid.
ENDIF.
ENDCASE.
ASSIGN COMPONENT ‘UPPGM’ OF STRUCTURE cs_data TO FIELD-SYMBOL(<fs_uppgm>).
IF <fs_uppgm> IS ASSIGNED.
<fs_uppgm> = lv_repid."sy-tcode.
ENDIF.
ENDFUNCTION.
1.3 在SM30里面记录日志
可参考本人这篇博文,地址:https://blog.csdn.net/HAND_YS/article/details/130338370?spm=1001.2014.3001.5502
1.4 缺点
此种方式只能查询修改与创建日期、时间、账号,无法查询哪些字段被修改。同时数据删除将没有日志。
1.5 优点
简单易用
2.表技术设置-日志数据更改
此种方式是基于表的日志,只要表里任何一个字段有修改,或者整条记录的删除、新增都会有对应的一条完整的记录,而不是多个字段修改多条记录
2.1 RZ10或者RZ11修改系统参数
TCODE:RZ10/RZ11
修改参数
rec/client = 300 (代表指定client要启用日志记录)
rec/client = all (代表所有客户端启用)
rec/client = off (不启用日志)
RZ10需要重启系统才能生效,且永久生效
RZ11当即生效,但是下次重启系统该参数将不会生效
2.2 设置表的属性
注意交付类为C,不能为A
点击技术设置
勾选“日志更改”
2.3 查询日志
无论是通过SM30手工前台维护,还是自定义代码进行增删改,均会记录日志到表DBTABLOG。
使用事务代码SCU3查询日志
日志效果如下
2.4 缺点
记录日志数据量会很大
2.5 优点
相比按照字段级别的日志数据量会少一些。假如修改数据,会有新数据和旧数据的对比。删除数据,也能看到操作记录
3 SCDO文档对象
此种方式是基于字段级别的日志,每个字段修改均会有一条记录
3.1 勾选相应字段-数据元素-更改文档
以下2个字段启用日志
3.2 SCDO创建对象,生成日志函数
事务代码SCDO
输入文本、表名,按照情况勾选表名后的选项,比如“内部表”选项:当勾选上则每次处理多条数据,生成的日志函数里包含CHANGEDOCUMENT_MULTIPLE_CASE子函数。不勾上则处理单条数据,生成的日志函数里包含CHANGEDOCUMENT_SINGLE_CASE的子函数。本例处理单条数据,则不勾选“内部表”
输入函数组名称(不需要事先创建)
此时会出现需要创建的清单,最后点击激活按钮
激活成功,即日志相应的对象已经生成
3.3 调用日志函数
3.3.1 表格维护生成器调用日志函数
以下代码为固定代码(如果SCDO维护时表后面的选项改变了,则需要调整以下代码,一般保持空选项即可),直接复制过去即可。以下代码会引用日志对象的2个INCLUDE文件,调用系统自动生成的FORM记录日志。
----------------------------------------------------------------------
***INCLUDE LZZTEST002F01.
----------------------------------------------------------------------
&---------------------------------------------------------------------
*& Form BEFORE_SAVE
&---------------------------------------------------------------------
& text
&---------------------------------------------------------------------
& --> p1 text
& <-- p2 text
&---------------------------------------------------------------------
FORM before_save .
TYPES: BEGIN OF ty_tcdrp,
object TYPE cdobjectcl,
reportname TYPE cdreport,
END OF ty_tcdrp,
BEGIN OF ty_view_tab,
object TYPE cdobjectcl,
tabname TYPE cdtabname,
END OF ty_view_tab.
DATA: lt_ptab TYPE STANDARD TABLE OF string,
lv_prog TYPE string,
lv_mess TYPE string,
lv_sid TYPE string,
lt_obj TYPE STANDARD TABLE OF ty_view_tab,
lt_tcdrp TYPE STANDARD TABLE OF ty_tcdrp,
lv_fugn TYPE funct_pool,
lv_table TYPE cdtabname,
lv_namesfunc TYPE namespace,
lv_funcgroup TYPE progname,
lv_namesprog TYPE namespace,
lv_program TYPE progname,
lrt_tabname TYPE RANGE OF tabname,
lt_dd26v TYPE TABLE OF dd26v,
lv_object TYPE cdobjectcl.
" Get tabnames
" DD: Interface for reading a view from the ABAP/4 Dictionary
CALL FUNCTION ‘DDIF_VIEW_GET’
EXPORTING
name = vim_view_name
TABLES
dd26v_tab = lt_dd26v
EXCEPTIONS
illegal_input = 1
OTHERS = 2.
IF sy-subrc IS NOT INITIAL.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
IF lt_dd26v IS INITIAL.
APPEND INITIAL LINE TO lrt_tabname ASSIGNING FIELD-SYMBOL(<lrs_tabname>).
<lrs_tabname>-sign = ‘I’.
<lrs_tabname>-option = ‘EQ’.
<lrs_tabname>-low = vim_view_name.
ELSE.
SORT lt_dd26v BY tabname.
DELETE ADJACENT DUPLICATES FROM lt_dd26v COMPARING tabname.
"-
LOOP AT lt_dd26v INTO DATA(ls_dd26v).
APPEND INITIAL LINE TO lrt_tabname ASSIGNING <lrs_tabname>.
<lrs_tabname>-sign = ‘I’.
<lrs_tabname>-option = ‘EQ’.
<lrs_tabname>-low = ls_dd26v-tabname.
ENDLOOP.
ENDIF.
" Objects for change document creation
SELECT object tabname
FROM tcdob
INTO TABLE lt_obj
WHERE tabname IN lrt_tabname
##WARN_OK.
IF sy-subrc IS NOT INITIAL.
" No change document objects found
MESSAGE i899(cd).
RETURN.
ENDIF.
" Information on Include Reports Generated by RSSCD000
SELECT object reportname
FROM tcdrp
INTO TABLE lt_tcdrp
FOR ALL ENTRIES IN lt_obj
WHERE object EQ lt_obj-object
##WARN_OK.
IF sy-subrc IS NOT INITIAL.
" Update program does not yet exist
MESSAGE i446(m2).
RETURN.
ENDIF.
" View Directory
SELECT SINGLE area
FROM tvdir
INTO lv_fugn
WHERE tabname EQ vim_view_name.
"-
LOOP AT lt_obj ASSIGNING FIELD-SYMBOL(<ls_obj>).
READ TABLE lt_tcdrp ASSIGNING FIELD-SYMBOL(<ls_tcdrp>)
WITH KEY object = <ls_obj>-object.
IF sy-subrc IS NOT INITIAL.
CONTINUE.
ENDIF.
" Split namespace
CLEAR: lv_namesprog, lv_program.
lv_program = <ls_tcdrp>-reportname.
CALL FUNCTION ‘RS_NAME_SPLIT_NAMESPACE’
EXPORTING
name_with_namespace = lv_program
IMPORTING
namespace = lv_namesprog
name_without_namespace = lv_program
EXCEPTIONS
delimiter_error = 1
OTHERS = 2.
IF sy-subrc <> 0.
lv_program = <ls_tcdrp>-reportname.
ENDIF.
CLEAR: lv_namesfunc, lv_funcgroup.
lv_funcgroup = lv_fugn.
CALL FUNCTION ‘RS_NAME_SPLIT_NAMESPACE’
EXPORTING
name_with_namespace = lv_funcgroup
IMPORTING
namespace = lv_namesfunc
name_without_namespace = lv_funcgroup
EXCEPTIONS
delimiter_error = 1
OTHERS = 2.
IF sy-subrc <> 0.
lv_funcgroup = lv_fugn.
ENDIF.
" Namespace conversion
lv_object = <ls_obj>-object.
IF lv_object(1) = ‘/’.
SHIFT lv_object LEFT DELETING LEADING ‘/’.
REPLACE FIRST OCCURRENCE OF ‘/’ IN lv_object WITH ‘'.
ENDIF.
lv_table = <ls_obj>-tabname.
IF lv_table(1) = ‘/’.
SHIFT lv_table LEFT DELETING LEADING ‘/’.
REPLACE FIRST OCCURRENCE OF ‘/’ IN lv_table WITH '’.
ENDIF.
" Subroutine pool
APPEND ##NO_TEXT:
PROGRAM SUBPOOL.
TO lt_ptab,
INCLUDE
&& lv_namesprog && F
&& lv_program && CDT.
TO lt_ptab,
INCLUDE
&& lv_namesprog && F
&& lv_program && CDC.
TO lt_ptab,
FORM frm_process.
TO lt_ptab,
TYPES: BEGIN OF total.
TO lt_ptab,
INCLUDE STRUCTURE
&& vim_view_name && .
TO lt_ptab,
INCLUDE STRUCTURE vimflagtab.
TO lt_ptab,
TYPES: END OF total.
TO lt_ptab,
FIELD-SYMBOLS: <fs_total> TYPE ANY TABLE,
TO lt_ptab,
<fs_total_wa> TYPE total,
TO lt_ptab,
<fs_x_namtab> TYPE ANY TABLE,
TO lt_ptab,
<fs_x_namtab_wa> TYPE vimnamtab,
TO lt_ptab,
<fs_field> TYPE any.
TO lt_ptab,
DATA: lv_tabname(40) TYPE c VALUE '(
&& lv_namesfunc && SAPL
&& lv_funcgroup && )TOTAL[]',
TO lt_ptab,
lv_cond_line TYPE string,
TO lt_ptab,
lv_cond_line2 TYPE string.
TO lt_ptab,
ASSIGN (lv_tabname) TO <fs_total>.
TO lt_ptab,
LOOP AT <fs_total> ASSIGNING <fs_total_wa> CASTING.
TO lt_ptab,
CASE <fs_total_wa>-action.
TO lt_ptab,
WHEN 'U'. " Update
TO lt_ptab,
lv_tabname = '(
&& lv_namesfunc && SAPL
&& lv_funcgroup && )X_NAMTAB[]'.
TO lt_ptab,
ASSIGN (lv_tabname) TO <fs_x_namtab>.
TO lt_ptab,
lv_cond_line2 = |keyflag EQ 'X' AND viewfield NE 'MANDT'|.
TO lt_ptab,
LOOP AT <fs_x_namtab> ASSIGNING <fs_x_namtab_wa> WHERE (lv_cond_line2).
TO lt_ptab,
ASSIGN COMPONENT <fs_x_namtab_wa>-viewfield OF STRUCTURE <fs_total_wa> TO <fs_field>.
TO lt_ptab,
IF sy-subrc IS INITIAL.
TO lt_ptab,
lv_cond_line = lv_cond_line && |AND | &&
TO lt_ptab,
<fs_x_namtab_wa>-viewfield && | EQ '| && <fs_field> && |' |.
TO lt_ptab,
objectid = objectid && <fs_field>.
TO lt_ptab,
UNASSIGN <fs_field>.
TO lt_ptab,
ENDIF.
TO lt_ptab,
ENDLOOP.
TO lt_ptab,
IF sy-subrc IS INITIAL.
TO lt_ptab,
SHIFT lv_cond_line LEFT BY 3 PLACES.
TO lt_ptab,
SELECT SINGLE *
TO lt_ptab,
FROM
&& <ls_obj>-tabname TO lt_ptab,
INTO *
&& <ls_obj>-tabname TO lt_ptab,
WHERE (lv_cond_line).
TO lt_ptab,
MOVE-CORRESPONDING <fs_total_wa> TO
&& <ls_obj>-tabname && .
TO lt_ptab,
objectid = objectid.
TO lt_ptab,
tcode = sy-tcode.
TO lt_ptab,
udate = sy-datum.
TO lt_ptab,
utime = sy-uzeit.
TO lt_ptab,
username = sy-uname.
TO lt_ptab,
cdoc_upd_object = 'U'.
TO lt_ptab,
upd_
&& lv_table && = 'U'.
TO lt_ptab,
PERFORM cd_call_
&& lv_object && .
TO lt_ptab,
ENDIF.
TO lt_ptab,
WHEN 'N'. " New
TO lt_ptab,
lv_tabname = '(
&& lv_namesfunc && SAPL
&& lv_funcgroup && )X_NAMTAB[]'.
TO lt_ptab,
ASSIGN (lv_tabname) TO <fs_x_namtab>.
TO lt_ptab,
lv_cond_line2 = |keyflag EQ 'X' AND viewfield NE 'MANDT'|.
TO lt_ptab,
LOOP AT <fs_x_namtab> ASSIGNING <fs_x_namtab_wa> WHERE (lv_cond_line2).
TO lt_ptab,
ASSIGN COMPONENT <fs_x_namtab_wa>-viewfield OF STRUCTURE <fs_total_wa> TO <fs_field>.
TO lt_ptab,
IF sy-subrc IS INITIAL.
TO lt_ptab,
objectid = objectid && <fs_field>.
TO lt_ptab,
UNASSIGN <fs_field>.
TO lt_ptab,
ENDIF.
TO lt_ptab,
ENDLOOP.
TO lt_ptab,
IF sy-subrc IS INITIAL.
TO lt_ptab,
MOVE-CORRESPONDING <fs_total_wa> TO
&& <ls_obj>-tabname && .
TO lt_ptab,
objectid = objectid.
TO lt_ptab,
tcode = sy-tcode.
TO lt_ptab,
udate = sy-datum.
TO lt_ptab,
utime = sy-uzeit.
TO lt_ptab,
username = sy-uname.
TO lt_ptab,
cdoc_upd_object = 'I'.
TO lt_ptab,
upd_
&& lv_table && = 'I'.
TO lt_ptab,
PERFORM cd_call_
&& lv_object && .
TO lt_ptab,
ENDIF.
TO lt_ptab,
WHEN 'D'. " Delete
TO lt_ptab,
lv_tabname = '(
&& lv_namesfunc && SAPL
&& lv_funcgroup && )X_NAMTAB[]'.
TO lt_ptab,
ASSIGN (lv_tabname) TO <fs_x_namtab>.
TO lt_ptab,
lv_cond_line2 = |keyflag EQ 'X' AND viewfield NE 'MANDT'|.
TO lt_ptab,
LOOP AT <fs_x_namtab> ASSIGNING <fs_x_namtab_wa> WHERE (lv_cond_line2).
TO lt_ptab,
ASSIGN COMPONENT <fs_x_namtab_wa>-viewfield OF STRUCTURE <fs_total_wa> TO <fs_field>.
TO lt_ptab,
IF sy-subrc IS INITIAL.
TO lt_ptab,
objectid = objectid && <fs_field>.
TO lt_ptab,
UNASSIGN <fs_field>.
TO lt_ptab,
ENDIF.
TO lt_ptab,
ENDLOOP.
TO lt_ptab,
IF sy-subrc IS INITIAL.
TO lt_ptab,
MOVE-CORRESPONDING <fs_total_wa> TO *
&& <ls_obj>-tabname && .
TO lt_ptab,
objectid = objectid.
TO lt_ptab,
tcode = sy-tcode.
TO lt_ptab,
udate = sy-datum.
TO lt_ptab,
utime = sy-uzeit.
TO lt_ptab,
username = sy-uname.
TO lt_ptab,
cdoc_upd_object = 'D'.
TO lt_ptab,
upd_
&& lv_table && = 'D'.
TO lt_ptab,
PERFORM cd_call_
&& lv_object && .
TO lt_ptab,
ENDIF.
TO lt_ptab,
ENDCASE.
TO lt_ptab,
CLEAR: lv_cond_line, lv_cond_line2, objectid,
&& <ls_obj>-tabname && , *
&& <ls_obj>-tabname && .
TO lt_ptab,
ENDLOOP.
TO lt_ptab,
ENDFORM.
TO lt_ptab.
"-
GENERATE SUBROUTINE POOL lt_ptab NAME lv_prog
MESSAGE lv_mess
SHORTDUMP-ID lv_sid.
IF sy-subrc = 0.
PERFORM (‘FRM_PROCESS’) IN PROGRAM (lv_prog) IF FOUND.
ELSEIF sy-subrc = 4.
MESSAGE lv_mess TYPE ‘I’.
ELSEIF sy-subrc = 8.
MESSAGE lv_sid TYPE ‘I’.
ENDIF.
CLEAR: lt_ptab.
ENDLOOP.
ENDFORM.
&---------------------------------------------------------------------
*& Form AFTER_SAVE
&---------------------------------------------------------------------
*& text
&---------------------------------------------------------------------
*& --> p1 text
*& <-- p2 text
&---------------------------------------------------------------------
FORM after_save .
COMMIT WORK AND WAIT.
ENDFORM.
3.3.2 非维护程序中调用日志函数
本例展示在REPORT程序中调用标准日志函数,实现修改、新增、删除的日志
代码如下:
&--------------------------------------------------------------------
- Program Name : *
- Program Title : *
- Application : *
- Description : *
- Func Spec ID : *
- Requested by : *
- Author : *
- Req Date : *
- MODIFICATIONS (latest entry at the top) *
- ------------------------------------------------------------------- *
- DATE NAME (COMPANY) DESCRIPTION TASK-NO *
- ---- ---- --------- ----------- ------- *
-
*
&--------------------------------------------------------------------
REPORT zys012 .
INCLUDE fzztest002_scdocdt.
INCLUDE fzztest002_scdocdc.
START-OF-SELECTION.
SELECT * FROM zztest002 INTO TABLE @DATA(lt_zztest002).
"修改数据
LOOP AT lt_zztest002 INTO DATA(ls_zztest002).
CLEAR:*zztest002,zztest002.
"旧数据
MOVE-CORRESPONDING ls_zztest002 TO *zztest002.
"新数据
MOVE-CORRESPONDING ls_zztest002 TO zztest002.
zztest002-text1_zztest002 = ‘118’.
zztest002-text2_zztest002 = ‘118’.
objectid = ls_zztest002-num.
PERFORM frm_call_zztest002_scdo USING ‘U’.
MODIFY zztest002 FROM zztest002.
ENDLOOP.
"新增数据
CLEAR:*zztest002,zztest002.
"新数据
zztest002-num = ‘29’.
zztest002-text1_zztest002 = ‘800’.
zztest002-text2_zztest002 = ‘801’.
objectid = zztest002-num.
PERFORM frm_call_zztest002_scdo USING ‘I’.
MODIFY zztest002 FROM zztest002.
"删除数据
CLEAR:*zztest002,zztest002.
"旧数据
*zztest002-num = ‘26’.
objectid = *zztest002-num.
PERFORM frm_call_zztest002_scdo USING ‘D’.
DELETE FROM zztest002 WHERE num = ‘26’…
&---------------------------------------------------------------------
*& Form frm_call_zztest002_scdo
&---------------------------------------------------------------------
*& text
&---------------------------------------------------------------------
*& --> LS_OLD_DATA
*& --> LS_NEW_DATA
*& --> P_
&---------------------------------------------------------------------
FORM frm_call_zztest002_scdo USING fu_flag TYPE cdpos-chngind.
cdoc_upd_object = fu_flag.
upd_zztest002 = fu_flag.
tcode = sy-tcode.
udate = sy-datum.
utime = sy-uzeit.
username = sy-uname.
PERFORM cd_call_zztest002_scdo.
COMMIT WORK AND WAIT.
ENDFORM.
3.4 查询日志
使用程序CHANGEDOCU_READ 查看更改日志
日志数据记录表 CDHDR、CDPOS
通过在SM30里面新增、修改、删除数据后,查询日志记录如下:
3.5 缺点
需要建立变更对象,同时在使用处调用日志函数,使用相对麻烦。同时是字段级别的日志,数据量会比较大。
3.6 优点
针对字段级别的日志,比较详细
4.SE16N表数据更改日志
4.1 使用要点
使用SE16N更改表数据时会记录更改日志。日志数据记录表 SE16N_CD_KEY(主表)、SE16N_CD_DATA(存历史数据)
4.2 缺点
SE16N权限较大,不能给普通用户使用,在权限方面需要控制
4.3 优点
简单易用。并且记录了历史数据。按照表级别记录日志。