2009年2月25日水曜日

[Oracle][Materialized View]元表メンテナンス時のTIPS

MLOGが付いている元表をメンテナンスする際に、2者の関係を一時的に切り離すことができる。元表に対する大量データ投入や、定義変更時に便利。
※ただし、メンテナンス完了後、Materialized Viewを完全リフレッシュする必要があるため注意。

[実装方法]
・以下のプロシージャでメンテナンス開始を宣言する。
---------------------------------------------------
DBMS_MVIEW.BEGIN_TABLE_REORGANIZATION (
tabowner IN VARCHAR2,
tabname IN VARCHAR2);
---------------------------------------------------
tabowner
再編成する表の所有者。

tabname
再編成する表の名前。


・以下のプロシージャでメンテナンス終了を宣言する。
---------------------------------------------------
DBMS_MVIEW.END_TABLE_REORGANIZATION (
tabowner IN VARCHAR2,
tabname IN VARCHAR2);
---------------------------------------------------
パラメータ 説明
tabowner
再編成する表の所有者。

tabname
再編成する表の名前。


[制限]
・メンテナンス完了後、Materialized Viewを完全リフレッシュする必要がある。
・メンテナンスを開始したセッションは、完了まで対象テーブルに対する表ロック状態を取得する。

2008年12月20日土曜日

[Oracle]Materialized ViewのORA-12899

▼問題
特定の条件を満たすと、Materialized Viewリフレッシュ時にORA-12899: 列 string の値が大きすぎます(実際: string、最大: string)が発生する。

▼対処方法
元表の項目桁数変更時はMaterialized Viewを再作成することで回避可能。

▼原因
Materialized View作成後、元表の項目桁数を拡張した際に発生する。これは、Materialized Viewの項目定義が、元表変更後の項目と一致しないために発生するようである。代表として桁数の観点で検証を実施したが、桁数以外にも当てはまると思われる。(例えば、Materialized Viewのカラム宣言でSELECT * ~を指定している場合など)

▼再現手順
--テーブル作成
create table tab(
col1 varchar2(1)
);


--データ投入
insert into tab
(col1)
values
('A')
;


--Materialized Viewを作成する
create materialized view mv_tab
refresh with rowid
as
select * from tab;


--この段階ではリフレッシュ可能である
begin dbms_mview.refresh('mv_tab'); end;
/
PL/SQLプロシージャが正常に完了しました。


--元表の定義を変更する
alter table tab modify(col1 varchar2(2));


--この時点でもリフレッシュ可能である
begin dbms_mview.refresh('mv_tab'); end;
/
PL/SQLプロシージャが正常に完了しました。


--データを変更する(1桁→2桁)
update tab
set col1 = 'AB'
;


--この時点でリフレッシュ不可能となる
begin dbms_mview.refresh('mv_tab'); end;
/

begin dbms_mview.refresh('mv_tab'); end;
*
行1でエラーが発生しました。:
ORA-12008: マテリアライズド・ビューのリフレッシュ・パスでエラーが発生しました。
ORA-12899: 列"FXUSER"."MV_TAB"."COL1"の値が大きすぎます(実際: 2、最大: 1)
ORA-06512: "SYS.DBMS_SNAPSHOT", 行2255
ORA-06512: "SYS.DBMS_SNAPSHOT", 行2461
ORA-06512: "SYS.DBMS_SNAPSHOT", 行2430
ORA-06512: 行1



[Appendix]
USER_TAB_COLUMNS表を確認したところ、Materialized Viewは実体表なので、内部的にはテーブルとして存在しているらしく、Materialized Viewと同名のオブジェクトが存在した。Materialized Viewのリフレッシュ時にはINSERTを実行しているため、トレースするまでもなくTAB→MV_TABへのデータ伝播(INSERT)の際に標記のエラーが発生したと見て間違いなさそうだ。
定義は以下の通り。

★エラーが発生しないときの定義
TABLE_NAME COLUMN_NAME DATA_LENGTH
----------- -------------- -----------
MV_TAB COL1 1
TAB COL1 1

★エラーが発生したときの定義
TABLE_NAME COLUMN_NAME DATA_LENGTH
----------- -------------- -----------
MV_TAB COL1 1
TAB COL1 2

2008年11月17日月曜日

[Oracle][SQL]テンポラリテーブルについて

リカバリ不要なワークテーブルとかに使う。

<特徴>
・REDOログを生成しないので高速にDML処理できる。
・生成したレコードは、生成したセッションからのみ参照できる。
・コミット後もしくはセッション終了後、データは削除(TRUNCATE)される。
・一時表領域を使用する。


<制限事項>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

<定義登録方法>
▼SQL文
CREATE GLOBAL TEMPORARY TABLE 表1
(
 列名 データ型...
)
ON COMMIT[DELETE ROWS | PRESERVE ROWS]
;

▼テンポラリテーブルのレコードをクリアするタイミングを設定する。
・DELETE ROWS
トランザクション終了時にデータをクリアする。

・PRESERVE ROWS
セッション終了時にデータをクリアする。
※外部アプリがDB接続をコネクションプーリングしている場合、要注意かも。


[Appendix]
・統計情報はどうなる?

2008年11月14日金曜日

[Oracle][SQL]外部結合に関する制約

外部結合演算子(+)を使用する際には、色々結合条件に制約がある。
今日は、たまたまぶちあたった問題についてのメモ。
結合条件に複数項目を指定してる場合、全て外部結合にしなくてはならない。


▼表定義
例えば、こんな表があったとする。

[表1]
CREATE TABLE EMP(
EMP_CD CHAR(8),
EMP_NAME VARCHAR2(100),
DEPT_CD CHAR(8),
DELETE_FLG CHAR(1)
);

[表2]
CREATE TABLE DEPT(
DEPT_CD CHAR(8),
DEPT_NAME VARCHAR2(100),
DELETE_FLG CHAR(1)
);


▼問題点
で、こんなSQLを実行したとする。

[問題のSQL]
1 SELECT
2 E.EMP_CD,
3 D.DEPT_NAME
4 FROM
5 EMP E,
6 DEPT D
7 WHERE E.DEPT_CD = D.DEPT_CD(+)
8 AND D.DELETE_FLG = '0'
9 ;

この場合、外部結合にしたくても、内部結合となってしまう。
EMP表のDEPT_CDが、DEPT表に存在しない場合でもEMP表のレコードは表示したい、
という意図で、7行目に「e.DEPT_CD = d.DEPT_CD(+)」外部結合として記述したが、
8行目の「d.DELETE_FLG = '0'」により外部結合の要件を満たさなくなる。


▼解決方法
で、問題のSQLをうまいこと外部結合にしたい場合はこう書く必要がある。

[解決のSQL]
1 SELECT
2 e.EMP_CD,
3 d.DEPT_NAME
4 FROM
5 EMP e,
6 (SELECT * FROM DEPT WHERE DELETE_FLG ='0') d
7 WHERE e.DEPT_CD = d.DEPT_CD(+)
8 ;

ということで、今回の様に、予め特定条件でフィルタできる場合は
FROM句にてインラインビューとして取得するのが吉。


▼APPENDIX
その他の制限については今後加筆予定。

2008年11月5日水曜日

[Oracle]Materialized View を高速に完全リフレッシュ

以下のコマンドにより、非常に高速に完全リフレッシュを行うことができる。

[コマンド]
DBMS_MVIEW.REFRESH('MVEW_NAME','c',NULL,TRUE,FALSE,1,0,0,FALSE,FALSE);

[注意]
リフレッシュ処理の間、他トランザクションから当該Materialized Viewを参照した場合、0件として表示されるため、読み取り一貫性を保障する必要がある場合注意が必要である。

[方式]
通常の完全リフレッシュでは、Materialized Viewは実体を持つ(結局はテーブルなのだけれど)ため、実体に対してDELETE→INSERT処理となる処理を、TRUNCATE→INSERT処理に変更してリフレッシュする。

なぜ、上記コマンドでDELETEからTRUNCATE処理に変更されるかについては、今後追記する。オンライン開局中よりもオフライン処理中に使うことが多いかもしれない。

2008年11月4日火曜日

[Oracle]PL/SQLにおける自律型トランザクション宣言

プロシージャ中に以下の宣言を追加することで、プロシージャを自律型トランザクションを定義することができる。プロシージャのみ独立してトランザクションを完結したい場合に使用する。逆に、自律型トランザクションでないプロシージャ内で、トランザクションを確定した場合、呼び出し元のトランザクションも確定するので注意。

[実装方法]
・自律型トランザクションプラグマを宣言する。
→PRAGMA AUTONOMOUS_TRANSACTION;

[メリット]
・呼び出し元のトランザクションに依存せずに処理を進めることができる。
→例)ログテーブルにエラーメッセージを出力する。
・ロック時間の短縮が期待できる
→プロシージャ中のDMLが対象とするオブジェクトのロック時間が短くなる。
・SELECT文中でも使用できる
→通常、SELECT文中で呼び出す関数にDMLが含まれるとエラーとなる。

[制限]
・プロシージャ中でトランザクションを確定する必要がある。
→トランザクションが確定しない場合、処理はロールバックし、呼び出し元に例外が通知される。

[サンプル]
CREATE OR REPLACE PROCEDURE PRC_LOGOUTPUT(
Pi_vMSG IN VARCHAR2,
Pi_vPG_ID IN VARCHAR2
)
IS
PRAGMA AUTONOMOUS_TRANSACTION; --プラグマ宣言
BEGIN
INSERT INTO LOGTABLES(MSG, PG_ID, CREATED_ON)
VALUES (Pi_vMSG, Pi_vPG_ID, SYSDATE);
COMMIT; --トランザクションの確定
END;
/

2008年10月13日月曜日

[Oracle]OPatchを使用した個別パッチの適用メモ

先日Oracleに個別パッチを適用したので次回の為にメモを残す。

[手順]
・「opatch lsinventory -detail」にてOracleのパッチ状況を調べる。
・Enterprise Manager Database ConsoleをSTOPする。
・LISTENERをSTOPする。
・インスタンスをSHUTDOWNする。
・OPatchを適用する。
・「opatch lsinventory -detail」にてパッチが適用されたことを確認する。
・インスタンスをSTARTUPする。
・LISTENERをSTARTする。
・Enterprise Manager Database ConsoleをSTARTする。

[備考]
・OPatchは「$ORACLE_HOME/OPatch」に存在する。※注:下記項目参照
・↑のものは古い可能性がある。OPatchは常に最新版のものを使用すること。
・個別パッチ解凍先に移動した状態でOPatchを起動する。