Oracle數據庫開發應用中經常對數據庫管理員有這樣的需求,對比兩個不同實例間某模式下對象的差異或者對比兩個不同實例某模式下表定義的差異性,這在涉及到數據庫軟件的開發應用中是經常遇到的。一般數據庫軟件的開發都是首先在開發數據庫上進行,開發到一定程度後,系統投入運行,此時軟件處於維護階段。針對在系統運行中遇到的錯誤、bug等,還有應用系統的升級,經常需要調整後台程序,數據庫開發人員經常遇到這樣一種尴尬的事情,維護到一定時期,開發庫和運行庫之間出現了一些差異,而這些差異又是什麼。還有一種情況就是如果應用在很多地方同時使用,每次的後台升級,那些數據庫作了升級,那些還沒有作升級,如果沒有詳細記錄的話,也會造成我們的尴尬事情。
另外,如果要搭建Oracle的高級表復制環境,在規劃好了復制的架構以及復制的表後,在正式將需要復制的表加入到復制組之前,一個重要工作就是檢查這些需要復制的表在主節點和主定義節點間的差異性。如果這些表定義間存在任何細微的差別,如某個字段在一個節點是允許Null,而在另一個節點是Not Null,在將表加入到復制組時都會出現這樣的錯誤,“ORA-23309 object string.string of type string exists”,其實造成這個錯誤的原因就是復制主定義節點和主節點之間表的定義存在某些差異,系統認為是兩個不同的表,但是在兩個數據庫中具有同樣的名稱。如果需要復制的表很多,手工對比兩個節點復制表之間的差異是需要很大工作量,而且不一定准確。
業界有名的數據庫軟件輔助產品提供商美國Embarcadero公司(www.embarcadero.com)在其全套數據庫解決方案中就相應提供了一個叫Change Manager的產品,該產品一個很大的用途就是對比分析不同實例間的差異。Oracle公司的OEM(Oracle enterprise manager)包中也相應提供了一個工具叫做DB-Diff,也用來比較兩個數據庫間的差異。
其實對於普通的數據庫開發在某種程度上說根本不需要上面提到的這些工具軟件,況且這些產品不是隨便可以得到並且使用的。針對上面提到的這些問題,提供了兩個SQL腳本,可以輕松實現對比分析兩個不同數據庫實例間對象的差異以及表定義的差異。
一、對比兩個不同實例下某模式中對象差異
在運行下面的腳本之前,必須首先創建當然數據庫用戶和需要對比的兩個實例間的數據庫聯接。下面是運行該腳本的一個例子。在SQL/PLUS下運行該腳本,系統出現如下提示:
對象屬主(模式): repadmin
第一個實例的數據庫聯接名稱 (包括 @):@ora_zs
第一個實例的數據庫聯接名稱 (包括 @):@ora_sjjk
其中程序要求輸入對象屬主,也就是對比那個用戶,這裡是repadmin用戶。然後是需要對比的實例聯接名稱,這裡是ora_zs和ora_sjjk,最後給出對比報告。
附:PL/SQL程序腳本清單:
set linesize 80 set verify off set feedback off set pause off; accept obj_owner prompt '對象屬主(模式): ' accept inst_1_dblink prompt '第一個實例的數據庫聯接名稱 (包括 @):' accept inst_2_dblink prompt '第一個實例的數據庫聯接名稱 (包括 @):' clear breaks ttitle off set heading off column datetime noprint new_value datetime column inst_code1_name noprint new_value inst_code1_name column inst_code2_name noprint new_value inst_code2_name select to_char(sysdate,'MM/DD/YY') datetime from dual / select global_name inst_code1_name from global_name&inst_1_dblink / select global_name inst_code2_name from global_name&inst_2_dblink / set feedback on set heading on set newpage 0 ttitle col 25 '對象比較結果報告單' - col 53 '日期: ' datetime - skip 1 col 60 '頁: ' sql.pno - skip 1 col 10 '屬主: ' obj_owner - skip 1 center '對象在 &inst_code1_name 但不在 &inst_code2_name ' - skip 2 column object_type format a15 heading '對象類型'; column object_name format a35 heading '對象名稱'; column status format a10 heading '狀態'; SELECT OBJECT_NAME, OBJECT_TYPE, STATUS FROM ALL_OBJECTS&inst_1_dblink WHERE OWNER = UPPER('&OBJ_OWNER') AND OBJECT_TYPE != 'SYNONYM' MINUS SELECT OBJECT_NAME, OBJECT_TYPE, STATUS FROM ALL_OBJECTS&inst_2_dblink WHERE OWNER = UPPER('&OBJ_OWNER') AND OBJECT_TYPE != 'SYNONYM' ORDER BY 2,3 / ttitle col 25 '對象比較結果報告單' - col 53 '日期: ' datetime - skip 1 col 60 '頁: ' sql.pno - skip 1 col 10 '屬主: ' obj_owner - skip 1 center '對象在 &inst_code2_name 但不在 &inst_code1_name ' - skip 2 SELECT OBJECT_NAME, OBJECT_TYPE, STATUS FROM ALL_OBJECTS&inst_2_dblink WHERE OWNER = UPPER('&OBJ_OWNER') AND OBJECT_TYPE != 'SYNONYM' MINUS SELECT OBJECT_NAME, OBJECT_TYPE, STATUS FROM ALL_OBJECTS&inst_1_dblink WHERE OWNER = UPPER('&OBJ_OWNER') AND OBJECT_TYPE != 'SYNONYM' ORDER BY 2,3 /
二、對比兩個不同模式下表定義的差異
運行方法和前面第一個腳本一樣,這裡不再詳述。下面是一個運行實例結果:
對象屬主(模式): db_zgxt
第一個實例的數據庫聯接名稱 (包括 @):@ora_cx
第一個實例的數據庫聯接名稱 (包括 @):@ora_zs
附:PL/SQL程序腳本清單:
SET PAGESIZE 60 SET LINESIZE 110 SET VERIFY OFF SET FEEDBACK OFF SET PAUSE OFF ACCEPT obj_owner PROMPT '對象屬主(模式): ' ACCEPT inst_1_dblink PROMPT '第一個實例的數據庫聯接名稱 (包括 @):' ACCEPT inst_2_dblink PROMPT '第一個實例的數據庫聯接名稱 (包括 @):' clear breaks TTITLE off SET HEADING off COLUMN datetime noprint new_value datetime COLUMN inst_code1_name noprint new_value inst_code1_name COLUMN inst_code2_name noprint new_value inst_code2_name SELECT TO_CHAR(SYSDATE,'MM/DD/YY') datetime FROM DUAL / SELECT global_name inst_code1_name FROM global_name&inst_1_dblink / SELECT global_name inst_code2_name FROM global_name&inst_2_dblink / SET feedback ON SET HEADING ON TTITLE COL 30 '對象比較結果報告單' - COL 63 '日期: ' datetime - SKIP 1 COL 68 '頁: ' sql.pno - SKIP 1 COL 10 '屬主: ' obj_owner - SKIP 1 CENTER '&inst_code1_name 和 &inst_code2_name 之間表定義的差別明細' - SKIP 2 COLUMN table_name format a25 HEADING '表名'; COLUMN column_name format a25 HEADING '列名'; COLUMN data_type format a8 HEADING '數據類型'; COLUMN data_length format 999 HEADING '長度'; COLUMN data_precision format 999 HEADING '精度'; COLUMN nullable format a5 HEADING '是否可空'; COLUMN inst_code format a15 HEADING '實例'; SELECT '&inst_code1_name' inst_code, table_name, column_name, data_type, data_length, data_precision, nullable FROM all_tab_COLUMNs&inst_1_dblink WHERE owner = UPPER('&obj_owner') AND table_name in (SELECT table_name FROM all_tables&inst_2_dblink WHERE owner = UPPER('&obj_owner')) MINUS SELECT '&inst_code1_name' inst_code, table_name, column_name, data_type, data_length, data_precision, nullable FROM all_tab_columns&inst_2_dblink WHERE owner = UPPER('&obj_owner') UNION SELECT '&inst_code2_name' inst_code, table_name, column_name, data_type, data_length, data_precision, nullable FROM all_tab_COLUMNs&inst_2_dblink WHERE owner = UPPER('&obj_owner') AND table_name in (SELECT table_name FROM all_tables&inst_1_dblink WHERE owner = UPPER('&obj_owner')) MINUS SELECT '&inst_code2_name' inst_code, table_name, column_name, data_type, data_length, data_precision, nullable FROM all_tab_columns&inst_1_dblink WHERE owner = UPPER('&obj_owner') ORDER BY 2, 3 /