在 Oracle 12c R2 中,LogMiner 是否支持 Table/Column 超过 30 个字符的名称?

In Oracle 12c R2 does LogMiner support Table/Column names longer than 30 characters?

到目前为止,任何列名超过 30 个字符的 table 在查询 V$LOGMNR_CONTENTS

时都会给出不支持的操作

如果我删除该列或将大小调整为 <=30,则所有 CRUD 操作都报告正常。

在 Oracle 12.2 中支持 128 个字符对象,所以我试图了解我是否配置有误。没完没了的谷歌搜索让我一无所获,Oracle 文档也没有。

提前致谢!

编辑

刚刚检查了 19c,同样的行为。兼容性设置为 19.0.0

编辑

有很多关于使用补充日志记录的评论,但无法创建与接受的答案相同的场景。

不管怎样,鉴于 Oracle 现在已经表示永远不会支持它,这无关紧要!

测试 I 运行 仍然无法正常工作

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
        
SELECT supplemental_log_data_min, supplemental_log_data_pk 
FROM V$Database;
        
SUPPLEME SUP
-------- --- 
YES      NO
        
CREATE TABLE "REPLICATION_OWNER"."ATABLEWITHQUITEALONGNAMELIKEVERYLONGACTUALLY"  (  
   "ID" NUMBER(10,0), 
   "NAME" VARCHAR2(254 BYTE) 
);
            
ALTER TABLE "REPLICATION_OWNER"."ATABLEWITHQUITEALONGNAMELIKEVERYLONGACTUALLY" MODIFY ("ID" NOT NULL ENABLE); 
    
ALTER TABLE "REPLICATION_OWNER"."ATABLEWITHQUITEALONGNAMELIKEVERYLONGACTUALLY" MODIFY ("NAME" NOT NULL ENABLE);    
    
INSERT INTO atablewithquitealongnamelikeverylongactually VALUES (1, 'My Name');
        
DECLARE  CURSOR LogMinerFileCursor IS  
SELECT LogFile 
FROM (      
       SELECT V$LOGFILE.Member AS LogFile,
              FIRST_CHANGE# AS FirstSCN,
              NEXT_CHANGE# AS LastSCN       
       FROM V$LOGFILE       
       INNER JOIN V$LOG ON V$LOGFILE.GROUP# = V$LOG.GROUP#      
       WHERE V$LOG.STATUS <> 'UNUSED'       
       AND FIRST_CHANGE# >= (SELECT RESETLOGS_CHANGE# FROM V$DATABASE)      
       UNION ALL        
       SELECT Name AS LogFile,
              FIRST_CHANGE# AS FirstSCN,
              NEXT_CHANGE# AS LastSCN       
       FROM V$ARCHIVED_LOG      
       WHERE FIRST_CHANGE# < (
                              SELECT MIN(FIRST_CHANGE#) 
                              FROM V$LOGFILE
                              INNER JOIN V$LOG ON V$LOGFILE.GROUP# = V$LOG.GROUP#
                              WHERE V$LOG.STATUS <> 'UNUSED'
                             ) AND FIRST_CHANGE# >= (SELECT RESETLOGS_CHANGE# FROM V$DATABASE)   
) LogFiles WHERE FirstSCN >= 0 OR LastSCN > 0; 



sDDL    varchar2(2000);
        
BEGIN  FOR LogMinerFileCursorRecords in LogMinerFileCursor    LOOP
 
    sDDL := 'BEGIN DBMS_LOGMNR.ADD_LOGFILE('''|| LogMinerFileCursorRecords.LogFile ||'''); END;';

    DBMS_OUTPUT.Put_Line(sDDL);       
    execute immediate sDDL;  
END LOOP; 
COMMIT; 
END; 

BEGIN DBMS_LOGMNR.START_LOGMNR(OPTIONS => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG +  + DBMS_LOGMNR.COMMITTED_DATA_ONLY ); END;
        
 SELECT SQL_REDO AS RedoSQL 
FROM V$LOGMNR_CONTENTS 
WHERE SEG_OWNER = 'REPLICATION_OWNER' 
AND TABLE_NAME = 'ATABLEWITHQUITEALONGNAMELIKEVERYLONGACTUALLY'
        
REDOSQL
--------------------------------------------------------------------------------
        
CREATE TABLE "REPLICATION_OWNER"."ATABLEWITHQUITEALONGNAMELIKEVERYLONGACTUALLY"
(    
  "ID" NUMBER(10,0),
  "NAME" VARCHAR2(254 BYTE)    
) 
SEGMENT CREATION IMMEDIATE   
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255  
NOCOMPRESS 
LOGGING   
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1   BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)   
TABLESPACE "REPLICATION_DATA";
        
REDOSQL
-------------------------------------------------------------------------------- ALTER TABLE "REPLICATION_OWNER"."ATABLEWITHQUITEALONGNAMELIKEVERYLONGACTUALLY" M ODIFY ("ID" NOT NULL ENABLE);
        
REDOSQL
-------------------------------------------------------------------------------- ALTER TABLE "REPLICATION_OWNER"."ATABLEWITHQUITEALONGNAMELIKEVERYLONGACTUALLY" M ODIFY ("NAME" NOT NULL ENABLE);
        
REDOSQL
-------------------------------------------------------------------------------- Unsupported
        
BEGIN DBMS_LOGMNR.END_LOGMNR; END;

新更新

几天后我确认这是一个错误。不幸的是,现在支持团队告诉我以下内容:

“这不是错误。从 12.2 开始,新 types/features 仅支持 dbms_rolling 和 golden gate。”结论是,LogMiner 将不支持名称超过 30 个字符的 table,即使 supplemental_logging 被禁用。他们将更新文档。只要我对此有更多详细信息,我就会更新答案。

SQL> select supplemental_log_data_min, supplemental_log_data_pk from v$database;

SUPPLEME SUP
-------- ---
YES YES

在这种情况下,当 table 的名称超过 30 个字符时,它将始终显示不支持任何 DML 操作。

SQL> select * from v$version ;

BANNER
--------------------------------------------------------------------------------
    CON_ID
----------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
         0

PL/SQL Release 12.2.0.1.0 - Production
         0

CORE    12.2.0.1.0      Production
         0


BANNER
--------------------------------------------------------------------------------
    CON_ID
----------
TNS for Linux: Version 12.2.0.1.0 - Production
         0

NLSRTL Version 12.2.0.1.0 - Production
         0

让我们开始测试用例

SQL> create table cpl_rep.my_table_with_a_very_long_name_with_more ( c1 number );

Table created.

SQL> insert into cpl_rep.my_table_with_a_very_long_name_with_more values ( 1 ) ;

1 row created.

SQL> insert into cpl_rep.my_table_with_a_very_long_name_with_more values ( 2 ) ;

1 row created

SQL> commit;

Commit complete.

SQL> select length(table_name) from dba_tables where table_name =  upper('my_table_with_a_very_long_name_with_more');

LENGTH(TABLE_NAME)
------------------
                40

SQL>

然后我开始我的 logminer 会话,首先我切换我的日志文件

SQL> alter system switch logfile ;

System altered.

SQL> exit

然后我再次输入以打开我的 logminer 会话

SQL> exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo11.ora' , 1);
 exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo21.ora' , 1);
 exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo12.ora' , 1);
 exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo22.ora' , 1);
 exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo13.ora' , 1);
 exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo23.ora' , 1);

PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.


SQL> exec dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog);

PL/SQL procedure successfully completed.

SQL>  select count(*) from v$logmnr_contents where seg_name =  upper('my_table_with_a_very_long_name_with_more');

  COUNT(*)
----------
         3

SQL> select operation,seg_name,sql_redo from v$logmnr_contents where seg_name =  upper('my_table_with_a_very_long_name_with_more');

OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------
SQL_REDO
--------------------------------------------------------------------------------
DDL
MY_TABLE_WITH_A_VERY_LONG_NAME_WITH_MORE
create table cpl_rep.my_table_with_a_very_long_name_with_more ( c1 number );

OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------
SQL_REDO
--------------------------------------------------------------------------------

INSERT
MY_TABLE_WITH_A_VERY_LONG_NAME_WITH_MORE
insert into "CPL_REP"."MY_TABLE_WITH_A_VERY_LONG_NAME_WITH_MORE"("C1") values ('
1');

OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------
SQL_REDO
--------------------------------------------------------------------------------
INSERT
MY_TABLE_WITH_A_VERY_LONG_NAME_WITH_MORE
insert into "CPL_REP"."MY_TABLE_WITH_A_VERY_LONG_NAME_WITH_MORE"("C1") values ('
2');

因此,如您所见,在我的例子中,当受影响的元素是 table.

时,不存在 30 个字符的限制。

让我们看看元素何时是列

SQL> create table cpl_rep.table_with_name_greater_than_30_characters ( column_greater_than_30_characters_test_case number );

Table created.

SQL> select length('table_with_name_greater_than_30_characters') , length('column_greater_than_30_characters_test_case') from dual ;

LENGTH('TABLE_WITH_NAME_GREATER_THAN_30_CHARACTERS')
----------------------------------------------------
LENGTH('COLUMN_GREATER_THAN_30_CHARACTERS_TEST_CASE')
-----------------------------------------------------
                                                  42
                                                   43



SQL> insert into cpl_rep.table_with_name_greater_than_30_characters values ( 1 );

1 row created.

SQL> r
  1* insert into cpl_rep.table_with_name_greater_than_30_characters values ( 1 )

1 row created.

SQL> commit;

Commit complete.

SQL> insert into cpl_rep.table_with_name_greater_than_30_characters values ( 2 );

1 row created.

SQL> r
  1* insert into cpl_rep.table_with_name_greater_than_30_characters values ( 2 )

1 row created.

SQL> commit;

Commit complete.

SQL> delete from cpl_rep.table_with_name_greater_than_30_characters where column_greater_than_30_characters_test_case=2 ;

2 rows deleted.

SQL> commit;

Commit complete.

$ sqlplus / as sysdba

SQL*Plus: Release 12.2.0.1.0 Production on Sun Jul 19 17:07:58 2020

Copyright (c) 1982, 2016, Oracle.  All rights reserved.
    
Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production

SQL> exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo11.ora' , 1);
     exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo21.ora' , 1);
     exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo12.ora' , 1);
     exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo22.ora' , 1);
     exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo1/redo13.ora' , 1);
     exec dbms_logmnr.add_logfile('/bbdd_odcgrc1r/redo2/redo23.ora' , 1);
PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.

SQL>
PL/SQL procedure successfully completed.

SQL>

PL/SQL procedure successfully completed.

SQL>
SQL> exec dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog);

PL/SQL procedure successfully completed.

SQL> select count(*) from v$logmnr_contents where seg_owner = 'CPL_REP' and seg_name = upper('table_with_name_greater_than_30_characters') ;

  COUNT(*)
----------
         3

SQL> select operation,seg_name,sql_redo from v$logmnr_contents where seg_owner = 'CPL_REP' and seg_name = upper('table_with_name_greater_than_30_characters') ;

OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------
SQL_REDO
--------------------------------------------------------------------------------
DDL
TABLE_WITH_NAME_GREATER_THAN_30_CHARACTERS
create table cpl_rep.table_with_name_greater_than_30_characters ( column_greater
_than_30_characters_test_case number );

INSERT
TABLE_WITH_NAME_GREATER_THAN_30_CHARACTERS

OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------
SQL_REDO
--------------------------------------------------------------------------------
insert into "CPL_REP"."TABLE_WITH_NAME_GREATER_THAN_30_CHARACTERS"("COLUMN_GREAT
ER_THAN_30_CHARACTERS_TEST_CASE") values ('1');

INSERT
TABLE_WITH_NAME_GREATER_THAN_30_CHARACTERS
insert into "CPL_REP"."TABLE_WITH_NAME_GREATER_THAN_30_CHARACTERS"("COLUMN_GREAT
ER_THAN_30_CHARACTERS_TEST_CASE") values ('1');

OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------
SQL_REDO
--------------------------------------------------------------------------------

因此,在我的例子中,我可以对两个列和大于 30 个字符的 tables 进行操作。

更新

在评论部分之后,我决定尝试使用 SUPPLEMENTAL_LOGGING 进行测试并且它有效。但是,当我为所有列 PK 添加 SUPPLEMENTAL_LOGGING 时,它不起作用

SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

Database altered.

SQL> select supplemental_log_data_min, supplemental_log_data_pk from v$database;

SUPPLEME SUP
-------- ---
YES      NO

SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY) COLUMNS;

Database altered.

SQL>  select supplemental_log_data_min, supplemental_log_data_pk from v$database;

SUPPLEME SUP
-------- ---
YES      YES

SQL> create table cpl_rep.my_test_with_a_very_very_long_name_for_test ( c1 number ) ;

Table created.

   
SQL> insert into cpl_rep.my_test_with_a_very_very_long_name_for_test values ( 1 ) ;

1 row created.

SQL> insert into cpl_rep.my_test_with_a_very_very_long_name_for_test values ( 2 ) ;

1 row created.

SQL> commit ;

Commit complete.

SQL> insert into cpl_rep.my_test_with_a_very_very_long_name_for_test values ( 3 ) ;

1 row created.

SQL> commit;

Commit complete.


SQL> delete from cpl_rep.my_test_with_a_very_very_long_name_for_test where c1 = 3 ;

1 row deleted.

SQL>

切换日志文件并启动logminer。内容现在显示值 UNSUPPORTED。

SQL> select sql_redo , operation, seg_name from v$logmnr_contents where seg_name = upper('my_test_with_a_very_very_long_name_for_test') ;

SQL_REDO
--------------------------------------------------------------------------------
OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------
create table cpl_rep.my_test_with_a_very_very_long_name_for_test ( c1 number ) ;
DDL
MY_TEST_WITH_A_VERY_VERY_LONG_NAME_FOR_TEST

Unsupported
UNSUPPORTED
MY_TEST_WITH_A_VERY_VERY_LONG_NAME_FOR_TEST

SQL_REDO
--------------------------------------------------------------------------------
OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------

Unsupported
UNSUPPORTED
MY_TEST_WITH_A_VERY_VERY_LONG_NAME_FOR_TEST

Unsupported
UNSUPPORTED

SQL_REDO
--------------------------------------------------------------------------------
OPERATION
--------------------------------
SEG_NAME
--------------------------------------------------------------------------------
MY_TEST_WITH_A_VERY_VERY_LONG_NAME_FOR_TEST

Unsupported
UNSUPPORTED
MY_TEST_WITH_A_VERY_VERY_LONG_NAME_FOR_TEST


SQL>

关键是SUPPLEMENTAL_LOGGINGPK,而且只影响DML操作,因为DDL显示的是真正执行的命令。

最后 Oracle 针对此问题打开了一个错误:Log Miner 显示 SQL_REDO 不支持 table 名称超过 30 个字符(文档 ID 2703406.1)。

虽然还没有解决:(

只是为了结束这个循环。 Oracle 21c 中的文档已更新以指定限制。

https://docs.oracle.com/en/database/oracle/oracle-database/21/sutil/oracle-logminer-utility.html#GUID-7594F0D7-0ACD-46E6-BD61-2751136ECDB4

真的不喜欢 Oracle 如何处理他们的文档,但是嘿,至少它在那里。