This article presents new extension added into DBMS_METADATA and new package DBMS_METADATA_DIFF.
Both packages allow you to compare the metadata for two objects and show differences. Compare results can be presented as simple XML – SXML or SQL ALTER statements that can be used to make one object like the other.
Simple XML later called SXML is new format to present an Oracle object.It removes a lot of information that are very hard to compare between objects like:
- structural complexity
- logical complexity – bit-encoded binary data (for example FLAGS, PROPERTY)
- instance specific values – for example
Supported objects for SXML
- AQ_QUEUE
- AQ_QUEUE_TABLE
- CLUSTER
- CONTEXT
- DB_LINK
- FGA_POLICY
- INDEX
- MATERIALIZED_VIEW
- MATERIALIZED_VIEW_LOG
- RLS_CONTEXT
- RLS_GROUP
- RLS_POLICY
- ROLE
- SEQUENCE
- SYNONYM
- TABLE
- TABLESPACE
- TRIGGER
- TYPE_SPEC
- TYPE_BODY
- USER
- VIEW
Example
First I have created two tables to compare TEST_TBL1 and TEST_TBL2 in schema TOMASZ.
create table tomasz.test_tbl1 ( id1 number, id2 number ); create table tomasz.test_tbl2 ( id2 number, id3 number );
There are 2 scenarios to synchronize the tables:
Scenario 1
- drop column TEST_TBL1.ID1
- add column TEST_TBL1.ID3
- rename TEST_TBL1 to TEST_TBL2
Scenario 2
- drop column TEST_TBL2.ID3
- add column TEST_TBL2.ID1
- rename TEST_TBL2 to TEST_TBL1
Of course it’s possible to generate much more permutations how the data can be synchronized but I’m not going to consider them in the article.
Standard function DBMS_METADATA.GET_XML returns a lot of data that are hard later to compare
select dbms_metadata.get_xml('TABLE', 'TEST_TBL1', 'TOMASZ')
from dual;
<?xml version="1.0"?><ROWSET><ROW>
<TABLE_T>
<VERS_MAJOR>1</VERS_MAJOR>
<VERS_MINOR>3 </VERS_MINOR>
<OBJ_NUM>88583</OBJ_NUM>
<SCHEMA_OBJ>
<OBJ_NUM>88583</OBJ_NUM>
<DATAOBJ_NUM>88583</DATAOBJ_NUM>
<OWNER_NUM>90</OWNER_NUM>
<OWNER_NAME>TOMASZ</OWNER_NAME>
<NAME>TEST_TBL1</NAME>
<NAMESPACE>1</NAMESPACE>
<TYPE_NUM>2</TYPE_NUM>
<TYPE_NAME>TABLE</TYPE_NAME>
<CTIME>2014-07-31 19:48:48</CTIME>
<MTIME>2014-07-31 19:48:48</MTIME>
<STIME>2014-07-31 19:48:48</STIME>
<STATUS>1</STATUS>
<FLAGS>0</FLAGS>
<SPARE1>6</SPARE1>
<SPARE2>1</SPARE2>
<SPARE3>90</SPARE3>
<OWNER_NAME2>TOMASZ</OWNER_NAME2>
</SCHEMA_OBJ>
<DEFERRED_STG>
<OBJ_NUM>88583</OBJ_NUM>
<FLAGS_STG>8</FLAGS_STG>
<CMPFLAG_STG>0</CMPFLAG_STG>
<CMPLVL_STG>0</CMPLVL_STG>
</DEFERRED_STG>
<TS_NAME>USERS</TS_NAME>
<BLOCKSIZE>8192</BLOCKSIZE>
<DATAOBJ_NUM>88583</DATAOBJ_NUM>
<COLS>2</COLS>
<PCT_FREE>10</PCT_FREE>
<PCT_USED>40</PCT_USED>
<INITRANS>1</INITRANS>
<MAXTRANS>255</MAXTRANS>
<FLAGS>1073741825</FLAGS>
<AUDIT_VAL>--------------------------------------</AUDIT_VAL>
<INTCOLS>2</INTCOLS>
<KERNELCOLS>2</KERNELCOLS>
<PROPERTY>536870912</PROPERTY>
<PROPERTY2>4</PROPERTY2>
<XMLSCHEMACOLS>N</XMLSCHEMACOLS>
<TSTZ_COLS>N</TSTZ_COLS>
<TRIGFLAG>0</TRIGFLAG>
<SPARE1>736</SPARE1>
<SPARE6>2014/07/31 17:48:48</SPARE6>
<COL_LIST>
<COL_LIST_ITEM>
<OBJ_NUM>88583</OBJ_NUM>
<COL_NUM>1</COL_NUM>
<INTCOL_NUM>1</INTCOL_NUM>
<SEGCOL_NUM>1</SEGCOL_NUM>
<PROPERTY>0</PROPERTY>
<NAME>ID1</NAME>
<TYPE_NUM>2</TYPE_NUM>
<LENGTH>22</LENGTH>
<NOT_NULL>0</NOT_NULL>
<CHARSETID>0</CHARSETID>
<CHARSETFORM>0</CHARSETFORM>
<BASE_INTCOL_NUM>1</BASE_INTCOL_NUM>
<BASE_COL_TYPE>0</BASE_COL_TYPE>
<SPARE1>0</SPARE1>
<SPARE2>0</SPARE2>
<SPARE3>0</SPARE3>
</COL_LIST_ITEM>
<COL_LIST_ITEM>
<OBJ_NUM>88583</OBJ_NUM>
<COL_NUM>2</COL_NUM>
<INTCOL_NUM>2</INTCOL_NUM>
<SEGCOL_NUM>2</SEGCOL_NUM>
<PROPERTY>0</PROPERTY>
<NAME>ID2</NAME>
<TYPE_NUM>2</TYPE_NUM>
<LENGTH>22</LENGTH>
<NOT_NULL>0</NOT_NULL>
<CHARSETID>0</CHARSETID>
<CHARSETFORM>0</CHARSETFORM>
<BASE_INTCOL_NUM>2</BASE_INTCOL_NUM>
<BASE_COL_TYPE>0</BASE_COL_TYPE>
<SPARE1>0</SPARE1>
<SPARE2>0</SPARE2>
<SPARE3>0</SPARE3>
</COL_LIST_ITEM>
</COL_LIST>
<CON0_LIST/>
<CON1_LIST/>
<CON2_LIST/>
<REFPAR_LEVEL>0</REFPAR_LEVEL>
</TABLE_T>
</ROW></ROWSET>
Simple SXML returned by new function DBMS_METADATA.GET_SXML for table TEST_TBL1 is much shorter
select
dbms_metadata.get_sxml('TABLE', 'TEST_TBL1', 'TOMASZ')
from dual;
<TABLE xmlns="http://xmlns.oracle.com/ku" version="1.0">
<SCHEMA>TOMASZ</SCHEMA>
<NAME>TEST_TBL1</NAME>
<RELATIONAL_TABLE>
<COL_LIST>
<COL_LIST_ITEM>
<NAME>ID1</NAME>
<DATATYPE>NUMBER</DATATYPE>
</COL_LIST_ITEM>
<COL_LIST_ITEM>
<NAME>ID2</NAME>
<DATATYPE>NUMBER</DATATYPE>
</COL_LIST_ITEM>
</COL_LIST>
<PHYSICAL_PROPERTIES>
<HEAP_TABLE>
<SEGMENT_ATTRIBUTES>
<SEGMENT_CREATION_DEFERRED></SEGMENT_CREATION_DEFERRED>
<PCTFREE>10</PCTFREE>
<PCTUSED>40</PCTUSED>
<INITRANS>1</INITRANS>
<TABLESPACE>USERS</TABLESPACE>
<LOGGING>Y</LOGGING>
</SEGMENT_ATTRIBUTES>
<COMPRESS>N</COMPRESS>
</HEAP_TABLE>
</PHYSICAL_PROPERTIES>
</RELATIONAL_TABLE>
</TABLE>
So comparing tables TEST_TBL1 and TEST_TBL2 with SXML function DBMS_METADATA_DIFF.COMPARE_SXML is now possible and reasonable
select
dbms_metadata_diff.compare_sxml('TABLE', 'TEST_TBL1', 'TEST_TBL2')
from dual;
<TABLE xmlns="http://xmlns.oracle.com/ku" version="1.0">
<SCHEMA>TOMASZ</SCHEMA>
<NAME value1="TEST_TBL1">TEST_TBL2</NAME>
<RELATIONAL_TABLE>
<COL_LIST>
<COL_LIST_ITEM src="1">
<NAME>ID1</NAME>
<DATATYPE>NUMBER</DATATYPE>
</COL_LIST_ITEM>
<COL_LIST_ITEM>
<NAME>ID2</NAME>
<DATATYPE>NUMBER</DATATYPE>
</COL_LIST_ITEM>
<COL_LIST_ITEM src="2">
<NAME>ID3</NAME>
<DATATYPE>NUMBER</DATATYPE>
</COL_LIST_ITEM>
</COL_LIST>
<PHYSICAL_PROPERTIES>
<HEAP_TABLE>
<SEGMENT_ATTRIBUTES>
<SEGMENT_CREATION_DEFERRED/>
<PCTFREE>10</PCTFREE>
<PCTUSED>40</PCTUSED>
<INITRANS>1</INITRANS>
<TABLESPACE>USERS</TABLESPACE>
<LOGGING>Y</LOGGING>
</SEGMENT_ATTRIBUTES>
<COMPRESS>N</COMPRESS>
</HEAP_TABLE>
</PHYSICAL_PROPERTIES>
</RELATIONAL_TABLE>
</TABLE>
It’s also possible to convert SXML difference into ALTER statements with function DBMS_METADATA_DIFF.COMPARE_ALTER.
!!! WATCH OUT !!! Returned information still requires attention before execution. Maybe your intention is to add only columns 🙂
select
dbms_metadata_diff.compare_alter('TABLE', 'TEST_TBL1', 'TEST_TBL2')
from dual;
ALTER TABLE "TOMASZ"."TEST_TBL1" ADD ("ID3" NUMBER)
ALTER TABLE "TOMASZ"."TEST_TBL1" DROP ("ID1")
ALTER TABLE "TOMASZ"."TEST_TBL1" RENAME TO "TEST_TBL2"
select
dbms_metadata_diff.compare_alter('TABLE', 'TEST_TBL2', 'TEST_TBL1')
from dual;
ALTER TABLE "TOMASZ"."TEST_TBL2" ADD ("ID1" NUMBER)
ALTER TABLE "TOMASZ"."TEST_TBL2" DROP ("ID3")
ALTER TABLE "TOMASZ"."TEST_TBL2" RENAME TO "TEST_TBL1"
Other example for trigger objects
create trigger TEST_TBL1_BI_TRG before insert on TEST_TBL1 begin null; end; /
get trigger SXML
select
dbms_metadata.get_sxml('TRIGGER', 'TEST_TBL1_BI_TRG', 'TOMASZ')
from dual;
<TRIGGER xmlns="http://xmlns.oracle.com/ku" version="1.0">
<SCHEMA>TOMASZ</SCHEMA>
<NAME>TEST_TBL1_BI_TRG</NAME>
<TRIGGER_TYPE>BEFORE</TRIGGER_TYPE>
<DML_EVENT>
<EVENT_LIST>
<EVENT_LIST_ITEM>
<EVENT>INSERT</EVENT>
</EVENT_LIST_ITEM>
</EVENT_LIST>
<SCHEMA>TOMASZ</SCHEMA>
<NAME>TEST_TBL1</NAME>
</DML_EVENT>
<PLSQL_BLOCK>begin
null;
end;</PLSQL_BLOCK>
</TRIGGER>
More advanced manipulation to generate ALTER statements as XML
CREATE OR REPLACE FUNCTION compare_alter_xml ( p_object_type IN VARCHAR2, p_object_name1 IN VARCHAR2, p_object_name2 IN VARCHAR2, p_schema_name1 IN VARCHAR2 DEFAULT NULL, p_schema_name2 IN VARCHAR2 DEFAULT NULL, p_network_link1 IN VARCHAR2 DEFAULT NULL, p_network_link2 IN VARCHAR2 DEFAULT NULL ) RETURN CLOB IS -- local variables l_c1 CLOB; l_c2 CLOB; whandle NUMBER; thandle NUMBER; BEGIN ---- Fetch the Metadata Diff document l_c1 := dbms_metadata_diff.compare_sxml ( p_object_type, p_object_name1, p_object_name2, p_schema_name1, p_schema_name2, p_network_link1, p_network_link2 ); -- Convert to an ALTER_XML document using the ALTERXML transform -- Set the CLAUSE_TYPE and COLUMN_ATTRIBUTE parse items whandle := dbms_metadata.openw(p_object_type); thandle := dbms_metadata.add_transform(whandle, 'ALTERXML'); dbms_metadata.set_parse_item(whandle,'CLAUSE_TYPE'); dbms_metadata.set_parse_item(whandle,'COLUMN_ATTRIBUTE'); DBMS_LOB.CREATETEMPORARY(l_c2, TRUE ); dbms_metadata.convert(whandle, l_c1, l_c2); dbms_metadata.close(whandle); RETURN l_c2; END; /
Output of this function
select
compare_alter_xml('TABLE','TEST_TBL1','TEST_TBL2')
from dual;
<ALTER_XML xmlns="http://xmlns.oracle.com/ku" version="1.0">
<OBJECT_TYPE>TABLE</OBJECT_TYPE>
<OBJECT1>
<SCHEMA>TOMASZ</SCHEMA>
<NAME>TEST_TBL1</NAME>
</OBJECT1>
<OBJECT2>
<SCHEMA>TOMASZ</SCHEMA>
<NAME>TEST_TBL2</NAME>
</OBJECT2>
<ALTER_LIST>
<ALTER_LIST_ITEM>
<PARSE_LIST>
<PARSE_LIST_ITEM>
<ITEM>CLAUSE_TYPE</ITEM>
<VALUE>ADD_COLUMN</VALUE>
</PARSE_LIST_ITEM>
</PARSE_LIST>
<SQL_LIST>
<SQL_LIST_ITEM>
<TEXT>
ALTER TABLE "TOMASZ"."TEST_TBL1" ADD ("ID3" NUMBER)
</TEXT>
</SQL_LIST_ITEM>
</SQL_LIST>
</ALTER_LIST_ITEM>
<ALTER_LIST_ITEM>
<PARSE_LIST>
<PARSE_LIST_ITEM>
<ITEM>CLAUSE_TYPE</ITEM>
<VALUE>DROP_COLUMN</VALUE>
</PARSE_LIST_ITEM>
</PARSE_LIST>
<SQL_LIST>
<SQL_LIST_ITEM>
<TEXT>
ALTER TABLE "TOMASZ"."TEST_TBL1" DROP ("ID1")
</TEXT>
</SQL_LIST_ITEM>
</SQL_LIST>
</ALTER_LIST_ITEM>
<ALTER_LIST_ITEM>
<PARSE_LIST>
<PARSE_LIST_ITEM>
<ITEM>CLAUSE_TYPE</ITEM>
<VALUE>RENAME_TABLE</VALUE>
</PARSE_LIST_ITEM>
</PARSE_LIST>
<SQL_LIST>
<SQL_LIST_ITEM>
<TEXT>
ALTER TABLE "TOMASZ"."TEST_TBL1" RENAME TO "TEST_TBL2"
</TEXT>
</SQL_LIST_ITEM>
</SQL_LIST>
</ALTER_LIST_ITEM>
</ALTER_LIST>
</ALTER_XML>
Have a fun 🙂
Tomasz