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