この章では、メタデータAPI、DBMS_METADATA
およびDBMS_METADATA_DIFF
の使用方法を説明します。
DBMS_METADATA
APIを使用すると、次の操作を行うことができます。
オブジェクトのメタデータのXMLとしての取得
SQL DDLへの変換を含む様々な方法でのXMLの変換
取得で抽出したオブジェクトを再作成するためのXMLの送信
DBMS_METADATA_DIFF
APIを使用すると、データベース間でオブジェクトを比較し、同じ型のオブジェクトのうち時間の経過によってメタデータが変化したものを識別できます。
この章の内容は、次のとおりです。
Oracle Databaseの長期の使用に伴い、ディレクトリからメタデータを抽出し、そのメタデータに対し列の追加やデータ型の変更などを行い、さらにDDLに変換して元のまたは異なるデータベースのオブジェクトを再作成するために、ユーザーが独自のコードを開発する機会が多くなりました。ディクショナリの新機能をサポートするよう、そのコードを更新された状態に保つことは困難です。
DBMS_METADATA
APIを使用すると、メタデータを抽出するために独自のコードを記述して管理する必要がなくなります。ディクショナリ・メタデータの抽出、操作および再作成に関する集中的な機能が提供されます。また、すべてのディクショナリ・オブジェクトを最新のレベルでサポートしています。
DBMS_METADATA
APIは、記述および管理するカスタム・コードの量を大幅に削減しますが、通常のデータベース手順の変更はありません。DBMS_METADATA
APIは、データ・ディクショナリ・ビューと同様、データベースのインストール時にcatproc
.sql
を実行し、SQLスクリプトを起動してインストールします。インストール後は、制限モードであっても、インスタンスが動作中はいつでも使用可能です。
DBMS_METADATA
APIは、異なるOracleのリリース間で上位互換性があるため、データベースのリリースを変更しても、ソース・コードの変更は必要ありません。1つのリリースで取得したXML文書を、同じまたはそれ以上のリリースの送信インタフェースで処理できます。たとえば、Oracle9iデータベースで取得したXML文書は、Oracle Database 10gに送信できます。
DBMS_METADATA
APIにおいては、データベース内のすべてのエンティティは、オブジェクト型に属するオブジェクトとして形成されています。たとえば、scott.emp
表はオブジェクトであり、オブジェクト型はTABLE
です。オブジェクトのメタデータをフェッチする場合は、オブジェクト型を指定する必要があります。
オブジェクト型の特定のオブジェクトまたは一連のオブジェクトをフェッチするには、フィルタを指定します。各オブジェクト型に対して異なるフィルタが定義されます。たとえば、TABLE
オブジェクト型に対して定義されている2つのフィルタはSCHEMA
およびNAME
です。これにより、たとえば、スキーマがscott
で名前がemp
である表を必要としていることを表現できます。
DBMS_METADATA
APIは、XML(Extensible Markup Language)およびXSLT(Extensible Stylesheet Language Transformation)を使用します。XMLは解析や変換が容易な汎用形式であるため、DBMS_METADATA
APIは、オブジェクト・メタデータをXMLとして表現します。DBMS_METADATA
APIは、XSLTを使用してXML文書を他のXML文書またはSQL DDLのいずれかに変換します。
DBMS_METADATA
APIを使用して、メタデータをフェッチするとき(または再送信するとき)に、1つ以上の変換(XSLTスクリプト)をXMLに適用するよう指定できます。APIには、XML文書をSQL作成DDLに変換する名前付きDDL以外にも、いくつかの事前定義変換があります。
次に変換パラメータを使用して、変換の条件を指定できます。また、オブジェクトのメタデータの特定の属性にアクセスするために、オプションの解析項目も指定できます。これらのオプションの詳細と実装例については、次の項を参照してください。
DBMS_METADATA
APIの取得インタフェースでは、取得するオブジェクトの種類を指定できます。種類は、特定のオブジェクト型(表、索引、プロシージャなど)または論理単位を形成する異なるオブジェクト型のコレクション(データベース・エクスポート、スキーマ・エクスポートなど)のいずれかです。デフォルトでは、フェッチされたメタデータがXML文書に返されます。
注意: スキーマにないオブジェクトにアクセスするにはSELECT_CATALOG_ROLE ロールが必要です。ただし、ロールは多くのPL/SQLオブジェクト(ストアド・プロシージャ、ファンクション、定義者権限API)内で無効とされます。そのため、他のスキーマのオブジェクト(一般的には、SELECT_CATALOG_ROLE ロールを必要とするすべてのオブジェクト)内にアクセスするPL/SQLプログラムを記述する場合は、実行者権限APIにコードを入力する必要があります。 |
プログラム・インタフェースを使用して、簡単な参照を実行したり、アプリケーションを開発できます。システム・メタデータの非定型の問合せを行う場合は、ブラウザ・インタフェースを使用します。アプリケーションの一部として、ディクショナリ・メタデータを抽出する場合は、プログラム・インタフェースを使用します。その場合、同じことをするために現在使用しているSQLスクリプトおよびカスタマイズしたコードのかわりに、DBMS_METADATA
APIで提供されているプロシージャを使用することもできます。
メタデータを取得する場合は、DBMS_METADATA
PL/SQL APIを使用します。次に、プログラム・インタフェースおよびブラウザ・インタフェースの例を示します。
例20-1では、DBMS_METADATA
プログラム・インタフェースを使用して1つの表のメタデータを取得する基本的な方法を説明します。この例では、get_table_md
というファンクションを作成するDBMS_METADATA
プログラムを作成します。ファンクションは、1つの表のメタデータを返します。
例20-1 データ取得のためのDBMS_METADATAプログラム・インタフェースの使用
hr
スキーマ内の1つの表(timecards
)のメタデータを返すファンクションget_table_md
を作成するDBMS_METADATA
プログラムを作成します。プログラムの内容は次のようになります。(この例ではプログラムにmetadata_program
.sql
という名前を付けます)。
CREATE OR REPLACE FUNCTION get_table_md RETURN CLOB IS -- Define local variables. h NUMBER; --handle returned by OPEN th NUMBER; -- handle returned by ADD_TRANSFORM doc CLOB; BEGIN -- Specify the object type. h := DBMS_METADATA.OPEN('TABLE'); -- Use filters to specify the particular object desired. DBMS_METADATA.SET_FILTER(h,'SCHEMA','HR'); DBMS_METADATA.SET_FILTER(h,'NAME','TIMECARDS'); -- Request that the metadata be transformed into creation DDL. th := DBMS_METADATA.ADD_TRANSFORM(h,'DDL'); -- Fetch the object. doc := DBMS_METADATA.FETCH_CLOB(h); -- Release resources. DBMS_METADATA.CLOSE(h); RETURN doc; END; /
ユーザーhr
として接続します。
プログラムを実行してget_table_md
ファンクションを作成します。
SQL> @metadata_program
新しく作成したget_table_md
ファンクションを選択操作で使用します。完全で中断のない出力を生成するには、問合せの実行前に、次のとおり、PAGESIZE
を0(ゼロ)にし、またLONG
を大きい数に設定します。
SQL> SET PAGESIZE 0 SQL> SET LONG 1000000 SQL> SELECT get_table_md FROM dual;
hr
スキーマのtimecards
表のメタデータを示す出力は、次のようになります。
CREATE TABLE "HR"."TIMECARDS" ( "EMPLOYEE_ID" NUMBER(6,0), "WEEK" NUMBER(2,0), "JOB_ID" VARCHAR2(10), "HOURS_WORKED" NUMBER(4,2), FOREIGN KEY ("EMPLOYEE_ID") REFERENCES "HR"."EMPLOYEES" ("EMPLOYEE_ID") ENABLE ) 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) TABLESPACE "EXAMPLE"
例20-2に示すとおり、ブラウザ・インタフェースを使用しても同じ結果を得られます。
例20-2 データ取得のためのDBMS_METADATAブラウザ・インタフェースの使用
SQL> SET PAGESIZE 0 SQL> SET LONG 1000000 SQL> SELECT DBMS_METADATA.GET_DDL('TABLE','TIMECARDS','HR') FROM dual;
結果は、例20-1の手順5と同様になります。
例20-1では、オブジェクトが1つしか存在しないことがわかっていたため、FETCH_CLOB
プロシージャは1回のみコールされました。scott
スキーマのすべての表などの複数のオブジェクトも取得できます。それを行うには、次の構成メンバーを使用する必要があります。
LOOP doc := DBMS_METADATA.FETCH_CLOB(h); -- -- When there are no more objects to be retrieved, FETCH_CLOB returns NULL. -- EXIT WHEN doc IS NULL; END LOOP;
例20-3に、構成メンバーの使用および複数のオブジェクトの取得を示します。この例では、ユーザーscott
として接続します。パスワードは、tiger
です。
例20-3 複数のオブジェクトの取得
次のように、my_metadata
という名前の表と、get_tables_md
という名前のプロシージャを作成します。すべてのオブジェクトを返すことはできないため、それらを表に格納して最後に問合せを実行します。
DROP TABLE my_metadata; CREATE TABLE my_metadata (md clob); CREATE OR REPLACE PROCEDURE get_tables_md IS -- Define local variables h NUMBER; -- handle returned by 'OPEN' th NUMBER; -- handle returned by 'ADD_TRANSFORM' doc CLOB; -- metadata is returned in a CLOB BEGIN -- Specify the object type. h := DBMS_METADATA.OPEN('TABLE'); -- Use filters to specify the schema. DBMS_METADATA.SET_FILTER(h,'SCHEMA','SCOTT'); -- Request that the metadata be transformed into creation DDL. th := DBMS_METADATA.ADD_TRANSFORM(h,'DDL'); -- Fetch the objects. LOOP doc := DBMS_METADATA.FETCH_CLOB(h); -- When there are no more objects to be retrieved, FETCH_CLOB returns NULL. EXIT WHEN doc IS NULL; -- Store the metadata in a table. INSERT INTO my_metadata(md) VALUES (doc); COMMIT; END LOOP; -- Release resources. DBMS_METADATA.CLOSE(h); END; /
次のようにプロシージャを実行します。
EXECUTE get_tables_md;
my_metadata
表を問い合せて、取得された内容を確認します。
SET LONG 9000000 SET PAGES 0 SELECT * FROM my_metadata;
変換パラメータを使用して、追加する変換の条件を指定できます。SET_TRANSFORM_PARAM
プロシージャを使用します。たとえば、TABLE
オブジェクトに対してDDL
変換を追加した場合、SEGMENT_ATTRIBUTES
変換パラメータを指定して、物理、ストレージ、ロギングなどのセグメント属性をDDLに表示しないようにすることができます。デフォルトでは、セグメント属性がDDLに表示されます。
例20-4に、SET_TRANSFORM_PARAM
プロシージャの使用方法を示します。
例20-4 変換の条件指定
次のように、get_table_md
という名前のファンクションを作成します。
CREATE OR REPLACE FUNCTION get_table_md RETURN CLOB IS -- Define local variables. h NUMBER; -- handle returned by 'OPEN' th NUMBER; -- handle returned by 'ADD_TRANSFORM' doc CLOB; BEGIN -- Specify the object type. h := DBMS_METADATA.OPEN('TABLE'); -- Use filters to specify the particular object desired. DBMS_METADATA.SET_FILTER(h,'SCHEMA','HR'); DBMS_METADATA.SET_FILTER(h,'NAME','TIMECARDS'); -- Request that the metadata be transformed into creation DDL. th := dbms_metadata.add_transform(h,'DDL'); -- Specify that segment attributes are not to be returned. -- Note that this call uses the TRANSFORM handle, not the OPEN handle. DBMS_METADATA.SET_TRANSFORM_PARAM(th,'SEGMENT_ATTRIBUTES',false); -- Fetch the object. doc := DBMS_METADATA.FETCH_CLOB(h); -- Release resources. DBMS_METADATA.CLOSE(h); RETURN doc; END; /
次の問合せを実行します。
SQL> SELECT get_table_md FROM dual;
出力は、次のようになります。
CREATE TABLE "HR"."TIMECARDS" ( "EMPLOYEE_ID" NUMBER(6,0), "WEEK" NUMBER(2,0), "JOB_ID" VARCHAR2(10), "HOURS_WORKED" NUMBER(4,2), FOREIGN KEY ("EMPLOYEE_ID") REFERENCES "HR"."EMPLOYEES" ("EMPLOYEE_ID") ENABLE )
ここまでの例ではDDL
変換という1つの変換を使用しました。DBMS_METADATA
APIでは、最初の出力が2番目の入力になり、2番目の出力が3番目の入力になるというように、複数の変換を指定することもできます。
Oracleには、XML文書を変更するMODIFY
と呼ばれる変換があります。スキーマ名や表領域名の変更などが行えます。これには、再マップ・パラメータおよびSET_REMAP_PARAM
プロシージャを使用します。
例20-5に、SET_REMAP_PARAM
プロシージャの使用例を示します。まずMODIFY
変換を追加し、再マップ・パラメータを指定して、スキーマ名をhr
からscott
に変更します。次にDDL
変換を追加します。MODIFY
変換の出力は、DDL
変換への入力となるXML文書です。最終的な結果は、timecards
表の作成DDLであり、hr
スキーマのすべてのインスタンスはscott
に変更されます。
例20-5 XML文書の変更
次のように、remap_schemaという名前のファンクションを作成します。
CREATE OR REPLACE FUNCTION remap_schema RETURN CLOB IS -- Define local variables. h NUMBER; --handle returned by OPEN th NUMBER; -- handle returned by ADD_TRANSFORM doc CLOB; BEGIN -- Specify the object type. h := DBMS_METADATA.OPEN('TABLE'); -- Use filters to specify the particular object desired. DBMS_METADATA.SET_FILTER(h,'SCHEMA','HR'); DBMS_METADATA.SET_FILTER(h,'NAME','TIMECARDS'); -- Request that the schema name be modified. th := DBMS_METADATA.ADD_TRANSFORM(h,'MODIFY'); DBMS_METADATA.SET_REMAP_PARAM(th,'REMAP_SCHEMA','HR','SCOTT'); -- Request that the metadata be transformed into creation DDL. th := DBMS_METADATA.ADD_TRANSFORM(h,'DDL'); -- Specify that segment attributes are not to be returned. DBMS_METADATA.SET_TRANSFORM_PARAM(th,'SEGMENT_ATTRIBUTES',false); -- Fetch the object. doc := DBMS_METADATA.FETCH_CLOB(h); -- Release resources. DBMS_METADATA.CLOSE(h); RETURN doc; END; /
次の問合せを実行します。
SELECT remap_schema FROM dual;
出力は、次のようになります。
CREATE TABLE "SCOTT"."TIMECARDS" ( "EMPLOYEE_ID" NUMBER(6,0), "WEEK" NUMBER(2,0), "JOB_ID" VARCHAR2(10), "HOURS_WORKED" NUMBER(4,2), FOREIGN KEY ("EMPLOYEE_ID") REFERENCES "SCOTT"."EMPLOYEES" ("EMPLOYEE_ID") ENABLE )
XSLTをよく理解しているユーザーであれば、独自の変換を追加してXMLを処理できます。
名前やスキーマなどのオブジェクトのメタデータの特定の属性へアクセスすることが必要な場合もあります。属性の情報は返されたメタデータを解析して取得できますが、DBMS_METADATA
APIには、別の機能もあります。メタデータから解析され、別のデータ構造に返される特定の属性を解析項目に指定できます。これを行うには、SET_PARSE_ITEM
プロシージャを使用します。
例20-6では、スキーマ内のすべての表をフェッチします。各表では、名前を取得するために解析項目を使用します。次に表のすべての索引を取得するために名前を使用します。次の例に、sys
.ku$_ddls
オブジェクトのメタデータを返すFETCH_DDL
ファンクションの使用を示します。
この例では、いくつかの表および索引を含むスキーマに接続していることとします。また、my_metadata
という名前の表も作成します。
例20-6 特定のメタデータ属性にアクセスするための解析項目の使用
次のように、my_metadata
という名前の表と、get_tables_and_indexes
という名前のプロシージャを作成します。
DROP TABLE my_metadata; CREATE TABLE my_metadata ( object_type VARCHAR2(30), name VARCHAR2(30), md CLOB); CREATE OR REPLACE PROCEDURE get_tables_and_indexes IS -- Define local variables. h1 NUMBER; -- handle returned by OPEN for tables h2 NUMBER; -- handle returned by OPEN for indexes th1 NUMBER; -- handle returned by ADD_TRANSFORM for tables th2 NUMBER; -- handle returned by ADD_TRANSFORM for indexes doc sys.ku$_ddls; -- metadata is returned in sys.ku$_ddls, -- a nested table of sys.ku$_ddl objects ddl CLOB; -- creation DDL for an object pi sys.ku$_parsed_items; -- parse items are returned in this object -- which is contained in sys.ku$_ddl objname VARCHAR2(30); -- the parsed object name idxddls sys.ku$_ddls; -- metadata is returned in sys.ku$_ddls, -- a nested table of sys.ku$_ddl objects idxname VARCHAR2(30); -- the parsed index name BEGIN -- This procedure has an outer loop that fetches tables, -- and an inner loop that fetches indexes. -- Specify the object type: TABLE. h1 := DBMS_METADATA.OPEN('TABLE'); -- Request that the table name be returned as a parse item. DBMS_METADATA.SET_PARSE_ITEM(h1,'NAME'); -- Request that the metadata be transformed into creation DDL. th1 := DBMS_METADATA.ADD_TRANSFORM(h1,'DDL'); -- Specify that segment attributes are not to be returned. DBMS_METADATA.SET_TRANSFORM_PARAM(th1,'SEGMENT_ATTRIBUTES',false); -- Set up the outer loop: fetch the TABLE objects. LOOP doc := dbms_metadata.fetch_ddl(h1); -- When there are no more objects to be retrieved, FETCH_DDL returns NULL. EXIT WHEN doc IS NULL; -- Loop through the rows of the ku$_ddls nested table. FOR i IN doc.FIRST..doc.LAST LOOP ddl := doc(i).ddlText; pi := doc(i).parsedItems; -- Loop through the returned parse items. IF pi IS NOT NULL AND pi.COUNT > 0 THEN FOR j IN pi.FIRST..pi.LAST LOOP IF pi(j).item='NAME' THEN objname := pi(j).value; END IF; END LOOP; END IF; -- Insert information about this object into our table. INSERT INTO my_metadata(object_type, name, md) VALUES ('TABLE',objname,ddl); COMMIT; END LOOP; -- Now fetch indexes using the parsed table name as -- a BASE_OBJECT_NAME filter. -- Specify the object type. h2 := DBMS_METADATA.OPEN('INDEX'); -- The base object is the table retrieved in the outer loop. DBMS_METADATA.SET_FILTER(h2,'BASE_OBJECT_NAME',objname); -- Exclude system-generated indexes. DBMS_METADATA.SET_FILTER(h2,'SYSTEM_GENERATED',false); -- Request that the index name be returned as a parse item. DBMS_METADATA.SET_PARSE_ITEM(h2,'NAME'); -- Request that the metadata be transformed into creation DDL. th2 := DBMS_METADATA.ADD_TRANSFORM(h2,'DDL'); -- Specify that segment attributes are not to be returned. DBMS_METADATA.SET_TRANSFORM_PARAM(th2,'SEGMENT_ATTRIBUTES',false); LOOP idxddls := dbms_metadata.fetch_ddl(h2); -- When there are no more objects to be retrieved, FETCH_DDL returns NULL. EXIT WHEN idxddls IS NULL; FOR i in idxddls.FIRST..idxddls.LAST LOOP ddl := idxddls(i).ddlText; pi := idxddls(i).parsedItems; -- Loop through the returned parse items. IF pi IS NOT NULL AND pi.COUNT > 0 THEN FOR j IN pi.FIRST..pi.LAST LOOP IF pi(j).item='NAME' THEN idxname := pi(j).value; END IF; END LOOP; END IF; -- Store the metadata in our table. INSERT INTO my_metadata(object_type, name, md) VALUES ('INDEX',idxname,ddl); COMMIT; END LOOP; -- for loop END LOOP; DBMS_METADATA.CLOSE(h2); END LOOP; DBMS_METADATA.CLOSE(h1); END; /
次のようにプロシージャを実行します。
EXECUTE get_tables_and_indexes;
次の問合せを実行して、取得された内容を確認します。
SET LONG 9000000 SET PAGES 0 SELECT * FROM my_metadata;
オブジェクトに対しメタデータをフェッチする場合、そのメタデータを使用して、オブジェクトを異なるデータベースまたはスキーマで再作成することがあります。
メタデータをフェッチする際に、再マップの実行を決定していない場合もあります。また、この決定を先送りにしたい場合があります。再マップを先送りにするには、メタデータをXMLとしてフェッチし、ファイルまたは表に格納します。後で送信インタフェースを使用しオブジェクトを再作成します。
送信インタフェースは取得インタフェースに類似した構成です。送信インタフェースには、作成するオブジェクトのオブジェクト型を指定するOPENW
プロシージャが存在します。変換の指定、パラメータの変換および項目の解析ができます。CONVERT
ファンクションをコールしてXMLからDDLに変換する、またはPUT
ファンクションをコールしてXMLをDDLに変換し、DDLを送信してオブジェクトを作成できます。
例20-7は、1つのスキーマの表のXMLをフェッチし、次に送信インタフェースを使用して別のスキーマの表を再作成します。
例20-7 取得したオブジェクトを再作成するための送信インタフェースの使用
次のように、権限を持つユーザーとして接続します。
CONNECT system
Enter password: password
実行者権限パッケージを作成してプロシージャを保持します。これは、別のスキーマ内のオブジェクトにアクセスするには、SELECT_CATALOG_ROLE
ロールが必要なためです。定義者権限PL/SQLオブジェクト(プロシージャ、ファンクションなど)では、ロールは使用できません。
CREATE OR REPLACE PACKAGE example_pkg AUTHID current_user IS PROCEDURE move_table( table_name in VARCHAR2, from_schema in VARCHAR2, to_schema in VARCHAR2 ); END example_pkg; / CREATE OR REPLACE PACKAGE BODY example_pkg IS PROCEDURE move_table( table_name in VARCHAR2, from_schema in VARCHAR2, to_schema in VARCHAR2 ) IS -- Define local variables. h1 NUMBER; -- handle returned by OPEN h2 NUMBER; -- handle returned by OPENW th1 NUMBER; -- handle returned by ADD_TRANSFORM for MODIFY th2 NUMBER; -- handle returned by ADD_TRANSFORM for DDL xml CLOB; -- XML document errs sys.ku$_SubmitResults := sys.ku$_SubmitResults(); err sys.ku$_SubmitResult; result BOOLEAN; BEGIN -- Specify the object type. h1 := DBMS_METADATA.OPEN('TABLE'); -- Use filters to specify the name and schema of the table. DBMS_METADATA.SET_FILTER(h1,'NAME',table_name); DBMS_METADATA.SET_FILTER(h1,'SCHEMA',from_schema); -- Fetch the XML. xml := DBMS_METADATA.FETCH_CLOB(h1); IF xml IS NULL THEN DBMS_OUTPUT.PUT_LINE('Table ' || from_schema || '.' || table_name || ' not found'); RETURN; END IF; -- Release resources. DBMS_METADATA.CLOSE(h1); -- Use the submit interface to re-create the object in another schema. -- Specify the object type using OPENW (instead of OPEN). h2 := DBMS_METADATA.OPENW('TABLE'); -- First, add the MODIFY transform. th1 := DBMS_METADATA.ADD_TRANSFORM(h2,'MODIFY'); -- Specify the desired modification: remap the schema name. DBMS_METADATA.SET_REMAP_PARAM(th1,'REMAP_SCHEMA',from_schema,to_schema); -- Now add the DDL transform so that the modified XML can be -- transformed into creation DDL. th2 := DBMS_METADATA.ADD_TRANSFORM(h2,'DDL'); -- Call PUT to re-create the object. result := DBMS_METADATA.PUT(h2,xml,0,errs); DBMS_METADATA.CLOSE(h2); IF NOT result THEN -- Process the error information. FOR i IN errs.FIRST..errs.LAST LOOP err := errs(i); FOR j IN err.errorLines.FIRST..err.errorLines.LAST LOOP dbms_output.put_line(err.errorLines(j).errorText); END LOOP; END LOOP; END IF; END; END example_pkg; /
ここで、次のように、スキーマSCOTT
でmy_example
という名前の表を作成します。
CONNECT scott
Enter password:
-- The password is tiger.
DROP TABLE my_example;
CREATE TABLE my_example (a NUMBER, b VARCHAR2(30));
CONNECT system
Enter password: password
SET LONG 9000000
SET PAGESIZE 0
SET SERVEROUTPUT ON SIZE 100000
次のように、my_example
表をSYSTEM
スキーマにコピーします。
DROP TABLE my_example; EXECUTE example_pkg.move_table('MY_EXAMPLE','SCOTT','SYSTEM');
次の問合せを実行して、正しく動作したことを確認します。
SELECT DBMS_METADATA.GET_DDL('TABLE','MY_EXAMPLE') FROM dual;
異なる型のオブジェクトであるにもかかわらず、1つの論理単位を形成するオブジェクトのコレクションを取得する必要がある場合があります。たとえば、データベースまたはスキーマのすべてのオブジェクト、または表および依存する索引、制約、権限付与、監査などすべてを取得する必要があるとします。DBMS_METADATA
APIでは、こうした取得を可能にするために、複数の異種オブジェクト型を提供しています。異種オブジェクト型は、オブジェクト型の順序付けられた集合です。
Oracleでは、次の異種オブジェクト型を提供しています。
TABLE_EXPORT
: 表およびその依存オブジェクト
SCHEMA_EXPORT
: スキーマおよびその内容
DATABASE_EXPORT
: データベース内のオブジェクト
これらのオブジェクト型は、データ・ポンプ・エクスポート・ユーティリティで使用するために開発されましたが、ユーザー独自のアプリケーションでも使用できます。
これらの型では、ブラウザ・インタフェースまたは送信インタフェースではなく、プログラム取得インタフェース(OPEN
, FETCH
, CLOSE
)のみが使用できます。
同種型と同様に、異種オブジェクト型に対してもフィルタを指定できます。たとえば、TABLE_EXPORT
に対してSCHEMA
およびNAME
フィルタを指定でき、SCHEMA_EXPORT
に対してSCHEMA
フィルタを指定できます。
例20-8に、scott
スキーマ内のオブジェクト型を取得する方法を示します。ユーザーscott
として接続します。パスワードは、tiger
です。
例20-8 異種オブジェクト型の取得方法
取得したオブジェクトを格納するための表を作成します。
DROP TABLE my_metadata; CREATE TABLE my_metadata (md CLOB); CREATE OR REPLACE PROCEDURE get_schema_md IS -- Define local variables. h NUMBER; -- handle returned by OPEN th NUMBER; -- handle returned by ADD_TRANSFORM doc CLOB; -- metadata is returned in a CLOB BEGIN -- Specify the object type. h := DBMS_METADATA.OPEN('SCHEMA_EXPORT'); -- Use filters to specify the schema. DBMS_METADATA.SET_FILTER(h,'SCHEMA','SCOTT'); -- Request that the metadata be transformed into creation DDL. th := DBMS_METADATA.ADD_TRANSFORM(h,'DDL'); -- Fetch the objects. LOOP doc := DBMS_METADATA.FETCH_CLOB(h); -- When there are no more objects to be retrieved, FETCH_CLOB returns NULL. EXIT WHEN doc IS NULL; -- Store the metadata in the table. INSERT INTO my_metadata(md) VALUES (doc); COMMIT; END LOOP; -- Release resources. DBMS_METADATA.CLOSE(h); END; /
次のようにプロシージャを実行します。
EXECUTE get_schema_md;
次の問合せを実行して、取得された内容を確認します。
SET LONG 9000000 SET PAGESIZE 0 SELECT * FROM my_metadata;
この例では、オブジェクトは、オブジェクト型による順序で返されます。たとえば、すべての表が返され、次に表のすべての権限が返され、次に表のすべての索引が返されます。順序は一般的に有効な作成順序となります。このため、返された順序でオブジェクトを取得し、送信インタフェースを使用して別のスキーマまたはデータベースで同じ順序で再作成した場合、通常、エラーは発生しません。(通常、例外は、循環参照の場合に発生します。たとえば、パッケージAにパッケージBへのコールが含まれ、パッケージBにパッケージAへのコールが含まれる場合、2回目にいずれかのパッケージを再コンパイルする必要があります。)
返されるオブジェクトに対して、より詳細な制御を行うには、SET_FILTER
プロシージャを使用して特定のメンバー型にのみフィルタを適用するよう指定します。これを行うには、SET_FILTER
の4つ目のパラメータとして、メンバー型のパス名を指定します。また、EXCLUDE_PATH_EXPR
フィルタを使用して、オブジェクト型のすべてのオブジェクトを除外できます。有効なパス名のリストは、TABLE_EXPORT_OBJECTS
カタログ・ビューを参照してください。
例20-9に、返されるオブジェクトに対して、より詳細な制御を指定するためのSET_FILTER
の使用方法を示します。ユーザーscott
として接続します。パスワードは、tiger
です。
例20-9 異種オブジェクト型の返りのフィルタ
取得したオブジェクトを格納するための表my_metadata
を作成します。さらに、プロシージャget_schema_md2
を作成します。
DROP TABLE my_metadata; CREATE TABLE my_metadata (md CLOB); CREATE OR REPLACE PROCEDURE get_schema_md2 IS -- Define local variables. h NUMBER; -- handle returned by 'OPEN' th NUMBER; -- handle returned by 'ADD_TRANSFORM' doc CLOB; -- metadata is returned in a CLOB BEGIN -- Specify the object type. h := DBMS_METADATA.OPEN('SCHEMA_EXPORT'); -- Use filters to specify the schema. DBMS_METADATA.SET_FILTER(h,'SCHEMA','SCOTT'); -- Use the fourth parameter to SET_FILTER to specify a filter -- that applies to a specific member object type. DBMS_METADATA.SET_FILTER(h,'NAME_EXPR','!=''MY_METADATA''','TABLE'); -- Use the EXCLUDE_PATH_EXPR filter to exclude procedures. DBMS_METADATA.SET_FILTER(h,'EXCLUDE_PATH_EXPR','=''PROCEDURE'''); -- Request that the metadata be transformed into creation DDL. th := DBMS_METADATA.ADD_TRANSFORM(h,'DDL'); -- Use the fourth parameter to SET_TRANSFORM_PARAM to specify a parameter -- that applies to a specific member object type. DBMS_METADATA.SET_TRANSFORM_PARAM(th,'SEGMENT_ATTRIBUTES',false,'TABLE'); -- Fetch the objects. LOOP doc := dbms_metadata.fetch_clob(h); -- When there are no more objects to be retrieved, FETCH_CLOB returns NULL. EXIT WHEN doc IS NULL; -- Store the metadata in the table. INSERT INTO my_metadata(md) VALUES (doc); COMMIT; END LOOP; -- Release resources. DBMS_METADATA.CLOSE(h); END; /
次のようにプロシージャを実行します。
EXECUTE get_schema_md2;
次の問合せを実行して、取得された内容を確認します。
SET LONG 9000000 SET PAGESIZE 0 SELECT * FROM my_metadata;
この項では、DBMS_METADATA
およびDBMS_METADATA_DIFF
の取得インタフェース、比較インタフェースおよび送信インタフェースの使用例を示します。この例では、2つの表のメタデータをフェッチして比較し、1つの表を他方と同じ内容に変更するALTER
文を生成します。わかりやすくするために、この例全体でファンクションの変形を使用しています。
例20-10 オブジェクト・メタデータの比較
次のように、2つの表TAB1
、TAB2
を作成します。
SQL> CREATE TABLE TAB1 2 ( "EMPNO" NUMBER(4,0), 3 "ENAME" VARCHAR2(10), 4 "JOB" VARCHAR2(9), 5 "DEPTNO" NUMBER(2,0) 6 ) ; Table created. SQL> CREATE TABLE TAB2 2 ( "EMPNO" NUMBER(4,0) PRIMARY KEY ENABLE, 3 "ENAME" VARCHAR2(20), 4 "MGR" NUMBER(4,0), 5 "DEPTNO" NUMBER(2,0) 6 ) ; Table created.
表TAB1
とTAB2
の差異に注意します。
表名が異なります。
TAB2
には主キー制約がありますが、TAB1
にはありません。
それぞれの表で、ENAME
列の長さが異なります。
TAB1
にはJOB
列がありますが、TAB2
にはありません。
TAB2
にはMGR
列がありますが、TAB1
にはありません。
SXML形式の表メタデータを返すファンクションを作成します。DBMS_METADATA_DIFF
APIを使用するときに、SXMLに関して注意する必要がある重要な点について示します。
SXMLはオブジェクト・メタデータのXML表現です。
返されるSXMLは、DBMS_METADATA.GET_XML
によって返されるXMLと同じではありません。その場合のXMLは、複雑かつ不透明であり、バイナリ値やインスタンス固有の値などが含まれます。
SXMLは、SQL作成DDLを直接XMLに変換したものと考えることができます。タグ名と構造は、『Oracle Database SQL言語リファレンス』で説明されている名前に対応しています。
SXMLは、編集と比較を行えるように設計されています。
ここでは例を単純にするため、変換パラメータを使用して物理プロパティを抑止します。
SQL> CREATE OR REPLACE FUNCTION get_table_sxml(name IN VARCHAR2) RETURN CLOB IS 2 open_handle NUMBER; 3 transform_handle NUMBER; 4 doc CLOB; 5 BEGIN 6 open_handle := DBMS_METADATA.OPEN('TABLE'); 7 DBMS_METADATA.SET_FILTER(open_handle,'NAME',name); 8 -- 9 -- Use the 'SXML' transform to convert XML to SXML 10 -- 11 transform_handle := DBMS_METADATA.ADD_TRANSFORM(open_handle,'SXML'); 12 -- 13 -- Use this transform parameter to suppress physical properties 14 -- 15 DBMS_METADATA.SET_TRANSFORM_PARAM(transform_handle,'PHYSICAL_PROPERTIES', 16 FALSE); 17 doc := DBMS_METADATA.FETCH_CLOB(open_handle); 18 DBMS_METADATA.CLOSE(open_handle); 19 RETURN doc; 20 END; 21 / Function created.
次のように、get_table_sxml
ファンクションを使用して、2つの表の表SXMLをフェッチします。
SQL> SELECT get_table_sxml('TAB1') FROM dual; <TABLE xmlns="http://xmlns.oracle.com/ku" version="1.0"> <SCHEMA>SCOTT</SCHEMA> <NAME>TAB1</NAME> <RELATIONAL_TABLE> <COL_LIST> <COL_LIST_ITEM> <NAME>EMPNO</NAME> <DATATYPE>NUMBER</DATATYPE> <PRECISION>4</PRECISION> <SCALE>0</SCALE> </COL_LIST_ITEM> <COL_LIST_ITEM> <NAME>ENAME</NAME> <DATATYPE>VARCHAR2</DATATYPE> <LENGTH>10</LENGTH> </COL_LIST_ITEM> <COL_LIST_ITEM> <NAME>JOB</NAME> <DATATYPE>VARCHAR2</DATATYPE> <LENGTH>9</LENGTH> </COL_LIST_ITEM> <COL_LIST_ITEM> <NAME>DEPTNO</NAME> <DATATYPE>NUMBER</DATATYPE> <PRECISION>2</PRECISION> <SCALE>0</SCALE> </COL_LIST_ITEM> </COL_LIST> </RELATIONAL_TABLE> </TABLE> 1 row selected. SQL> SELECT get_table_sxml('TAB2') FROM dual; <TABLE xmlns="http://xmlns.oracle.com/ku" version="1.0"> <SCHEMA>SCOTT</SCHEMA> <NAME>TAB2</NAME> <RELATIONAL_TABLE> <COL_LIST> <COL_LIST_ITEM> <NAME>EMPNO</NAME> <DATATYPE>NUMBER</DATATYPE> <PRECISION>4</PRECISION> <SCALE>0</SCALE> </COL_LIST_ITEM> <COL_LIST_ITEM> <NAME>ENAME</NAME> <DATATYPE>VARCHAR2</DATATYPE> <LENGTH>20</LENGTH> </COL_LIST_ITEM> <COL_LIST_ITEM> <NAME>MGR</NAME> <DATATYPE>NUMBER</DATATYPE> <PRECISION>4</PRECISION> <SCALE>0</SCALE> </COL_LIST_ITEM> <COL_LIST_ITEM> <NAME>DEPTNO</NAME> <DATATYPE>NUMBER</DATATYPE> <PRECISION>2</PRECISION> <SCALE>0</SCALE> </COL_LIST_ITEM> </COL_LIST> <PRIMARY_KEY_CONSTRAINT_LIST> <PRIMARY_KEY_CONSTRAINT_LIST_ITEM> <COL_LIST> <COL_LIST_ITEM> <NAME>EMPNO</NAME> </COL_LIST_ITEM> </COL_LIST> </PRIMARY_KEY_CONSTRAINT_LIST_ITEM> </PRIMARY_KEY_CONSTRAINT_LIST> </RELATIONAL_TABLE> </TABLE> 1 row selected.
次のように、DBMS_METADATA
ブラウザAPIを使用して結果を比較します。
SQL> SELECT dbms_metadata.get_sxml('TABLE','TAB1') FROM dual; SQL> SELECT dbms_metadata.get_sxml('TABLE','TAB2') FROM dual;
DBMS_METADATA_DIFF
APIを使用してファンクションを作成し、2つの表のメタデータを比較します。このファンクションでは、手順2で定義したget_table_sxml
ファンクションを使用します。
SQL> CREATE OR REPLACE FUNCTION compare_table_sxml(name1 IN VARCHAR2, 2 name2 IN VARCHAR2) RETURN CLOB IS 3 doc1 CLOB; 4 doc2 CLOB; 5 diffdoc CLOB; 6 openc_handle NUMBER; 7 BEGIN 8 -- 9 -- Fetch the SXML for the two tables 10 -- 11 doc1 := get_table_sxml(name1); 12 doc2 := get_table_sxml(name2); 13 -- 14 -- Specify the object type in the OPENC call 15 -- 16 openc_handle := DBMS_METADATA_DIFF.OPENC('TABLE'); 17 -- 18 -- Add each document 19 -- 20 DBMS_METADATA_DIFF.ADD_DOCUMENT(openc_handle,doc1); 21 DBMS_METADATA_DIFF.ADD_DOCUMENT(openc_handle,doc2); 22 -- 23 -- Fetch the SXML difference document 24 -- 25 diffdoc := DBMS_METADATA_DIFF.FETCH_CLOB(openc_handle); 26 DBMS_METADATA_DIFF.CLOSE(openc_handle); 27 RETURN diffdoc; 28 END; 29 / Function created.
次のようにファンクションを使用して、2つの表のSXML差分ドキュメントをフェッチします。
SQL> SELECT compare_table_sxml('TAB1','TAB2') FROM dual; <TABLE xmlns="http://xmlns.oracle.com/ku" version="1.0"> <SCHEMA>SCOTT</SCHEMA> <NAME value1="TAB1">TAB2</NAME> <RELATIONAL_TABLE> <COL_LIST> <COL_LIST_ITEM> <NAME>EMPNO</NAME> <DATATYPE>NUMBER</DATATYPE> <PRECISION>4</PRECISION> <SCALE>0</SCALE> </COL_LIST_ITEM> <COL_LIST_ITEM> <NAME>ENAME</NAME> <DATATYPE>VARCHAR2</DATATYPE> <LENGTH value1="10">20</LENGTH> </COL_LIST_ITEM> <COL_LIST_ITEM src="1"> <NAME>JOB</NAME> <DATATYPE>VARCHAR2</DATATYPE> <LENGTH>9</LENGTH> </COL_LIST_ITEM> <COL_LIST_ITEM> <NAME>DEPTNO</NAME> <DATATYPE>NUMBER</DATATYPE> <PRECISION>2</PRECISION> <SCALE>0</SCALE> </COL_LIST_ITEM> <COL_LIST_ITEM src="2"> <NAME>MGR</NAME> <DATATYPE>NUMBER</DATATYPE> <PRECISION>4</PRECISION> <SCALE>0</SCALE> </COL_LIST_ITEM> </COL_LIST> <PRIMARY_KEY_CONSTRAINT_LIST src="2"> <PRIMARY_KEY_CONSTRAINT_LIST_ITEM> <COL_LIST> <COL_LIST_ITEM> <NAME>EMPNO</NAME> </COL_LIST_ITEM> </COL_LIST> </PRIMARY_KEY_CONSTRAINT_LIST_ITEM> </PRIMARY_KEY_CONSTRAINT_LIST> </RELATIONAL_TABLE> </TABLE> 1 row selected.
SXML差分ドキュメントでは、2つのSXMLドキュメントの結合内容が示され、差分を識別するXML属性value1
およびsrc
が使用されます。ある要素が1つのドキュメントのみに存在するときは、その要素がsrc
でマーキングされます。したがって、<COL_LIST_ITEM src="1">
は、この要素が最初のドキュメント(TAB1
)に含まれていて、2番目のドキュメントに含まれていないことを表します。ある要素が両方のドキュメントに存在し、値がそれぞれ異なる場合、この要素の値は2番目のドキュメント内の値とされ、value1
によって最初のドキュメント内のその値が示されます。たとえば、<LENGTH value1="10">20</LENGTH>
は、長さがTAB1
(最初のドキュメント)では10、TAB2
では20であることを表します。
次のように、DBMS_METADATA_DIFF
ブラウザAPIを使用して、結果を比較します。
SQL> SELECT dbms_metadata_diff.compare_sxml('TABLE','TAB1','TAB2') FROM dual;
DBMS_METADATA.CONVERT
APIを使用してファンクションを作成し、ALTERXMLドキュメントを生成します。これは、片方のオブジェクトをもう一方のオブジェクトと同様にするためのALTER
文が含まれているXML文書です。解析項目を使用して、個々のALTER
文に関する情報を取得することもできます。(この例ではこれまでに定義したファンクションを使用しています。)
SQL> CREATE OR REPLACE FUNCTION get_table_alterxml(name1 IN VARCHAR2, 2 name2 IN VARCHAR2) RETURN CLOB IS 3 diffdoc CLOB; 4 openw_handle NUMBER; 5 transform_handle NUMBER; 6 alterxml CLOB; 7 BEGIN 8 -- 9 -- Use the function just defined to get the difference document 10 -- 11 diffdoc := compare_table_sxml(name1,name2); 12 -- 13 -- Specify the object type in the OPENW call 14 -- 15 openw_handle := DBMS_METADATA.OPENW('TABLE'); 16 -- 17 -- Use the ALTERXML transform to generate the ALTER_XML document 18 -- 19 transform_handle := DBMS_METADATA.ADD_TRANSFORM(openw_handle,'ALTERXML'); 20 -- 21 -- Request parse items 22 -- 23 DBMS_METADATA.SET_PARSE_ITEM(openw_handle,'CLAUSE_TYPE'); 24 DBMS_METADATA.SET_PARSE_ITEM(openw_handle,'NAME'); 25 DBMS_METADATA.SET_PARSE_ITEM(openw_handle,'COLUMN_ATTRIBUTE'); 26 -- 27 -- Create a temporary LOB 28 -- 29 DBMS_LOB.CREATETEMPORARY(alterxml, TRUE ); 30 -- 31 -- Call CONVERT to do the transform 32 -- 33 DBMS_METADATA.CONVERT(openw_handle,diffdoc,alterxml); 34 -- 35 -- Close context and return the result 36 -- 37 DBMS_METADATA.CLOSE(openw_handle); 38 RETURN alterxml; 39 END; 40 / Function created.
次のようにファンクションを使用して、ALTER_XML文書をフェッチします。
SQL> SELECT get_table_alterxml('TAB1','TAB2') FROM dual; <ALTER_XML xmlns="http://xmlns.oracle.com/ku" version="1.0"> <OBJECT_TYPE>TABLE</OBJECT_TYPE> <OBJECT1> <SCHEMA>SCOTT</SCHEMA> <NAME>TAB1</NAME> </OBJECT1> <OBJECT2> <SCHEMA>SCOTT</SCHEMA> <NAME>TAB2</NAME> </OBJECT2> <ALTER_LIST> <ALTER_LIST_ITEM> <PARSE_LIST> <PARSE_LIST_ITEM> <ITEM>NAME</ITEM> <VALUE>MGR</VALUE> </PARSE_LIST_ITEM> <PARSE_LIST_ITEM> <ITEM>CLAUSE_TYPE</ITEM> <VALUE>ADD_COLUMN</VALUE> </PARSE_LIST_ITEM> </PARSE_LIST> <SQL_LIST> <SQL_LIST_ITEM> <TEXT>ALTER TABLE "SCOTT"."TAB1" ADD ("MGR" NUMBER(4,0))</TEXT> </SQL_LIST_ITEM> </SQL_LIST> </ALTER_LIST_ITEM> <ALTER_LIST_ITEM> <PARSE_LIST> <PARSE_LIST_ITEM> <ITEM>NAME</ITEM> <VALUE>JOB</VALUE> </PARSE_LIST_ITEM> <PARSE_LIST_ITEM> <ITEM>CLAUSE_TYPE</ITEM> <VALUE>DROP_COLUMN</VALUE> </PARSE_LIST_ITEM> </PARSE_LIST> <SQL_LIST> <SQL_LIST_ITEM> <TEXT>ALTER TABLE "SCOTT"."TAB1" DROP ("JOB")</TEXT> </SQL_LIST_ITEM> </SQL_LIST> </ALTER_LIST_ITEM> <ALTER_LIST_ITEM> <PARSE_LIST> <PARSE_LIST_ITEM> <ITEM>NAME</ITEM> <VALUE>ENAME</VALUE> </PARSE_LIST_ITEM> <PARSE_LIST_ITEM> <ITEM>CLAUSE_TYPE</ITEM> <VALUE>MODIFY_COLUMN</VALUE> </PARSE_LIST_ITEM> <PARSE_LIST_ITEM> <ITEM>COLUMN_ATTRIBUTE</ITEM> <VALUE> SIZE_INCREASE</VALUE> </PARSE_LIST_ITEM> </PARSE_LIST> <SQL_LIST> <SQL_LIST_ITEM> <TEXT>ALTER TABLE "SCOTT"."TAB1" MODIFY ("ENAME" VARCHAR2(20)) </TEXT> </SQL_LIST_ITEM> </SQL_LIST> </ALTER_LIST_ITEM> <ALTER_LIST_ITEM> <PARSE_LIST> <PARSE_LIST_ITEM> <ITEM>CLAUSE_TYPE</ITEM> <VALUE>ADD_CONSTRAINT</VALUE> </PARSE_LIST_ITEM> </PARSE_LIST> <SQL_LIST> <SQL_LIST_ITEM> <TEXT>ALTER TABLE "SCOTT"."TAB1" ADD PRIMARY KEY ("EMPNO") ENABLE </TEXT> </SQL_LIST_ITEM> </SQL_LIST> </ALTER_LIST_ITEM> <ALTER_LIST_ITEM> <PARSE_LIST> <PARSE_LIST_ITEM> <ITEM>NAME</ITEM> <VALUE>TAB1</VALUE> </PARSE_LIST_ITEM> <PARSE_LIST_ITEM> <ITEM>CLAUSE_TYPE</ITEM> <VALUE>RENAME_TABLE</VALUE> </PARSE_LIST_ITEM> </PARSE_LIST> <SQL_LIST> <SQL_LIST_ITEM> <TEXT>ALTER TABLE "SCOTT"."TAB1" RENAME TO "TAB2"</TEXT> </SQL_LIST_ITEM> </SQL_LIST> </ALTER_LIST_ITEM> </ALTER_LIST> </ALTER_XML> 1 row selected.
次のように、DBMS_METADATA_DIFF
ブラウザAPIを使用して、結果を比較します。
SQL> SELECT dbms_metadata_diff.compare_alter_xml('TABLE','TAB1','TAB2') FROM dual;
ALTER_XML文書には、それぞれの変更のALTER_LISTが含まれています。それぞれのALTER_LIST_ITEMには、名前と値のペアとしての解析項目が含まれたPARSE_LISTと、特定の変更のSQLが含まれたSQL_LISTがあります。PARSE_LIST内の情報を使用してこの文書を解析し、どのSQL文を実行するかを決定できます。(たとえば、この場合は、変更の1つがDROP_COLUMNであり、それを実行しないことを決定できます。)
次のように、DBMS_METADATA.CONVERT
APIおよびALTER DDL
変換を使用する最後の1つのファンクションを作成して、ALTER_XML文書をSQL DDLに変換します。
SQL> CREATE OR REPLACE FUNCTION get_table_alterddl(name1 IN VARCHAR2, 2 name2 IN VARCHAR2) RETURN CLOB IS 3 alterxml CLOB; 4 openw_handle NUMBER; 5 transform_handle NUMBER; 6 alterddl CLOB; 7 BEGIN 8 -- 9 -- Use the function just defined to get the ALTER_XML document 10 -- 11 alterxml := get_table_alterxml(name1,name2); 12 -- 13 -- Specify the object type in the OPENW call 14 -- 15 openw_handle := DBMS_METADATA.OPENW('TABLE'); 16 -- 17 -- Use ALTERDDL transform to convert the ALTER_XML document to SQL DDL 18 -- 19 transform_handle := DBMS_METADATA.ADD_TRANSFORM(openw_handle,'ALTERDDL'); 20 -- 21 -- Use the SQLTERMINATOR transform parameter to append a terminator 22 -- to each SQL statement 23 -- 24 DBMS_METADATA.SET_TRANSFORM_PARAM(transform_handle,'SQLTERMINATOR',true); 25 -- 26 -- Create a temporary lob 27 -- 28 DBMS_LOB.CREATETEMPORARY(alterddl, TRUE ); 29 -- 30 -- Call CONVERT to do the transform 31 -- 32 DBMS_METADATA.CONVERT(openw_handle,alterxml,alterddl); 33 -- 34 -- Close context and return the result 35 -- 36 DBMS_METADATA.CLOSE(openw_handle); 37 RETURN alterddl; 38 END; 39 / Function created.
次のようにファンクションを使用して、SQL ALTER文をフェッチします。
SQL> SELECT get_table_alterddl('TAB1','TAB2') FROM dual; ALTER TABLE "SCOTT"."TAB1" ADD ("MGR" NUMBER(4,0)) / ALTER TABLE "SCOTT"."TAB1" DROP ("JOB") / ALTER TABLE "SCOTT"."TAB1" MODIFY ("ENAME" VARCHAR2(20)) / ALTER TABLE "SCOTT"."TAB1" ADD PRIMARY KEY ("EMPNO") ENABLE / ALTER TABLE "SCOTT"."TAB1" RENAME TO "TAB2" / 1 row selected.
次のように、DBMS_METADATA_DIFF
ブラウザAPIを使用して、結果を比較します。
SQL> SELECT dbms_metadata_diff.compare_alter('TABLE','TAB1','TAB2') FROM dual; ALTER TABLE "SCOTT"."TAB1" ADD ("MGR" NUMBER(4,0)) ALTER TABLE "SCOTT"."TAB1" DROP ("JOB") ALTER TABLE "SCOTT"."TAB1" MODIFY ("ENAME" VARCHAR2(20)) ALTER TABLE "SCOTT"."TAB1" ADD PRIMARY KEY ("EMPNO") USING INDEX PCTFREE 10 INITRANS 2 STORAGE ( INITIAL 16384 NEXT 16384 MINEXTENTS 1 MAXEXTENTS 505 PCTINCREASE 50 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) ENABLE ALTER TABLE "SCOTT"."TAB1" RENAME TO "TAB2" 1 row selected.
この項では、DBMS_METADATA
APIのプログラム・インタフェース使用時にパフォーマンスを向上する方法について説明します。
ある型のすべてのオブジェクトを、次の型のオブジェクトをフェッチする前にフェッチします。たとえば、スキーマ内に含まれるすべてのオブジェクトの定義を取得する場合は、まずすべての表をフェッチし、次にすべての索引をフェッチし、その次にすべてのトリガーをフェッチします。この方法は、OPEN
コンテキストをネストする方法(表をフェッチした後でそのすべての索引、権限およびトリガーをフェッチし、その後、次の表をフェッチした後でそのすべての索引、権限およびトリガーをフェッチするという方法)より非常に高速です。「DBMS_METADATA APIの使用例」では、この非効率な後者の方法が示されていますが、その例の目的はほとんどのプログラム・コールを示すことであり、この方法がその目的に最適です。
SET_COUNT
プロシージャを使用して、複数のオブジェクトを一度に取得します。これによってサーバーのラウンドトリップが最小化され、多くの冗長なファンクション・コールが削減されます。
DBMS_METADATA
APIをコールするPL/SQLパッケージを記述する場合、LOB変数およびLOBを含むオブジェクト(SYS
.KU
$_DDLS
など)は、個々のファンクション内ではなく、パッケージ・スコープで宣言します。これによって、ファンクションの開始および終了時に、負荷の高い操作である、LOBの存続時間構造の作成および削除が実行されなくなります。
参照: Oracle Database SecureFiles and Large Objects開発者ガイド |
この項では、DBMS_METADATA
APIの使用例を示します。デモを自動的に実行するスクリプトの手順は、次のとおりです。
スキーマ(MDDEMO
)および何人かの従業員名簿のユーザーを確立します。
スキーマ内に従業員名簿に似せた表を3つ作成し、関連する索引、トリガーおよび権限を作成します。
DBMS_METADATA
APIを使用するPAYROLL_DEMO
というパッケージを作成します。PAYROLL_DEMO
パッケージには、PAYROLL
から始まるMDDEMO
スキーマ内の2つの表のDDLを取得するGET_PAYROLL_TABLES
というプロシージャを含めます。各表に対して、表の関連する依存オブジェクト、索引、権限およびトリガーのDDLを取得します。すべてのDDLは、MDDEMO
.DDL
という名前の表に書き込まれます。
例を実行するには、次の手順に従います。
ユーザーsystem
としてSQL*Plusを起動します。パスワードを入力するように要求されます。
sqlplus system
デモをインストールします。このデモは、rdbms
/demo
にあるファイルmddemo.sql
に含まれています。
SQL> @mddemo
この手順で行われる処理の詳細は、「DBMS_METADATAの例で行われる処理」を参照してください。
ユーザーmddemo
として接続します。パスワードを入力するように要求されます。パスワードもmddemo
です。
SQL> CONNECT mddemo Enter password:
問合せの出力を完全なものにして読取り可能にするために、次のパラメータを設定します
SQL> SET PAGESIZE 0 SQL> SET LONG 1000000
次のとおりGET_PAYROLL_TABLES
プロシージャを実行します。
SQL> CALL payroll_demo.get_payroll_tables();
次のSQL問合せを実行します。
SQL> SELECT ddl FROM DDL ORDER BY SEQNO;
生成される出力は、GET_PAYROLL_TABLES
プロシージャの実行結果です。デモのインストール時に、手順2で行われたすべてのDDLが示されます。実際の出力のリストについては、「GET_PAYROLL_TABLESプロシージャで生成される出力」を参照してください。
mddemo
スクリプトを実行すると、次の手順が行われます。これらの手順をユーザーの状況に合わせることができます。
ユーザーが存在する場合は、次のとおり削除します。これで、新しいデータを使用した作業が開始できます。ユーザーが存在しない場合は、その影響を示すメッセージが表示されますが、問題はありません。デモは継続して実行されます。
CONNECT system
Enter password: password
SQL> DROP USER mddemo CASCADE;
SQL> DROP USER mddemo_clerk CASCADE;
SQL> DROP USER mddemo_mgr CASCADE;
mddemo
で識別されるユーザーmddemo
を作成します。
SQL> CREATE USER mddemo IDENTIFIED BY mddemo; SQL> GRANT resource, connect, create session, 1 create table, 2 create procedure, 3 create sequence, 4 create trigger, 5 create view, 6 create synonym, 7 alter session, 8 TO mddemo;
clerk
で識別されるユーザーmddemo_clerk
を作成します。
CREATE USER mddemo_clerk IDENTIFIED BY clerk;
mgr
で識別されるユーザーmddemo_mgr
を作成します。
CREATE USER mddemo_mgr IDENTIFIED BY mgr;
mddemo
としてSQL*Plusに接続します(パスワードもmddemo
です)。
CONNECT mddemo Enter password:
いくつかの従業員名簿型の表を作成します。
SQL> CREATE TABLE payroll_emps 2 ( lastname VARCHAR2(60) NOT NULL, 3 firstname VARCHAR2(20) NOT NULL, 4 mi VARCHAR2(2), 5 suffix VARCHAR2(10), 6 dob DATE NOT NULL, 7 badge_no NUMBER(6) PRIMARY KEY, 8 exempt VARCHAR(1) NOT NULL, 9 salary NUMBER (9,2), 10 hourly_rate NUMBER (7,2) ) 11 / SQL> CREATE TABLE payroll_timecards 2 (badge_no NUMBER(6) REFERENCES payroll_emps (badge_no), 3 week NUMBER(2), 4 job_id NUMBER(5), 5 hours_worked NUMBER(4,2) ) 6 /
audit_trail
というダミーの表を作成します。この表は、payroll
で始まらない表は、GET_PAYROLL_TABLES
プロシージャでは取得されないことを示すために使用します。
SQL> CREATE TABLE audit_trail 2 (action_time DATE, 3 lastname VARCHAR2(60), 4 action LONG ) 5 /
作成された表の権限を作成します。
SQL> GRANT UPDATE (salary,hourly_rate) ON payroll_emps TO mddemo_clerk; SQL> GRANT ALL ON payroll_emps TO mddemo_mgr WITH GRANT OPTION; SQL> GRANT INSERT,UPDATE ON payroll_timecards TO mddemo_clerk; SQL> GRANT ALL ON payroll_timecards TO mddemo_mgr WITH GRANT OPTION;
作成された表の索引を作成します。
SQL> CREATE INDEX i_payroll_emps_name ON payroll_emps(lastname); SQL> CREATE INDEX i_payroll_emps_dob ON payroll_emps(dob); SQL> CREATE INDEX i_payroll_timecards_badge ON payroll_timecards(badge_no);
作成された表のトリガーを作成します。
SQL> CREATE OR REPLACE PROCEDURE check_sal( salary in number) AS BEGIN 2 RETURN; 3 END; 4 /
例を簡単にするために、セキュリティは比較的低く設定しています。
SQL> CREATE OR REPLACE TRIGGER salary_trigger BEFORE INSERT OR UPDATE OF salary ON payroll_emps FOR EACH ROW WHEN (new.salary > 150000) CALL check_sal(:new.salary) / SQL> CREATE OR REPLACE TRIGGER hourly_trigger BEFORE UPDATE OF hourly_rate ON payroll_emps FOR EACH ROW BEGIN :new.hourly_rate:=:old.hourly_rate;END; /
生成されたDDLを保持する表を設定します。
CREATE TABLE ddl (ddl CLOB, seqno NUMBER);
DBMS_METADATA
プロシージャを使用できる例を提供するPAYROLL_DEMO
パッケージを作成します。
SQL> CREATE OR REPLACE PACKAGE payroll_demo AS PROCEDURE get_payroll_tables; END; /
注意: PAYROLL_DEMO パッケージの内容を含めて、この例のスクリプト全体を参照するには、$ORACLE_HOME/rdbms/demo ディレクトリにあるファイルmddemo.sql を参照してください。 |
mddemo.payroll_demo.get_payroll_tables
プロシージャの実行後に、次の問合せを実行できます。
SQL> SELECT ddl FROM ddl ORDER BY seqno;
結果は次のとおりです。前述の項で説明したとおり、スクリプトによって実行されたすべてのDDLが反映されます。
CREATE TABLE "MDDEMO"."PAYROLL_EMPS" ( "LASTNAME" VARCHAR2(60) NOT NULL ENABLE, "FIRSTNAME" VARCHAR2(20) NOT NULL ENABLE, "MI" VARCHAR2(2), "SUFFIX" VARCHAR2(10), "DOB" DATE NOT NULL ENABLE, "BADGE_NO" NUMBER(6,0), "EXEMPT" VARCHAR2(1) NOT NULL ENABLE, "SALARY" NUMBER(9,2), "HOURLY_RATE" NUMBER(7,2), PRIMARY KEY ("BADGE_NO") ENABLE ) ; GRANT UPDATE ("SALARY") ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_CLERK"; GRANT UPDATE ("HOURLY_RATE") ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_CLERK"; GRANT ALTER ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT DELETE ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT INDEX ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT INSERT ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT SELECT ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT UPDATE ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT REFERENCES ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT ON COMMIT REFRESH ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT QUERY REWRITE ON "MDDEMO"."PAYROLL_EMPS" TO "MDDEMO_MGR" WITH GRANT OPTION; CREATE INDEX "MDDEMO"."I_PAYROLL_EMPS_DOB" ON "MDDEMO"."PAYROLL_EMPS" ("DOB") PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 10240 NEXT 10240 MINEXTENTS 1 MAXEXTENTS 121 PCTINCREASE 50 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM" ; CREATE INDEX "MDDEMO"."I_PAYROLL_EMPS_NAME" ON "MDDEMO"."PAYROLL_EMPS" ("LASTNAME") PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 10240 NEXT 10240 MINEXTENTS 1 MAXEXTENTS 121 PCTINCREASE 50 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM" ; CREATE OR REPLACE TRIGGER hourly_trigger before update of hourly_rate on payroll_emps for each row begin :new.hourly_rate:=:old.hourly_rate;end; / ALTER TRIGGER "MDDEMO"."HOURLY_TRIGGER" ENABLE; CREATE OR REPLACE TRIGGER salary_trigger before insert or update of salary on payroll_emps for each row WHEN (new.salary > 150000) CALL check_sal(:new.salary) / ALTER TRIGGER "MDDEMO"."SALARY_TRIGGER" ENABLE; CREATE TABLE "MDDEMO"."PAYROLL_TIMECARDS" ( "BADGE_NO" NUMBER(6,0), "WEEK" NUMBER(2,0), "JOB_ID" NUMBER(5,0), "HOURS_WORKED" NUMBER(4,2), FOREIGN KEY ("BADGE_NO") REFERENCES "MDDEMO"."PAYROLL_EMPS" ("BADGE_NO") ENABLE ) ; GRANT INSERT ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_CLERK"; GRANT UPDATE ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_CLERK"; GRANT ALTER ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT DELETE ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT INDEX ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT INSERT ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT SELECT ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT UPDATE ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT REFERENCES ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT ON COMMIT REFRESH ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; GRANT QUERY REWRITE ON "MDDEMO"."PAYROLL_TIMECARDS" TO "MDDEMO_MGR" WITH GRANT OPTION; CREATE INDEX "MDDEMO"."I_PAYROLL_TIMECARDS_BADGE" ON "MDDEMO"."PAYROLL_TIMECARDS" ("BADGE_NO") PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 10240 NEXT 10240 MINEXTENTS 1 MAXEXTENTS 121 PCTINCREASE 50 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM" ;
この項では、DBMS_METADATA
APIによって提供されるプロシージャを簡単に説明します。これらのプロシージャの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。
表20-1では、複数オブジェクトの取得に使用する、DBMS_METADATA
プログラム・インタフェースのプロシージャを簡単に説明します。
表20-1 複数オブジェクトの取得に使用するDBMS_METADATAプロシージャ
PL/SQLプロシージャ名 | 説明 |
---|---|
DBMS_METADATA.OPEN() |
取得するオブジェクトの型、メタデータのバージョンおよびオブジェクト・モデルを指定します。 |
DBMS_METADATA.SET_FILTER() |
取得するオブジェクトに、オブジェクト名、スキーマなどの制限を指定します。 |
DBMS_METADATA.SET_COUNT() |
1回の |
DBMS_METADATA.GET_QUERY() |
|
DBMS_METADATA.SET_PARSE_ITEM() |
解析して返すオブジェクトの属性を指定して、出力を解析に利用します。 |
DBMS_METADATA.ADD_TRANSFORM() |
取得したオブジェクトのXML表現に、 |
DBMS_METADATA.SET_TRANSFORM_PARAM() |
|
DBMS_METADATA.SET_REMAP_PARAM() |
|
DBMS_METADATA.FETCH_xxx() |
|
DBMS_METADATA.CLOSE() |
|
表20-2では、DBMS_METADATA
ブラウザ・インタフェースで提供されるプロシージャを簡単に説明します。これらのファンクションは、1つ以上の依存オブジェクトまたは権限付与されたオブジェクトのメタデータを返します。これらのプロシージャでは、異種オブジェクト型はサポートされません。
表20-2 ブラウザ・インタフェースで使用するDBMS_METADATAプロシージャ
PL/SQLプロシージャ名 | 説明 |
---|---|
DBMS_METADATA.GET_xxx() |
シングル・オブジェクトに対してメタデータを返す方法を提供します。各
変換を指定した場合、セッション・レベルの変換フラグが継承されます。 |
DBMS_METADATA.GET_DEPENDENT_xxx() |
XMLまたはDDLで指定したとおり、1つ以上の依存オブジェクトに対してメタデータが返されます。 |
DBMS_METADATA.GET_GRANTED_xxx() |
XMLまたはDDLで指定したとおり、権限が付与された1つ以上のオブジェクトに対してメタデータが返されます。 |
表20-3では、XMLの送信で使用されるDBMS_METADATA
プロシージャおよびファンクションを簡単に説明します。
表20-3 XMLデータの送信に使用するDBMS_METADATAのプロシージャおよびファンクション
PL/SQL名 | 説明 |
---|---|
DBMS_METADATA.OPENW() |
書込みコンテキストをオープンします。 |
DBMS_METADATA.ADD_TRANSFORM() |
XML文書の変換を指定します。 |
DBMS_METADATA.SET_TRANSFORM_PARAM() and DBMS_METADATA.SET_REMAP_PARAM() |
|
DBMS_METADATA.SET_PARSE_ITEM() |
解析するオブジェクト属性を指定します。 |
DBMS_METADATA.CONVERT() |
XML文書をDDLに変換します。 |
DBMS_METADATA.PUT() |
データベースにXML文書を送信します。 |
DBMS_METADATA.CLOSE() |
|
この項では、DBMS_METADATA_DIFF
APIによって提供されるプロシージャおよびファンクションを簡単に説明します。これらのプロシージャの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。