2025年6月18日 (水)

VECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(後編)

Previously on Mac De Oracle
前回は、VECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(前編) でした。
今日はその後編です。

10046トレースよりVECTOR INDEX(HNSW)作成では前回の通り、補助表3つ(うち2つは変更をトラックするためなので作成直後は空)、それらの表に加え、各1つの索引が作成されるということがハッキリしました。
ただ、VECTOR INDEXの補助表はvecsys.vector$indexという表にJSONとして保持されている点と、USER_INDEXESから確認できるVECTOR INDEX自体は実態を持たない、さらに、Oraclerにはお馴染みの*_INDEXESなどから単純に取得できないことも見えてきました(今後もっと便利になることを期待したいですね)

(2025/6/18追記)
これ、HNSWってインメモリーと言っても、元ネタは永続化されているわけで、それをメモリープールにポピュレートしてるって理解(間違ってないとは思うけど、違ってたらコメントもらえるとありがたいです)なので、後編ではそのあたりを見ておこうかと。。。
インメモリー近傍グラフ・ベクトル索引

(作成される補助表の名称にVECTOR INDEXのベース表の名称が入っているようなので中間一致で検索していますが、これだとノイズも多くなるので検索キツイですよね。索引名はシステム生成名称なのでベース表の名称で中間一致検索はできません!。このケースに限ればなんとか拾えてるけどという感じではありますね。。。ということで後半ではJSONから引っこ抜いてなんとかするパズルもwやってますw)

SCOTT@localhost:1521/freepdb1> select index_name , table_name, index_type from user_indexes where table_name like '%SEARCH_DATA%';

INDEX_NAME TABLE_NAME INDEX_TYPE
------------------------------ -------------------------------------------------------------------------------- ---------------------------
SYS_IL0000079525C00007$$ VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_SHARED_JOURNAL_CHANGE_LOG LOB
SYS_IL0000078074C00009$$ SEARCH_DATA LOB
SEARCH_DATA_HNSW_IX SEARCH_DATA VECTOR
SYS_C0013800 VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_ROWID_VID_MAP NORMAL
PK_XID_79519 VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS NORMAL

SCOTT@localhost:1521/freepdb1> r
1 SELECT
2 idx_name
3 ,(
4 SELECT
5 table_name
6 FROM
7 user_tables
8 WHERE
9 user_tables.table_name = (
10 SELECT
11 object_name
12 FROM
13 user_objects
14 WHERE
15 user_objects.object_id = vecsys.vector$index.idx_base_table_objn
16 )
17 ) AS tab_name
18 ,JSON_SERIALIZE(
19 idx_auxiliary_tables
20 RETURNING VARCHAR2 PRETTY
21 ) AS idx_auxiliary_tables
22 FROM
23 vecsys.vector$index
24 WHERE
25* idx_name = 'SEARCH_DATA_HNSW_IX'

IDX_NAME TAB_NAME IDX_AUXILIARY_TABLES
------------------------------ ------------------------------ ----------------------------------------------------------------------------------------------------------------------------------
SEARCH_DATA_HNSW_IX SEARCH_DATA {
"rowid_vid_map_objn" : 79520,
"shared_journal_transaction_commits_objn" : 79522,
"shared_journal_change_log_objn" : 79525,
"rowid_vid_map_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_ROWID_VID_MAP",
"shared_journal_transaction_commits_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS",
"shared_journal_change_log_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_SHARED_JOURNAL_CHANGE_LOG"
}

 

これまでの調査の結果、VECTOR INEDEX (HNSW) であるSEARCH_DATA_HNSW_IXはオブジェクトでもなく、セグメントもない(当然、シノニムにもなれない。仮にも索引ですから)
VECTOR INDEX (HNSW)の索引セグメントのサイズは、作成直後のIDX_AUXILIARY_TABLESと、索引があればそれら索引のセグメントサイズの合計ということになりますよね!!!
(やっと見えてきたw、なんなんだこれ)

 

こんな感じでJSONからIDX_AUXILIARY_TABLESにある関連表のオブジェクトID、または、テーブル名を取り出しUSER_INDEXESから索引も合わせて取得したうえで、それぞれのセグメントサイズの合計を取得すれば物理的なサイズは見えますよね。。。。(めんどくさいw)

SCOTT@localhost:1521/freepdb1> l
1 WITH
2 vector_idx_auxiliary_tables
3 AS (
4 SELECT
5 idx_name AS vector_index_name
6 , REPLACE(aux_table_name,'"','') AS aux_table_name
7 FROM
8 (
9 SELECT
10 vvi.idx_name AS idx_name
11 ,vvi.idx_auxiliary_tables.rowid_vid_map_name
12 ,vvi.idx_auxiliary_tables.shared_journal_transaction_commits_name
13 ,vvi.idx_auxiliary_tables.shared_journal_change_log_name
14 FROM
15 vecsys.vector$index vvi
16 ) objs
17 UNPIVOT (
18 aux_table_name FOR related_obj_name IN
19 (
20 rowid_vid_map_name
21 , shared_journal_transaction_commits_name
22 , shared_journal_change_log_name
23 )
24 )
25 )
26 SELECT
27 aux_table_name AS segment_name
28 FROM
29 vector_idx_auxiliary_tables
30 WHERE
31 vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
32 UNION ALL
33 SELECT
34 index_name AS segment_name
35 FROM
36 user_indexes
37 WHERE
38 EXISTS
39 (
40 SELECT
41 1
42 FROM
43 vector_idx_auxiliary_tables
44 WHERE
45 vector_idx_auxiliary_tables.aux_table_name = user_indexes.table_name
46 AND vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
47 )
48*
SCOTT@localhost:1521/freepdb1> /

SEGMENT_NAME
---------------------------------------------------------------------------------------
VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_ROWID_VID_MAP
VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS
VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_SHARED_JOURNAL_CHANGE_LOG
SYS_C0013800
PK_XID_79519
SYS_IL0000079525C00007$$

6行が選択されました。

経過: 00:00:00.16

 

では、あらためて、EXPLAIN PLAN FOR CREATE VECTOR INDEX...の見積もりサイズと、実際のサイズを比較してみましょう!

SCOTT@localhost:1521/freepdb1> l
1 explain plan for
2 CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
3 ORGANIZATION
4 INMEMORY NEIGHBOR GRAPH
5 DISTANCE COSINE
6* WITH TARGET ACCURACY 90
SCOTT@localhost:1521/freepdb1> /

解析されました。

経過: 00:00:00.02
SCOTT@localhost:1521/freepdb1> @?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------
Plan hash value: 2727344110

----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | CREATE INDEX STATEMENT | | 125K| 188M| 14689 (1)| 00:00:01 |
| 1 | VECTOR INDEX BUILD | SEARCH_DATA_HNSW_IX | | | | |
----------------------------------------------------------------------------------------------

Note
-----
- estimated index size: 293M bytes



なんどかコメントしてますが、SEARCH_DATA_HNSW_IXという索引セグメントは無く!、複数の補助表と索引群から構成されている。それらの作成直後のサイズは....

SCOTT@localhost:1521/freepdb1> r
1 WITH
2 vector_idx_auxiliary_tables
3 AS (
4 SELECT
5 idx_name AS vector_index_name
6 , REPLACE(aux_table_name,'"','') AS aux_table_name
7 FROM
8 (
9 SELECT
10 vvi.idx_name AS idx_name
11 ,vvi.idx_auxiliary_tables.rowid_vid_map_name
12 ,vvi.idx_auxiliary_tables.shared_journal_transaction_commits_name
13 ,vvi.idx_auxiliary_tables.shared_journal_change_log_name
14 FROM
15 vecsys.vector$index vvi
16 ) objs
17 UNPIVOT (
18 aux_table_name FOR related_obj_name IN
19 (
20 rowid_vid_map_name
21 , shared_journal_transaction_commits_name
22 , shared_journal_change_log_name
23 )
24 )
25 )
26 SELECT
27 user_segments.segment_name
28 , user_segments.segment_type
29 , user_segments.bytes / 1024 / 1024 AS "MB"
30 FROM
31 user_segments
32 INNER JOIN
33 (
34 SELECT
35 aux_table_name AS segment_name
36 FROM
37 vector_idx_auxiliary_tables
38 WHERE
39 vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
40 UNION ALL
41 SELECT
42 index_name AS segment_name
43 FROM
44 user_indexes
45 WHERE
46 EXISTS
47 (
48 SELECT
49 1
50 FROM
51 vector_idx_auxiliary_tables
52 WHERE
53 vector_idx_auxiliary_tables.aux_table_name = user_indexes.table_name
54 AND vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
55 )
56 ) vector_idx_aux_objects
57 ON
58 vector_idx_aux_objects.segment_name = user_segments.segment_name
59*

SEGMENT_NAME SEGMENT_TYPE MB
-------------------------------------------------------------------------------- ------------------ ----------
VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_ROWID_VID_MAP TABLE 3
SYS_C0013800 INDEX 6

 

誤差が大きいですよね。正しく見積もれてない可能性が高いように思いますが、少なく出るよりマシ程度かもしれません。今後のチューニングに期待。。。。。というところでしょうか。。。

SCOTT@localhost:1521/freepdb1> l
1 explain plan for
2 CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
3 ORGANIZATION
4 INMEMORY NEIGHBOR GRAPH
5 DISTANCE COSINE
6* WITH TARGET ACCURACY 90
SCOTT@localhost:1521/freepdb1> /

 

だと、

- estimated index size: 293M bytes
ですが、
実際には以下の通り、 9M Bytes程度でした。
(もしかして、VECTOR INDEX (HNSW)ってINMEMORYだからメモリーサイズ? んなことないか、いや、あったりしてw と思ったりし始めているw  23ai 23.6では、DBMS_VECTOR.INDEX_VECTOR_MEMORY_ADVISORプロシージャによりメモリーサイズを見積もれるようになっていたりする。。怪しい。2025/6/19追記)

SEGMENT_NAME                                                                     SEGMENT_TYPE               MB
-------------------------------------------------------------------------------- ------------------ ----------
VECTOR$SEARCH_DATA_HNSW_IX$78074_79519_0$HNSW_ROWID_VID_MAP TABLE 3
SYS_C0013800 INDEX 6

 

Bツリー索引の見積り(19cで確かめたときは、これほど大きな差にはなっていませんでした)に比べると精度は低そうですよね。と言うより、VECTOR INDEXには対応できてないのかもしれませんね....

おまけで、
DBMS_SPACEパッケージの索引サイズ見積もりも試しておきましょう。。。。。やはり、explain plan for文同様にVECTOR INDEXのセグメントサイズ見積もりには対応してなさそう。(小さくでるわけではないのですがw)

SCOTT@localhost:1521/freepdb1> set serveroutput on
SCOTT@localhost:1521/freepdb1> l
1 DECLARE
2 used_bytes NUMBER;
3 segment_bytes NUMBER;
4 BEGIN
5 DBMS_SPACE.CREATE_INDEX_COST (
6 ddl=> 'CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )'
7 || ' ORGANIZATION'
8 || ' INMEMORY NEIGHBOR GRAPH'
9 || ' DISTANCE COSINE'
10 || ' WITH TARGET ACCURACY 90'
11 , used_bytes => used_bytes
12 , alloc_bytes => segment_bytes
13 );
14 DBMS_OUTPUT.PUT_LINE('Segment Size (MB) :'||segment_bytes/1024/1024);
15 DBMS_OUTPUT.PUT_LINE('Index data Size (MB) :'||used_bytes/1024/1024);
16* END;
SCOTT@localhost:1521/freepdb1> /
Segment Size (MB) :280
Index data Size (MB) :188.94672393798828125

PL/SQLプロシージャが正常に完了しました。

 

Enjoy SQL, PL/SQL and VECTOR SEARCH!

VECTOR INDEXの謎はもう少し探る必要があるようだ...w (後編のおまけにつづくw)

めちゃ快適な気温の場所なので、
東京は猛暑と聞いて恐怖している

では、また。

 


参考)
今回利用したコードの一部

WITH
vector_idx_auxiliary_tables
AS (
SELECT
idx_name AS vector_index_name
, REPLACE(aux_table_name,'"','') AS aux_table_name
FROM
(
SELECT
vvi.idx_name AS idx_name
,vvi.idx_auxiliary_tables.rowid_vid_map_name
,vvi.idx_auxiliary_tables.shared_journal_transaction_commits_name
,vvi.idx_auxiliary_tables.shared_journal_change_log_name
FROM
vecsys.vector$index vvi
) objs
UNPIVOT (
aux_table_name FOR related_obj_name IN
(
rowid_vid_map_name
, shared_journal_transaction_commits_name
, shared_journal_change_log_name
)
)
)
SELECT
aux_table_name AS segment_name
FROM
vector_idx_auxiliary_tables
WHERE
vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
UNION ALL
SELECT
index_name AS segment_name
FROM
user_indexes
WHERE
EXISTS
(
SELECT
1
FROM
vector_idx_auxiliary_tables
WHERE
vector_idx_auxiliary_tables.aux_table_name = user_indexes.table_name
AND vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
)
;
WITH
vector_idx_auxiliary_tables
AS (
SELECT
idx_name AS vector_index_name
, REPLACE(aux_table_name,'"','') AS aux_table_name
FROM
(
SELECT
vvi.idx_name AS idx_name
,vvi.idx_auxiliary_tables.rowid_vid_map_name
,vvi.idx_auxiliary_tables.shared_journal_transaction_commits_name
,vvi.idx_auxiliary_tables.shared_journal_change_log_name
FROM
vecsys.vector$index vvi
) objs
UNPIVOT (
aux_table_name FOR related_obj_name IN
(
rowid_vid_map_name
, shared_journal_transaction_commits_name
, shared_journal_change_log_name
)
)
)
SELECT
user_segments.segment_name
, user_segments.segment_type
, user_segments.bytes / 1024 / 1024 AS "MB"
FROM
user_segments
INNER JOIN
(
SELECT
aux_table_name AS segment_name
FROM
vector_idx_auxiliary_tables
WHERE
vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
UNION ALL
SELECT
index_name AS segment_name
FROM
user_indexes
WHERE
EXISTS
(
SELECT
1
FROM
vector_idx_auxiliary_tables
WHERE
vector_idx_auxiliary_tables.aux_table_name = user_indexes.table_name
AND vector_idx_auxiliary_tables.vector_index_name = 'SEARCH_DATA_HNSW_IX'
)
) vector_idx_aux_objects
ON
vector_idx_aux_objects.segment_name = user_segments.segment_name
;
set serveroutput on
DECLARE
used_bytes NUMBER;
segment_bytes NUMBER;
BEGIN
DBMS_SPACE.CREATE_INDEX_COST (
ddl=> 'CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )'
|| ' ORGANIZATION'
|| ' INMEMORY NEIGHBOR GRAPH'
|| ' DISTANCE COSINE'
|| ' WITH TARGET ACCURACY 90'
, used_bytes => used_bytes
, alloc_bytes => segment_bytes
);
DBMS_OUTPUT.PUT_LINE('Segment Size (MB) :'||segment_bytes/1024/1024);
DBMS_OUTPUT.PUT_LINE('Index data Size (MB) :'||used_bytes/1024/1024);
END;
/

 


Oracle Database 23ai freeで試すVector Search - ONNXモデル準備編
Oracle Database 23ai freeで試すVector Search - データ準備編
実行計画は, SQL文のレントゲン写真だ! No.66 / AI Vector Search - VECTOR INDEX HNSW SCAN
VECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(前編)

 

 

| | | コメント (0)

2025年6月17日 (火)

VECTOR INDEX はどこ?、見積もりサイズだとそれなりのサイズだったのに... の謎を探るべく、我々は洞窟の奥へ向かった!(前編)

Previously on Mac De Oracle
前回は、実行計画は, SQL文のレントゲン写真だ! No.66 / AI Vector Search - VECTOR INDEX HNSW SCANでした。
今日はそれに絡んで気づいたVECTOR INDEXの謎を追いかけてみることにします。

前回の最後の宿題というか謎の一つ目、覚えてますか?  そう、VECTOR INDEXを作成後、まあよくある、USER_INDEXESを検索して作成されたVECTOR INDEXの名称を確認して、EXPLAIN PLAN FORで見積もった索引サイズとどの程度乖離しているのだろうと、手癖でUSER_SEGMENTSをアクセスしてみると、なな、なーーーーーんと、ない、無い!、なーーーい!。 (INMEMORYとはいっても元ネタがあってINMEMORY化されるわけで仕組み的に理解しにくいのと、これまでのディクショナリービューの使い方と異なっていてものすごく追いにくい)
なんで? という謎。

SCOTT@localhost:1521/freepdb1> select index_name,index_type,table_name from user_indexes where table_name='SEARCH_DATA';

INDEX_NAME INDEX_TYPE TABLE_NAME
------------------------------ --------------------------- ------------------------------
SYS_IL0000078074C00009$$ LOB SEARCH_DATA
SEARCH_DATA_HNSW_IX VECTOR SEARCH_DATA

経過: 00:00:00.05
SCOTT@localhost:1521/freepdb1> select segment_name from user_segments where segment_name = 'SEARCH_DATA_HNSW_IX';

レコードが選択されませんでした。

 

その後、アッ! 
いつもと違うビューがあったことを思い出し、覗いてみると、
作成したVECTOR INDEX (HNSW)の名称とともに、IDX_AUXILIARY_TABLES列(JSONデータ型)に、なにやらそれらしい値があるじゃありませんか!!!
そーなんだーーーーっ。VECTOR INDEXの名称は、VECTOR INDEXの補助表の集まりをまとめる為だけの存在?!!!
というところまででした。

SCOTT@localhost:1521/freepdb1> r
1* SELECT * FROM vecsys.vector$index

IDX_OBJN IDX_OBJD IDX_OWNER# IDX_NAME IDX_BASE_TABLE_OBJN IDX_BASE_TABLE_OWNER# IDX_PARAMS IDX_AUXILIARY_TABLES
---------- ---------- ---------- ------------------------------ ------------------- --------------------- ------------------------------ ------------------------------
78797 153 SEARCH_DATA_HNSW_IX 78074 153 {"type":"HNSW","num_neighbors" {"rowid_vid_map_objn":78798,"s
:32,"efConstruction":200,"dist hared_journal_transaction_comm
ance":"COSINE","accuracy":90," its_objn":78800,"shared_journa
vector_type":"FLOAT32","vector l_change_log_objn":78803,"rowi
_dimension":384,"degree_of_par d_vid_map_name":"VECTOR$SEARCH
allelism":1,"pdb_id":3,"indexe _DATA_HNSW_IX$78074_78797_0$HN
d_col":"VECTOR_DESC"} SW_ROWID_VID_MAP","shared_jour
nal_transaction_commits_name":
"VECTOR$SEARCH_DATA_HNSW_IX$78
074_78797_0$HNSW_SHARED_JOURNA
L_TRANSACTION_COMMITS","shared
_journal_change_log_name":"VEC
TOR$SEARCH_DATA_HNSW_IX$78074_
78797_0$HNSW_SHARED_JOURNAL_CH
ANGE_LOG"}

SCOTT@localhost:1521/freepdb1> r
1 SELECT
2 JSON_SERIALIZE(idx_params RETURNING VARCHAR2 PRETTY) AS "INDEX PARAM"
3 ,JSON_SERIALIZE(idx_auxiliary_tables RETURNING VARCHAR2 PRETTY) AS "INDEX AUX"
4* FROM vecsys.vector$index

INDEX PARAM INDEX AUX
-------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------
{ {
"type" : "HNSW", rowid_vid_map_objn" : 78798,
"num_neighbors" : 32, "shared_journal_transaction_commits_objn" : 78800,
"efConstruction" : 200, "shared_journal_change_log_objn" : 78803,
"distance" : "COSINE", "rowid_vid_map_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_78797_0$HNSW_ROWID_VID_MAP",
"accuracy" : 90, "shared_journal_transaction_commits_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_78797_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS",
"vector_type" : "FLOAT32", "shared_journal_change_log_name" : "VECTOR$SEARCH_DATA_HNSW_IX$78074_78797_0$HNSW_SHARED_JOURNAL_CHANGE_LOG"
"vector_dimension" : 384, }
"degree_of_parallelism" : 1,
"pdb_id" : 3,
"indexed_col" : "VECTOR_DESC"
}

 

一旦ここまでの情報を整理すると

VECTOR INDEX(この例ではHNSW)の実態は、複数の補助表から構成されており、それらをまとめているのが 作成時に指定したVECTOR INDEXの名称。
オブジェクトとしては存在しているが、それ自体は、セグメントを持たない!!!(IN MEMORY展開される索引だからとはいっても。。なにかしらあるのでは。。?)

現時点の23aiには、使い勝手の良いディクショナリービューは提供されていない。。。。まじか!

その代わりに、vecsys.vector$indexディクショナリー表(ビューではない)から詳細を追うことができる。(vecsysスキーマ)

さらに、補助表を見つけるためには、該当ディクショナリー表のIDX_AUXILIARY_TABLES列に格納されているJSONの*_objnや、補助表のオブジェクトID、*_nameから探れ!、と。
ようするに、このJSONに対象となるVECTOR INDEXとその実態の依存関係が入っている!!!!!
(ちなみに、表しかないが、おそらく、関連索引もありそうだ、というかあるだろうな。補助表の索引は補助表をキーにしてUSER_INDEXESから探すしかないだろうけども。)

ここまで見てきたところで、
めんどくせーーーーーーーーーーーーーっ! なぜ、依存関係をJSONに突っ込んだの?。。。。という顔をしているところwwwww

そしてもう一つの謎、EXPLAIN PLAN FOR CREATE VECTOR INDEX ...foo bar ..として見積もられたVECTOR INDEXの見積もりサイズって、こいつら補助表含めた合計値? 
なのか?  。。。だよなw 精度的に微妙な気がしなくも無い

全部かき集めて、合計して、見積もりサイズとの差分を見てみよう!
B*Treeの索引見積もりって、結構いい感じのサイズを弾き出してくれるわけだが。。。果たして。。。こいつは、どうなんだ? SEARCH_DATA_HNSW_IXって実態持ってないし。。。

20250613-142549

 

では、もっと探ってみなければw

 

 

そのまえに、CREATE VECTOR INDEXの謎を探るべくw
10046トレースで洞窟の奥に潜入することにします! ww (わからなくなったら、これしかない!)

SCOTT@localhost:1521/freepdb1> desc search_data
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NUMBER
PRIMARY_DESCRIPTION VARCHAR2(40)
DESCRIPTION VARCHAR2(100)
LOCATION_DESC VARCHAR2(100)
DISTRICT VARCHAR2(30)
WARD NUMBER
COMMUNITY VARCHAR2(30)
C_YEAR NUMBER
VECTOR_DESC VECTOR(*, *)

SCOTT@localhost:1521/freepdb1> select count(*) from search_data

COUNT(*)
----------
125000

 

作成済みVECTOR INDEX (HNSW)を削除しておきます。

SCOTT@localhost:1521/freepdb1> drop index search_data_hnsw_ix;

索引が削除されました。

経過: 00:00:01.77

SCOTT@localhost:1521/freepdb1> select index_name , table_name, index_type from user_indexes where table_name like '%SEARCH_DATA%';

INDEX_NAME TABLE_NAME INDEX_TYPE
------------------------------ ------------------------------ ---------------------------
SYS_IL0000078074C00009$$ SEARCH_DATA LOB

SCOTT@localhost:1521/freepdb1> select table_name,column_name,segment_name,index_name from user_lobs where table_name like '%SEARCH_DATA%';

TABLE_NAME COLUMN_NAME SEGMENT_NAME INDEX_NAME
------------------------------ ------------------------------ ------------------------------ ------------------------------
SEARCH_DATA VECTOR_DESC SYS_LOB0000078074C00009$$ SYS_IL0000078074C00009$$

経過: 00:00:00.07

 

10046トレースの準備ができたので、トレースを設定しCREATE VECTOR INDEX文を実行して洞窟の奥へw

SCOTT@localhost:1521/freepdb1> show parameter timed_statistics

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
timed_statistics boolean TRUE

SCOTT@localhost:1521/freepdb1> show parameter max_dump_file_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
max_dump_file_size string 32M
SCOTT@localhost:1521/freepdb1> alter session set max_dump_file_size = unlimited;

セッションが変更されました。

経過: 00:00:00.00
SCOTT@localhost:1521/freepdb1> show parameter tracefile_identifier

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
tracefile_identifier string
SCOTT@localhost:1521/freepdb1> alter session set tracefile_identifier = 'create_vector_index';

セッションが変更されました。

経過: 00:00:00.00
SCOTT@localhost:1521/freepdb1> alter session set events '10046 trace name context forever, level 12';

セッションが変更されました。

経過: 00:00:00.02
SCOTT@localhost:1521/freepdb1> l
1 CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
2 ORGANIZATION
3 INMEMORY NEIGHBOR GRAPH
4 DISTANCE COSINE
5* WITH TARGET ACCURACY 90
SCOTT@localhost:1521/freepdb1> /

索引が作成されました。

経過: 00:00:39.24
SCOTT@localhost:1521/freepdb1> alter session set events '10046 trace name context off';

セッションが変更されました。

経過: 00:00:00.01

...略...

[oracle@localhost ~]$ echo $ORACLE_BASE
/opt/oracle
[oracle@localhost ~]$ cd $ORACLE_BASE/diag/rdbms/free/FREE/trace
[oracle@localhost trace]$ pwd
/opt/oracle/diag/rdbms/free/FREE/trace
[oracle@localhost trace]$
[oracle@localhost trace]$ ll *create_vector_index*
-rw-r-----. 1 oracle oinstall 2836064 6月 12 00:24 FREE_ora_4133_create_vector_index.trc
-rw-r-----. 1 oracle oinstall 602896 6月 12 00:24 FREE_ora_4133_create_vector_index.trm
[oracle@localhost trace]$
[oracle@localhost trace]$ tkprof FREE_ora_4133_create_vector_index.trc FREE_ora_4133_create_vector_index.txt waits=yes explain=scott@localhost:1521/freepdb1 sys=yes

...略...

[oracle@localhost trace]$ ll *create_vector_index*
-rw-r-----. 1 oracle oinstall 2836064 6月 12 00:24 FREE_ora_4133_create_vector_index.trc
-rw-r-----. 1 oracle oinstall 602896 6月 12 00:24 FREE_ora_4133_create_vector_index.trm
-rw-rw-r--. 1 oracle oracle 500986 6月 12 00:30 FREE_ora_4133_create_vector_index.txt
[oracle@localhost trace]$
[oracle@localhost trace]$ view FREE_ora_4133_create_vector_index.txt

 

ということで、整形したトレースファイルからCREATE文を抜き出してみた。
これは私が実行したDDL文なので参考程度に...

CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
ORGANIZATION
INMEMORY NEIGHBOR GRAPH
DISTANCE COSINE
WITH TARGET ACCURACY 90

 

以降、トレースから抜き出した補助表及関連する索引を作成するDDL
VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_ROWID_VID_MAP表、および、主キー索引の作成(基本的にこの補助表が主役)

CREATE TABLE SCOTT.VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_ROWID_VID_MAP 
(base_table_rowid ROWID PRIMARY KEY, vertex_id NUMBER)

CREATE UNIQUE INDEX "SCOTT"."SYS_C0013760" on
"SCOTT"."VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_ROWID_VID_MAP"("BASE_TABLE_ROWID")
NOPARALLEL



VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS表、および、主キー索引の作成

CREATE TABLE SCOTT.VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS 
(usn NUMBER NOT NULL, slot NUMBER NOT NULL, seq
NUMBER NOT NULL, commit_scn NUMBER NOT NULL,
CONSTRAINT pk_xid_79461 PRIMARY KEY(usn, slot, seq))
PARTITION BY RANGE(commit_scn)
INTERVAL(100)
(PARTITION pdefault VALUES LESS THAN (0))

CREATE UNIQUE INDEX "SCOTT"."PK_XID_79461" on
"SCOTT"."VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS"("USN","SLOT","SEQ")
NOPARALLEL


VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_CHANGE_LOG表の作成(HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS表の参照パーティションになっている)

CREATE TABLE SCOTT.VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_CHANGE_LOG 
(usn NUMBER NOT NULL, slot NUMBER NOT NULL, seq NUMBER NOT
NULL, xcn NUMBER, base_table_rowid ROWID, dml_op VARCHAR2(10),
data_vector vector(384, FLOAT32),
CONSTRAINT fk_xid_79461 FOREIGN KEY(usn, slot, seq)
REFERENCES
SCOTT.VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS(usn, slot, seq))
PARTITION BY REFERENCE(fk_xid_79461)

各表をdescribeしてみると...

VECTOR列を持つ表のROWIDとVERTEX_IDをマップしていますね、表を見てなんとなく想像できる列名で素敵w。RDBMSのモデリングで列からなにやってるか想像もできねー設計しているのを見たりしてると心が清くなった気がしますね。
これがVECTOR INDEXの主役といってもよいでしょうね。

SCOTT@localhost:1521/freepdb1> desc VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_ROWID_VID_MAP
名前 NULL? 型
----------------------------------------- -------- ----------------------------
BASE_TABLE_ROWID NOT NULL ROWID
VERTEX_ID NUMBER

 

次の2つの表は、元の表のVECTORに影響のある更新がトラックされているみたい。詳しい資料読んではいないのですが、変更をトラックしていく表っぽうので、変更のない状況では空なんじゃないだろうか。。。あとで確認します。

SCOTT@localhost:1521/freepdb1> desc VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS
名前 NULL? 型
----------------------------------------- -------- ----------------------------
USN NOT NULL NUMBER
SLOT NOT NULL NUMBER
SEQ NOT NULL NUMBER
COMMIT_SCN NOT NULL NUMBER

SCOTT@localhost:1521/freepdb1> desc VECTOR$SEARCH_DATA_HNSW_IX$78074_79461_0$HNSW_SHARED_JOURNAL_CHANGE_LOG
名前 NULL? 型
----------------------------------------- -------- ----------------------------
USN NOT NULL NUMBER
SLOT NOT NULL NUMBER
SEQ NOT NULL NUMBER
XCN NUMBER
BASE_TABLE_ROWID ROWID
DML_OP VARCHAR2(10)
DATA_VECTOR VECTOR(384, FLOAT32)

 

一応user_indexesビューから全体を見てみます。USER_INDEXESビューを通して見ると新たに7つのオブジェクトが作成されたことがわかります!!!!(赤字部分)
vecsys.vector$index表からは、補助表の存在しか見えませんでしたが、索引も作成されてます。実際にはこの表と索引のセグメントサイズの合計が、VECTOR INDEX作成時のセグメントサイズってことですよね?!(INMEMORYだからメモリーサイズって可能性もなくはないけど、どっちかわからんw) とはいえ、CREATE VECTOR INDEX ほげほげ索引で、指定したほげほげ索引自体はセグメントは持たない!(INMEMORYテーブルとは異なる持ち方をしているだけなのかもしれないが、わかりにくいw)


SCOTT@localhost:1521/freepdb1> select index_name , table_name from user_indexes where table_name like '%SEARCH_DATA%';

INDEX_NAME TABLE_NAME
------------------------------ ----------------------------------------------------------------------------------------------------
SYS_IL0000078074C00009$$ SEARCH_DATA
SEARCH_DATA_HNSW_IX SEARCH_DATA
SYS_C0013720 VECTOR$SEARCH_DATA_HNSW_IX$78074_79421_0$HNSW_ROWID_VID_MAP
PK_XID_79421 VECTOR$SEARCH_DATA_HNSW_IX$78074_79421_0$HNSW_SHARED_JOURNAL_TRANSACTION_COMMITS
SYS_IL0000079427C00007$$ VECTOR$SEARCH_DATA_HNSW_IX$78074_79421_0$HNSW_SHARED_JOURNAL_CHANGE_LOG

経過: 00:00:00.29

表名を中間一致検索すると関連する索引と表をリストすることができますが、これだとなんかイケてない感じが強いのでvecsys.vector$index表より扱いやすいビューを提供してもらいたいですね! まじで (苦笑

長くなってしまったので、一旦、謎の1つをまとめておきたいと思います。
VECTOR INDEX (HNSW)のセグメントサイズとEXPLAIN PLAN FOR CREATE VECTOR INDEX...の見積もりサイズの差分確認は次回のおたのしみということで。

CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
ORGANIZATION
INMEMORY NEIGHBOR GRAPH
DISTANCE COSINE
WITH TARGET ACCURACY 90

 

作成されるベクター索引(今回の例では、search_data_hnsw_ix)は *_INDEXESにはVECTOR索引としてリストされるが実体は持たない(INMEMORYとはいっても何がどうINMEMORY化されるかディクショナリーから追いやすかったテーブルのINMEMORY化とは傾向が違いそう)。
補助表とその索引を含めたオブジェクト群をアクセスするためのキーのような存在。
ベクター索引(今回のHNSW索引の例では、マップ表とベクターの更新をトラックするパーティション表と参照パーティション表からなる3表と付随する3つの索引から構成されている。 ベクター索引の主役は、ベクター列をもつベース表のROWIDをVERTEX_IDをマップするマップ表とその索引。変更がない状態であれば、おそらくこの2つのオブジェクトがセベクター索引のセグメントサイズの総量になりそう。

また、
これらオブジェクト(索引も含め)を簡単に一覧するビューは無く、中間一致検索で無理やり検索するか、vecsys.vector$index表のJSONデータを使って他のビューと結合して取得する必要がありそう(めんどくせぇw)

ということまでは見えた。(以下は、洞窟の奥へ潜入したときの手書きメモww)

もうひとつ、前述の通り、search_data_hnsw_ixというベクター索引の便宜上のオブジェクトは同一名のセグメントは持っていません。しかし、以下のようなSELECT文では、該当ベクター索引が利用されていることを示すオブジェクトとして現れてくるというのも特徴的ですね。
SQL文と実行計画によってはMAP表が現われてくることもあるようなので、その例についてもいずれ書こうと思っています:) (それはそれで謎いですよね。突然裏の主役が実行計画に顔を出してくるわけなのでw)

SCOTT@localhost:1521/freepdb1> r
1 SELECT
2 id
3 , description
4 , community
5 , location_desc
6 , district
7 , TO_NUMBER( v_distance ) AS v_distance
8 FROM
9 (
10 SELECT
11 id
12 , description
13 , community
14 , location_desc
15 , district
16 , VECTOR_DISTANCE
17 (
18 vector_desc
19 , VECTOR_EMBEDDING
20 (
21 all_minilm_l6 USING 'Incident in which someone may have been murdered' AS data
22 )
23 , COSINE
24 ) v_distance
25 FROM
26 search_data
27 ORDER BY
28 v_distance
29 FETCH APPROX FIRST 10 ROWS ONLY
30* )


10行が選択されました。

経過: 00:00:00.32

実行計画
----------------------------------------------------------
Plan hash value: 2333665681

-------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 1570 | 2 (50)| 00:00:01 |
| 1 | VIEW | | 10 | 1570 | 2 (50)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | VIEW | | 10 | 1570 | 2 (50)| 00:00:01 |
|* 4 | SORT ORDER BY STOPKEY | | 10 | 16400 | 2 (50)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| SEARCH_DATA | 10 | 16400 | 1 (0)| 00:00:01 |
| 6 | VECTOR INDEX HNSW SCAN | SEARCH_DATA_HNSW_IX | 10 | 16400 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

 

なお、今回利用している Oracle Database 23.4は最近の更新ペースからするとすでに古いリリースになっていますw 
VECTOR関連追加など含め結構な差分がりそうですね。例えば、DBMS_VECTOR.INDEX_VECTOR_MEMORY_ADVISOR()プロシージャが無いなど。(マニュアルには記載されているんですが、リリースいくつから有効というような表記は見当たらず)
また、VirtualBox Applienceだと、現時点では、23.7 が最新みたいなので入れ替えないとな。。。

 

23って、機能的にいつ頃落ち着くんだろうなぁ〜っ。

次回へ、つづく!

Enjoy SQL! and JSON?!

 

 


Oracle Database 23ai freeで試すVector Search - ONNXモデル準備編
Oracle Database 23ai freeで試すVector Search - データ準備編
実行計画は, SQL文のレントゲン写真だ! No.66 / AI Vector Search - VECTOR INDEX HNSW SCAN

 

| | | コメント (0)

2025年6月 4日 (水)

Oracle Database 23ai freeで試すVector Search - データ準備編

Previously on Mac De Oracle
前回は、 ONNXモデル準備編でした。意外にすんなりできちゃいましたね。
で、今回は、Vector Searchのレントゲンという名の実行計画を取得するデータの準備編です。

ただし、Oracle Database 23ai Freeを利用するため以下のリソース制限があります。
その制限の中で行える量に調整しつつ準備します。

Oracle Database 23ai Free limitations

https://www.oracle.com/database/free/

Resource limit up to
・2 CPUs (for foreground processes)
・2 GB of RAM
・12 GB of user data on disk (included SYSAUX)


VirtualBOXの対象VMストレージサイズは十分なサイズにしてあります。(Oracle Database 23ai Freeの制限以上は確保してあります。Linux上での作業領域含む)

% VBoxManage showvminfo 25951093-0df5-47e8-823c-ac7fdf2902bf | grep Memory
Memory size: 4096MB

 

VM上からみたストレージ空き容量とCPU数

[oracle@localhost work4vector]$ sudo df -aH | grep '/sd.'
[sudo] oracle のパスワード:
/dev/sda3 58G 20G 38G 35% /
/dev/sda1 1.1G 346M 719M 33% /boot

[oracle@localhost work4vector]$ grep cpu.cores /proc/cpuinfo | sort -u
cpu cores : 2

 

Oracle Database 23aiのデータサイズは以下の通り。制限まで十分に余裕はあります

SYS@localhost:1521/freepdb1> r
1* select file_name,tablespace_name,bytes/1024/1024/1024 "GB",autoextensible from dba_data_files

FILE_NAME TABLESPACE_NAME GB AUT
------------------------------------------------------ ------------------------------ ---------- ---
/opt/oracle/oradata/FREE/FREEPDB1/sysaux01.dbf SYSAUX .76171875 YES
/opt/oracle/oradata/FREE/FREEPDB1/users01.dbf USERS .463867188 YES
/opt/oracle/oradata/FREE/FREEPDB1/system01.dbf SYSTEM .419921875 YES
/opt/oracle/oradata/FREE/FREEPDB1/undotbs01.dbf UNDOTBS1 .1953125 YES

 

Vector Search向けサンプルデータはGetting Started with Oracle Database 23ai AI Vector Searchでも紹介されているChicago Data Portalのオープンデータを利用します。結構いいサイズのデータが公開されています。
とは言ってもVector Indexも含めたサイズを考えると、Oracle Database 23ai Freeに全データを取り込むのは現実的なく一部を取り込んで使うことにします。

一部を使うとはいっても結構なサイズです。データ自体欠損していたりするのもあるようなのでそれなりに工夫して使いまっすw
ETL屋さんの腕の見せ所ですねww

スクリーンショット1
20250603-61755

 

スクリーンショット2
20250603-61807

 

Chcago Data Portalから取得したオープンデータのCSV形式ファイルは以下のとおり。

シカゴの犯罪データで2001年から直近のデータまで公開されています。 データセットには座標を含むGEOデータも含まれていますが今回は利用しません。このデータセットだけで1.9GB程度ですが、最終的に取り込むのは1年分より少ない程度にします。
chicago_crimes_2001_20250508.csv

属性やコード値の意味および、マスタデータとなるデータセットは何かという情報は、Chicago Data Portalで該当するデータセットを開くと表示される列ヘッダーのヘルプポップアップダイアログに記載されています。
(前述、スクリーンショット2参照のこと)
以下のマスターデータセットは、chicago_crimes_2001_20250508.csv と結合してコード値の読み替えなどに利用します。

Community Areaとして定義されているエリア名称などが含まれます(最新のもの) Boundaries_-_Community_Areas_20250508.csv

Districtの地区名称などが定義されています(最新のもの) PoliceDistrictDec2012_20250508.csv

シカゴ警察が利用しているイリノイ州の標準犯罪レポートIUCRコードマスター(最新のもの) Chicago_Police_Department_-_Illinois_Uniform_Crime_Reporting__IUCR__Codes_20250512.csv

使うかわからないですが、犯罪発生データに含まれているWARDの情報のマスターデータセットです(最新のものですが、結局使わなかったw) WARDS_2015_20250508.csv

 

上記データは、以下に作成したディレクトリオブジェクトに対応するディレクトリに配置して外部表のソースとなります。

SYS@localhost:1521/freepdb1> create directory chicago_crimes_data_dir as '/home/oracle/work4vector';

ディレクトリが作成されました。

経過: 00:00:00.46
SYS@localhost:1521/freepdb1> r
1* select directory_name, directory_path from dba_directories where directory_name = upper('chicago_crimes_data_dir')

DIRECTORY_NAME DIRECTORY_PATH
------------------------------ ------------------------------
CHICAGO_CRIMES_DATA_DIR /home/oracle/work4vector

経過: 00:00:00.01

SYS@localhost:1521/freepdb1> grant read on directory chicago_crimes_data_dir to scott;
権限付与が成功しました。

経過: 00:00:00.04

SCOTT@localhost:1521/freepdb1> !ls -l
合計 1917360
-rw-r--r--. 1 oracle oracle 3566 5月 9 09:46 Boundaries_-_Community_Areas_20250508.csv
-rw-r--r--. 1 oracle oracle 22307 5月 12 23:33 Chicago_Police_Department_-_Illinois_Uniform_Crime_Reporting__IUCR__Codes_20250512.csv
-rw-r--r--. 1 oracle oracle 202 5月 9 09:47 PoliceDistrictDec2012_20250508.csv
-rw-r--r--. 1 oracle oracle 1558 5月 9 09:48 WARDS_2015_20250508.csv
-rw-r--r--. 1 oracle oracle 1947211979 5月 9 03:40 chicago_crimes_2001_20250508.csv

 

Vector Searchで利用する表は以下、それ以外はこの表のデータを作りだすための元となるデータで外部票として取り込みVector Searchで利用する表データに変換します。
(DROP TABLE IF EXISTS構文は、Oracle Database 23ai 以降で使えます)

 DROP TABLE IF EXISTS search_data PURGE;
CREATE TABLE search_data
(
id NUMBER
, primary_description VARCHAR2(40)
, description VARCHAR2(100)
, location_desc VARCHAR2(100)
, district VARCHAR2(30)
, ward NUMBER
, community VARCHAR2(30)
, c_year NUMBER
, vector_desc VECTOR
)
/

上記表のインプットとなる各外部票は以下の通り。

シカゴの犯罪データのオリジナルです。search_data_org(外部表)

列コメントにある数字と列名は、search_dataの列順と列名に対応しています。(ただし、そのままセットするものもあれば適宜型変換やマスターデータと結合して読み替えたりします。元になる列を示しています。
chicago_crimes_2001_20250508_2.csvというファイルがありますが、chicago_crimes_2001_20250508.csvのうちBad dataとして弾かれたデータが出力される.badファイルを元にデータが通るように整形したものを再度インプットとして利用したものです。

CREATE TABLE search_data_org
(
id VARCHAR2(100) -- 1 id
,case_number VARCHAR2(100)
,case_Date VARCHAR2(100)
,block VARCHAR2(100)
,iucr VARCHAR2(100) -- 2 primary_description
,primary_type VARCHAR2(100)
,description VARCHAR2(100) -- 3 description
,Location_desc VARCHAR2(100) -- 4 location_desc
,arrest VARCHAR2(100)
,domestic VARCHAR2(100)
,beat VARCHAR2(100)
,district VARCHAR2(100) -- 5 district
,ward VARCHAR2(100) -- 6 ward
,community VARCHAR2(100) -- 7 community
,fbi_code VARCHAR2(100)
,x_coordinate VARCHAR2(100)
,y_coordinate VARCHAR2(100)
,c_year VARCHAR2(100) -- 8 c_year
,updated_on VARCHAR2(100)
,latitude VARCHAR2(100)
,longitude VARCHAR2(100)
,location VARCHAR2(100)
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY chicago_crimes_data_dir
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
MISSING FIELD VALUES ARE NULL
(
id
,case_number
,case_Date
,block
,iucr
,primary_type
,description
,Location_desc
,arrest
,domestic
,beat
,district
,ward
,community
,fbi_code
,x_coordinate
,y_coordinate
,c_year
,updated_on
,latitude
,longitude
,location1
,location2
)
COLUMN TRANSFORMS
(
location FROM CONCAT
(
location1
,location2
)
)
)
LOCATION (
'chicago_crimes_2001_20250508.csv'
,'chicago_crimes_2001_20250508_2.csv'
)
)
REJECT LIMIT UNLIMITED
/

 

以降、マスターデータセットの外部表定義です。

communitiesマスタ(外部表)
AREA_NUMBER列がキーです。search_data_org外部表(シカゴの犯罪データ)のcommunity列と結合することで (列名違いすぎるwww、実案件だったらキレてるな、俺w)、COMMUNITY_NAMEに読み替えることができる、と。。。
とは言え、ちょいと怪しいので外部結合するようにしとくかw

CREATE TABLE communities 
(
AREA_NUMBER NUMBER
, COMMUNITY_NAME VARCHAR2(100)
, AREA_NUM_1 NUMBER
, SHAPE_AREA NUMBER
, SHAPE_LEN NUMBER
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY chicago_crimes_data_dir
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
SKIP 1
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
MISSING FIELD VALUES ARE NULL
(
AREA_NUMBER
, COMMUNITY_NAME
, AREA_NUM_1
, SHAPE_AREA
, SHAPE_LEN
)
)
LOCATION (
'Boundaries_-_Community_Areas_20250508.csv'
)
)REJECT LIMIT UNLIMITED/

 

districtsマスタ(外部表)
DIST_NUMBER列がキーで、search_data_org外部表(シカゴの犯罪データ)のdistrict列と結合することで、DIST_LABELに読み替えることができます。(これも列名が異なっていて非常にわかりにくいwwww なんでこんな状態なのみたいなw)
これも外部結合の方が良さそうな雰囲気はありますね。実際詳しく追ったわけではないですがw

CREATE TABLE districts
(
DIST_LABEL VARCHAR2(100)
, DIST_NUMBER NUMBER
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY chicago_crimes_data_dir
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
SKIP 1
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
MISSING FIELD VALUES ARE NULL
(
DIST_LABEL
, DIST_NUMBER
)
)
LOCATION (
'PoliceDistrictDec2012_20250508.csv'
)
)
REJECT LIMIT UNLIMITED
/

 

illinoi_uniform_crime_reporting_codesマスタ(外部表)
iucr列とsearch_data_org外部表(シカゴの犯罪データ)のiucr列を結合することでprimary_descriptionに読み替えることができます。結合キー列名が同じなのはよろしいのですが、ここまでバラバラなのにここだけ同じなのかww (これもなんとなく怪しいので、外部結合にしようと思います)

CREATE TABLE illinoi_uniform_crime_reporting_codes
(
iucr VARCHAR2(5)
, primary_description VARCHAR2(100)
, secondary_description VARCHAR2(100)
, index_code CHAR(1)
, active BOOLEAN NOT NULL
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY chicago_crimes_data_dir
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
MISSING FIELD VALUES ARE NULL
(
iucr
,primary_description
,secondary_description
,index_code
,active
)
)
LOCATION (
'Chicago_Police_Department_-_Illinois_Uniform_Crime_Reporting__IUCR__Codes_20250512.csv'
)
)
REJECT LIMIT UNLIMITED
/

 

最後にsearch_dataにVECTORを埋め込んだデータを登録するためのIASです。VECTOR_EMBEDDING関数でsearch_data_org.descriptionを元にVECTORデータを生成しています。
全データ(830万件)のうち、2024年に発生したデータのID順で、125,000行だけ登録します。(理由は、Oracle Database 23ia Freeのデータ量というより2GBまでしか使えないというメモリサイズの制限が重しになっているためです。後述)

TRUNCATE TABLE search_data;
INSERT INTO search_data
(
id
, primary_description
, description
, location_desc
, district
, ward
, community
, c_year
, vector_desc
)
SELECT
sdo.id AS id
, iucrc.primary_description AS primary_description
, sdo.description AS description
, sdo.Location_desc AS Location_desc
, d.dist_label AS district
, TO_NUMBER(sdo.ward) AS ward
, c.community_name AS community
, TO_NUMBER(sdo.c_year) AS c_year
, VECTOR_EMBEDDING(all_minilm_l6 USING sdo.description AS data) AS vector_desc
FROM
search_data_org sdo
LEFT OUTER JOIN districts d
ON
TO_NUMBER(sdo.district) = d.dist_number
LEFT OUTER JOIN communities c
ON
TO_NUMBER(sdo.community) = c.area_number
LEFT OUTER JOIN illinoi_uniform_crime_reporting_codes iucrc
ON
sdo.iucr = iucrc.iucr
WHERE
TO_NUMBER(sdo.c_year) IN 2024
ORDER BY
id
, c_year
FETCH FIRST 125000 ROWS ONLY
/

 

では最初に、Vector Searchで利用する表だけ作成しておきます。データは前述のIASでガツンと登録しちゃいます。

SCOTT@localhost:1521/freepdb1> l
1 CREATE TABLE search_data
2 (
3 id NUMBER
4 , primary_description VARCHAR2(40)
5 , description VARCHAR2(100)
6 , location_desc VARCHAR2(100)
7 , district VARCHAR2(30)
8 , ward NUMBER
9 , community VARCHAR2(30)
10 , c_year NUMBER
11 , vector_desc VECTOR
12* )
SCOTT@localhost:1521/freepdb1> /

表が作成されました。

経過: 00:00:00.03

 

もろもろエラーデータを補正しつつw なんとか search_data_org 外部表の作成に成功! (エラーになっていたログはバッサリ削除しましたが。。。)

SCOTT@localhost:1521/freepdb1> l
1 CREATE TABLE search_data_org
2 (
3 id VARCHAR2(100) -- 1
4 ,case_number VARCHAR2(100)
5 ,case_Date VARCHAR2(100)
6 ,block VARCHAR2(100)
7 ,iucr VARCHAR2(100) -- 2
8 ,primary_type VARCHAR2(100)
9 ,description VARCHAR2(100) -- 3
10 ,Location_desc VARCHAR2(100) -- 4
11 ,arrest VARCHAR2(100)
12 ,domestic VARCHAR2(100)
13 ,beat VARCHAR2(100)
14 ,district VARCHAR2(100) -- 5 nullあり
15 ,ward VARCHAR2(100) -- 6 nullあり
16 ,community VARCHAR2(100) -- 7 数値データのみ、nullあり
17 ,fbi_code VARCHAR2(100)
18 ,x_coordinate VARCHAR2(100)
19 ,y_coordinate VARCHAR2(100)
20 ,c_year VARCHAR2(100) -- 8 数値データのみ
21 ,updated_on VARCHAR2(100)
22 ,latitude VARCHAR2(100)
23 ,longitude VARCHAR2(100)
24 ,location VARCHAR2(100)
25 )
26 ORGANIZATION EXTERNAL (
27 TYPE ORACLE_LOADER
28 DEFAULT DIRECTORY chicago_crimes_data_dir
29 ACCESS PARAMETERS (
30 RECORDS DELIMITED BY NEWLINE
31 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
32 MISSING FIELD VALUES ARE NULL
33 (
34 id
35 ,case_number
36 ,case_Date
37 ,block
38 ,iucr
39 ,primary_type
40 ,description
41 ,Location_desc
42 ,arrest
43 ,domestic
44 ,beat
45 ,district
46 ,ward
47 ,community
48 ,fbi_code
49 ,x_coordinate
50 ,y_coordinate
51 ,c_year
52 ,updated_on
53 ,latitude
54 ,longitude
55 ,location1
56 ,location2
57 )
58 COLUMN TRANSFORMS
59 (
60 location FROM CONCAT
61 (
62 location1
63 ,location2
64 )
65 )
66 )
67 LOCATION (
68 'chicago_crimes_2001_20250508.csv'
69 ,'chicago_crimes_2001_20250508_2.csv'
70 )
71 )
72* REJECT LIMIT UNLIMITED
SCOTT@localhost:1521/freepdb1> /

表が作成されました。

経過: 00:00:00.02
SCOTT@localhost:1521/freepdb1> select count(1) from search_data_org;

COUNT(1)
----------
8307418

経過: 00:00:36.80
SCOTT@localhost:1521/freepdb1> ! ls -l *.bad
ls: '*.bad' にアクセスできません: No such file or directory

 

communitiesマスタ(外部表)の作成

SCOTT@localhost:1521/freepdb1> l
1 CREATE TABLE communities
2 (
3 AREA_NUMBER NUMBER
4 , COMMUNITY_NAME VARCHAR2(100)
5 , AREA_NUM_1 NUMBER
6 , SHAPE_AREA NUMBER
7 , SHAPE_LEN NUMBER
8 )
9 ORGANIZATION EXTERNAL (
10 TYPE ORACLE_LOADER
11 DEFAULT DIRECTORY chicago_crimes_data_dir
12 ACCESS PARAMETERS (
13 RECORDS DELIMITED BY NEWLINE
14 SKIP 1
15 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
16 MISSING FIELD VALUES ARE NULL
17 (
18 AREA_NUMBER
19 , COMMUNITY_NAME
20 , AREA_NUM_1
21 , SHAPE_AREA
22 , SHAPE_LEN
23 )
24 )
25 LOCATION (
26 'Boundaries_-_Community_Areas_20250508.csv'
27 )
28 )
29* REJECT LIMIT UNLIMITED
SCOTT@localhost:1521/freepdb1> /

表が作成されました。

経過: 00:00:00.03

SCOTT@localhost:1521/freepdb1> select count(1) from communities;

COUNT(1)
----------
77

経過: 00:00:00.03
SCOTT@localhost:1521/freepdb1> ! ls -l *.bad
ls: '*.bad' にアクセスできません: No such file or directory

 

 

districtsマスタ(外部表)の作成

SCOTT@localhost:1521/freepdb1> l
1 CREATE TABLE districts
2 (
3 DIST_LABEL VARCHAR2(100)
4 , DIST_NUMBER NUMBER
5 )
6 ORGANIZATION EXTERNAL (
7 TYPE ORACLE_LOADER
8 DEFAULT DIRECTORY chicago_crimes_data_dir
9 ACCESS PARAMETERS (
10 RECORDS DELIMITED BY NEWLINE
11 SKIP 1
12 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
13 MISSING FIELD VALUES ARE NULL
14 (
15 DIST_LABEL
16 , DIST_NUMBER
17 )
18 )
19 LOCATION (
20 'PoliceDistrictDec2012_20250508.csv'
21 )
22 )
23* REJECT LIMIT UNLIMITED
SCOTT@localhost:1521/freepdb1> /

表が作成されました。

経過: 00:00:00.02

SCOTT@localhost:1521/freepdb1> ! ls -l *.bad
ls: '*.bad' にアクセスできません: No such file or directory

SCOTT@localhost:1521/freepdb1> select count(1) from districts;

COUNT(1)
----------
25

経過: 00:00:00.04

 

illinoi_uniform_crime_reporting_codesマスタ(外部表)の作成

SCOTT@localhost:1521/freepdb1> l
1 CREATE TABLE illinoi_uniform_crime_reporting_codes
2 (
3 iucr VARCHAR2(5)
4 , primary_description VARCHAR2(100)
5 , secondary_description VARCHAR2(100)
6 , index_code CHAR(1)
7 , active BOOLEAN NOT NULL
8 )
9 ORGANIZATION EXTERNAL (
10 TYPE ORACLE_LOADER
11 DEFAULT DIRECTORY chicago_crimes_data_dir
12 ACCESS PARAMETERS (
13 RECORDS DELIMITED BY NEWLINE
14 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
15 MISSING FIELD VALUES ARE NULL
16 (
17 iucr
18 ,primary_description
19 ,secondary_description
20 ,index_code
21 ,active
22 )
23 )
24 LOCATION (
25 'Chicago_Police_Department_-_Illinois_Uniform_Crime_Reporting__IUCR__Codes_20250512.csv'
26 )
27 )
28* REJECT LIMIT UNLIMITED
SCOTT@localhost:1521/freepdb1> /

表が作成されました。

経過: 00:00:00.02

SCOTT@localhost:1521/freepdb1> ! ls -l *.bad
ls: '*.bad' にアクセスできません: No such file or directory

SCOTT@localhost:1521/freepdb1> select count(1) from illinoi_uniform_crime_reporting_codes;

COUNT(1)
----------
410

経過: 00:00:00.02

 

 

では、最後の仕上げ。VECTOR INDEX以外はこれで完成です!!!, 0.25GB程度の消費ですね。表だけで。VECTOR INDEXは別途作成します。

SCOTT@localhost:1521/freepdb1> l
1 INSERT INTO search_data
2 (
3 id
4 , primary_description
5 , description
6 , location_desc
7 , district
8 , ward
9 , community
10 , c_year
11 , vector_desc
12 )
13 SELECT
14 sdo.id AS id
15 , iucrc.primary_description AS primary_description
16 , sdo.description AS description
17 , sdo.Location_desc AS Location_desc
18 , d.dist_label AS district
19 , TO_NUMBER(sdo.ward) AS ward
20 , c.community_name AS community
21 , TO_NUMBER(sdo.c_year) AS c_year
22 , VECTOR_EMBEDDING(all_minilm_l6 USING sdo.description AS data) AS vector_desc
23 FROM
24 search_data_org sdo
25 LEFT OUTER JOIN districts d
26 ON
27 TO_NUMBER(sdo.district) = d.dist_number
28 LEFT OUTER JOIN communities c
29 ON
30 TO_NUMBER(sdo.community) = c.area_number
31 LEFT OUTER JOIN illinoi_uniform_crime_reporting_codes iucrc
32 ON
33 sdo.iucr = iucrc.iucr
34 WHERE
35 TO_NUMBER(sdo.c_year) IN 2024
36 ORDER BY
37 id
38 , c_year
39* 
FETCH FIRST 125000 ROWS ONLY
SCOTT@localhost:1521/freepdb1> /

125000 行が作成されました。

経過: 00:39:28.49
SCOTT@localhost:1521/freepdb1> commit;

コミットが完了しました。

経過: 00:00:00.04
SCOTT@localhost:1521/freepdb1> select segment_name,bytes/1024/1024/1024 "GB"
2 from user_segments where segment_name = 'SEARCH_DATA';

SEGMENT_NAME GB
------------------------------ ----------
SEARCH_DATA .2421875

経過: 00:00:00.29
SCOTT@localhost:1521/freepdb1> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'SEARCH_DATA',no_invalidate=>false);

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:05.97

 

 

 

ちなみに、前述のIASの実行計画はこんな感じ。

---------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 551K| 55M| | 77540 (1)| 00:00:04 |
| 1 | LOAD TABLE CONVENTIONAL | SEARCH_DATA | | | | | |
| 2 | SORT ORDER BY | | 551K| 55M| 61M| 77540 (1)| 00:00:04 |
|* 3 | HASH JOIN RIGHT OUTER | | 551K| 55M| | 64408 (1)| 00:00:03 |
| 4 | EXTERNAL TABLE ACCESS FULL | DISTRICTS | 25 | 200 | | 2 (0)| 00:00:01 |
|* 5 | HASH JOIN RIGHT OUTER | | 510K| 47M| | 64405 (1)| 00:00:03 |
|* 6 | EXTERNAL TABLE ACCESS FULL | ILLINOI_UNIFORM_CRIME_REPORTING_CODES | 410 | 8610 | | 3 (0)| 00:00:01 |
|* 7 | HASH JOIN RIGHT OUTER | | 510K| 37M| | 64401 (1)| 00:00:03 |
| 8 | EXTERNAL TABLE ACCESS FULL| COMMUNITIES | 77 | 1232 | | 2 (0)| 00:00:01 |
|* 9 | EXTERNAL TABLE ACCESS FULL| SEARCH_DATA_ORG | 510K| 29M| | 64398 (1)| 00:00:03 |
---------------------------------------------------------------------------------------------------------------------------------

 

 

次回、VECTOR SEARCHの実行計画という名のレントゲンネタのために、VECTOR INDEXサイズの見積サイズを確認しておきましょう。(Hierarchical Navigable Small World索引を利用します)
以下のように

参考) Oracle Database Release 23 / Oracle AI Vector Searchユーザーズ・ガイド / ベクトル索引の様々なカテゴリの管理

https://docs.oracle.com/cd/G11854_01/vecse/manage-different-categories-vector-indexes.html

Oracle Database Release 23 / Oracle AI Vector Searchユーザーズ・ガイド / ベクトル索引の使用に関するガイドライン

https://docs.oracle.com/cd/G11854_01/vecse/guidelines-using-vector-indexes.html

Oracle Database Release 23 / Oracle AI Vector Searchユーザーズ・ガイド / ベクトル索引のオプティマイザ計画

https://docs.oracle.com/cd/G11854_01/vecse/optimizer-plans-vector-indexes.html

Oracle Database Release 23 / Oracle AI Vector Searchユーザーズ・ガイド / 6 ベクトル索引およびハイブリッド・ベクトル索引の作成

https://docs.oracle.com/cd/G11854_01/vecse/create-vector-indexes-and-hybrid-vector-indexes.html#VECSE-GUID-8AF956F3-D951-4968-9B79-A6E180E87456

Oracle Database Release 23 / Oracle AI Vector Searchユーザーズ・ガイド / インメモリー近傍グラフ・ベクトル索引

https://docs.oracle.com/cd/G11854_01/vecse/memory-neighbor-graph-vector-index.html

Oracle Database Release 23 / Oracle AI Vector Searchユーザーズ・ガイド / Hierarchical Navigable Small World索引の理解

https://docs.oracle.com/cd/G11854_01/vecse/understand-hierarchical-navigable-small-world-indexes.html

Oracle Database Release 23 / Oracle AI Vector Searchユーザーズ・ガイド / Hierarchical Navigable Small World索引の構文およびパラメータ

https://docs.oracle.com/cd/G11854_01/vecse/hierarchical-navigable-small-world-index-syntax-and-parameters.html

冒頭で約830万行のデータから125,000行に制限したと書きましたが、その理由はベクトル・プールをSGA内に確保する必要があるからなんですよー。
そもそも、Oracle Database 23ai Free はメモリサイズの制限もあるので、その範囲に収まるようデータ量を制限しました。(ストレージより先にメモリサイズの制限にあたりそうだったので。。。w)

Oracle Database Release 23 / Oracle AI Vector Searchユーザーズ・ガイド / ベクトル・プールのサイズ設定

https://docs.oracle.com/cd/G11854_01/vecse/size-vector-pool.html

以下のVECTOR HNSW INDEXを作成予定なのですが、索引サイズを見積りサイズを確認しておきましょう。(Oracle Databaseには昔から便利なコマンドがあります。Explainで索引サイズの見積もりができるんですよね。

CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
ORGANIZATION
INMEMORY NEIGHBOR GRAPH
DISTANCE COSINE
WITH TARGET ACCURACY 90
/

SCOTT@localhost:1521/freepdb1> l
1 explain plan for
2 CREATE VECTOR INDEX search_data_hnsw_ix ON search_data ( vector_desc )
3 ORGANIZATION
4 INMEMORY NEIGHBOR GRAPH
5 DISTANCE COSINE
6* WITH TARGET ACCURACY 90
05:41:08 SCOTT@localhost:1521/freepdb1> /

解析されました。

経過: 00:00:00.02
SCOTT@localhost:1521/freepdb1> @?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------
Plan hash value: 2727344110

----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | CREATE INDEX STATEMENT | | 125K| 188M| 14689 (1)| 00:00:01 |
| 1 | VECTOR INDEX BUILD | SEARCH_DATA_HNSW_IX | | | | |
----------------------------------------------------------------------------------------------

Note
-----
- estimated index size: 293M bytes

12GBまでデータを使えるんだけどインメモリー系機能使おうとすると、メモリサイズの制限キツイよねと、思ったりw

Enjoy SQL and Oracle Database.

次回へ続く。

 


Oracle Database 23ai freeで試すVector Search - ONNXモデル準備編

| | | コメント (0)

2025年6月 3日 (火)

Oracle Database 23ai freeで試すVector Search - ONNXモデル準備編

Getting Started with Oracle Database 23ai AI Vector Search
https://blogs.oracle.com/database/post/getting-started-with-oracle-database-23ai-ai-vector-search

上記ブログを見て、これも実行計画はSQL文のレントゲン写真ネタにはなりそうだなぁ、と思い。いきなり準備を始めたw 今日はその準備(日本語対応してないモデル)

環境は以下の通り。
VirtualBOX向けPrebuild VMを利用(なお、Arm64ではなく、Intel Mac)
いずれ、Arm64のネタも書く予定ではいますがw それだと古ーーーーーい Oracleだとこんな結果に。。。みたいな差分比較ネタができないのでw とりあえず。X86_64環境にて。。。(Arm64ネイティブの環境はまだ作りかけなのでw)

クラウドではなくオフラインでも楽しめる環境(23ai Freeなのでリソース制限の範囲内で遊べる環境にします)を作ります。

*** mac info. ***
ProductName: macOS
ProductVersion: 12.7.6
BuildVersion: 21H1320

*** macOS ver. ***
Model Name: MacBook
Processor Name: Dual-Core Intel Core m5
Processor Speed: 1.2 GHz
Number of Processors: 1
Total Number of Cores: 2
Memory: 8 GB

*** VirtualBox ver. ***
7.0.10r158379
[oracle@localhost ~]$ cat /etc/oracle-release /etc/redhat-release
Oracle Linux Server release 8.9
Red Hat Enterprise Linux release 8.9 (Ootpa)
[oracle@localhost ~]$ uname -srpo
Linux 5.15.0-3.60.5.1.el8uek.x86_64 x86_64 GNU/Linux
SCOTT@localhost:1521/freepdb1> select banner_full from v$version;

BANNER_FULL
--------------------------------------------------------------------------------
Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.4.0.24.05


12.2 ONNXモデルへの事前トレーニング済モデルの変換: エンドツーエンドの手順
https://docs.oracle.com/cd/G28130_01/2-23ai/mlpug/convert-pretrained-models-onnx-model-end-end-instructions.html

の手順に沿ってONNIXモデルの準備をします!。 

ドキュメント斜め読みしながら環境をつくっていたこともあり、インストールするOML4Pyのリリースが異なっていたりしてやり直したりしているログも含めているため読みずらいかもしれません。悪しからず m(_ _)m  
進める前に、必要なversionなど事前確認しておくとスムーズだと思います。。。。。(お前が言うか〜っw



初っ端からエラー!。なんだろ。(すんなり進むのかこれw)

Pythonのインストール

[oracle@localhost ~]$ sudo yum install libffi-devel openssl openssl-devel tk-devel xz-devel zlib-devel bzip2-devel readline-devel libuuid-devel ncurses-devel libaio
[sudo] oracle のパスワード:
Oracle Linux 8 BaseOS Latest (x86_64) 0.0 B/s | 0 B 08:00
Errors during downloading metadata for repository 'ol8_baseos_latest':
- Curl error (28): Timeout was reached for https://yum-us-phoenix-1.oracle.com/repo/OracleLinux/OL8/baseos/latest/x86_64/repodata/repomd.xml
[Connection timed out after 120000 milliseconds]
エラー: repo 'ol8_baseos_latest' のメタデータのダウンロードに失敗しました : Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried


こんなの見つけた〜。多分これ。
yum update not working using Oracle Database 23ai Free Container Image Lite #2900
https://github.com/oracle/docker-images/issues/2900


間違いない! これが原因だ
書かれている対策をまんま適用して。。。。

[oracle@localhost ~]$ cd /etc/yum/vars/
[oracle@localhost vars]$ ll
合計 8
-rw-r--r--. 1 root root 11 5月 1 2024 ocidomain
-rw-r--r--. 1 root root 14 5月 1 2024 ociregion
[oracle@localhost vars]$ cat ociregion
-us-phoenix-1
[oracle@localhost vars]$ cat ocidomain
oracle.com

[oracle@localhost vars]$ sudo mv ociregion ociregion.org
[sudo] oracle のパスワード:
[oracle@localhost vars]$ sudo su -
[root@localhost ~]# sudo echo -n "" > /etc/yum/vars/ociregion
[root@localhost ~]# cat /etc/yum/vars/ociregion
[root@localhost ~]# exit
logout

気を取り直して、もう一度。

[oracle@localhost ~]$ sudo yum install libffi-devel openssl openssl-devel tk-devel xz-devel zlib-devel bzip2-devel readline-devel libuuid-devel ncurses-devel libaio

Oracle Linux 8 BaseOS Latest (x86_64) 5.0 MB/s | 97 MB 00:19
Oracle Linux 8 Application Stream (x86_64) 5.1 MB/s | 70 MB 00:13
Latest Unbreakable Enterprise Kernel Release 7 for Oracle Linux 8 (x86_64) 5.1 MB/s | 61 MB 00:12

...略...

パッケージ openssl-1:1.1.1k-12.el8_9.x86_64 は既にインストールされています。
パッケージ libaio-0.3.112-1.el8.x86_64 は既にインストールされています。
依存関係が解決しました。
===============================================================================================================
パッケージ アーキテクチャー バージョン リポジトリー サイズ
===============================================================================================================
インストール:
bzip2-devel x86_64 1.0.6-28.el8_10 ol8_baseos_latest 224 k

...略...

xorg-x11-proto-devel noarch 2020.1-3.el8 ol8_appstream 280 k

トランザクションの概要
===============================================================================================================
インストール 32 パッケージ
アップグレード 10 パッケージ

ダウンロードサイズの合計: 17 M
これでよろしいですか? [y/N]: y
パッケージのダウンロード:
(1/42): bzip2-devel-1.0.6-28.el8_10.x86_64.rpm 1.5 MB/s | 224 kB 00:00

...略...

(42/42): util-linux-2.32.1-46.0.1.el8.x86_64.rpm 3.6 MB/s | 2.5 MB 00:00
-----------------------------------------------------------------------------------------------------------
合計 4.3 MB/s | 17 MB 00:03

...略...

トランザクションのテストに成功しました。
トランザクションを実行中
準備 : 1/1
scriptletの実行中: libuuid-2.32.1-46.0.1.el8.x86_64 1/1
アップグレード中 : libuuid-2.32.1-46.0.1.el8.x86_64 1/52

...略...

xz-devel-5.2.4-4.el8_6.x86_64 zlib-devel-1.2.11-25.el8.x86_64

[oracle@localhost ~]$ mkdir -p $HOME/python
[oracle@localhost ~]$ wget https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tgz

--2025-05-13 02:48:50-- https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tgz

...略...

HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 27195214 (26M) [application/octet-stream]
`Python-3.12.0.tgz' に保存中

Python-3.12.0.tgz 100%[===================================>] 25.93M 4.79MB/s 時間 5.4s

2025-05-13 02:48:56 (4.80 MB/s) - `Python-3.12.0.tgz' へ保存完了 [27195214/27195214]

[oracle@localhost ~]$ tar -xvzf Python-3.12.0.tgz --strip-components=1 -C /home/$USER/python
Python-3.12.0/Grammar/
Python-3.12.0/Grammar/python.gram

...略...

Python-3.12.0/Objects/tupleobject.c
Python-3.12.0/install-sh

[oracle@localhost ~]$ cd $HOME/python
[oracle@localhost python]$ ./configure --prefix=$HOME/python

checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu

...略...

configure: creating Modules/Setup.local
configure: creating Makefile
configure:

If you want a release build with all stable optimizations active (PGO, etc),
please run ./configure --enable-optimizations

[oracle@localhost python]$ make clean; make

find . -depth -name '__pycache__' -exec rm -rf {} ';'
find . -name '*.py[co]' -exec rm -f {} ';'
find . -name '*.[oa]' -exec rm -f {} ';'

...略...

# Pristine binaries before BOLT optimization.
rm -f *.prebolt
# BOLT instrumented binaries.
rm -f *.bolt_inst
gcc -pthread -c -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall
-std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes
-Werror=implicit-function-declaration -fvisibility=hidden -I./Include/internal
-I. -I./Include -DPy_BUILD_CORE -o Programs/python.o ./Programs/python.c

...略...

LC_ALL=C sed -e 's,\$(\([A-Za-z0-9_]*\)),\$\{\1\},g' < Misc/python-config.sh >python-config
The following modules are *disabled* in configure script:
_sqlite3

The necessary bits to build these optional modules were not found:
_dbm _gdbm nis
To find the necessary bits, look in configure.ac and config.log.

Checked 111 modules (31 built-in, 75 shared, 1 n/a on linux-x86_64, 1 disabled, 3 missing, 0 failed on import)

[oracle@localhost python]$ make altinstall

Creating directory /home/oracle/python/bin
Creating directory /home/oracle/python/lib

...略...

The necessary bits to build these optional modules were not found:
_dbm _gdbm nis
To find the necessary bits, look in configure.ac and config.log.

Checked 111 modules (31 built-in, 75 shared, 1 n/a on linux-x86_64, 1 disabled, 3 missing, 0 failed on import)
Creating directory /home/oracle/python/lib/python3.12
Creating directory /home/oracle/python/lib/python3.12/asyncio

...略...

Looking in links: /tmp/tmp9k7fgbr3
Processing /tmp/tmp9k7fgbr3/pip-23.2.1-py3-none-any.whl
Installing collected packages: pip
WARNING: The script pip3.12 is installed in '/home/oracle/python/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pip-23.2.1

ここまでマニュアルの通り。想定外のエラーもなく順調ですね〜

変数PYTHONHOME、PATHおよび LD_LIBRARY_PATHを設定〜python3およびpip3のシンボリックリンクの作成など

[oracle@localhost python]$ export PYTHONHOME=$HOME/python
[oracle@localhost python]$ export PATH=$PYTHONHOME/bin:$PATH
[oracle@localhost python]$ export LD_LIBRARY_PATH=$PYTHONHOME/lib:$LD_LIBRARY_PATH
[oracle@localhost python]$ cd $HOME/python/bin
[oracle@localhost bin]$ ln -s python3.12 python3
[oracle@localhost bin]$ ln -s pip3.12 pip3
[oracle@localhost bin]$ cd $HOME
[oracle@localhost ~]$ wget https://download.oracle.com/otn_software/linux/instantclient/2340000/instantclient-basic-linux.x64-23.4.0.24.05.zip

--2025-05-13 03:03:13-- https://download.oracle.com/otn_software/linux/instantclient/2340000/instantclient-basic-linux.x64-23.4.0.24.05.zip

...略...

HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 118377607 (113M) [application/zip]
`instantclient-basic-linux.x64-23.4.0.24.05.zip' に保存中

instantclient-basic-linux.x64-23.4.0.24.05.zip 100%[===========================>] 112.89M 5.83MB/s 時間 18s

2025-05-13 03:03:31 (6.38 MB/s) - `instantclient-basic-linux.x64-23.4.0.24.05.zip' へ保存完了 [118377607/118377607]

[oracle@localhost ~]$
[oracle@localhost ~]$ unzip instantclient-basic-linux.x64-23.4.0.24.05.zip
Archive: instantclient-basic-linux.x64-23.4.0.24.05.zip
replace META-INF/MANIFEST.MF? [y]es, [n]o, [A]ll, [N]one, [r]ename: A
inflating: META-INF/MANIFEST.MF

...略...

instantclient_23_4/libocci.so.21.1 -> libocci.so.23.1
instantclient_23_4/libocci.so.22.1 -> libocci.so.23.1
[oracle@localhost ~]$
[oracle@localhost ~]$ export LD_LIBRARY_PATH=$HOME/instantclient_23_4:$LD_LIBRARY_PATH




.bashrcにも以下環境変数を追加しておきます
export PYTHONHOME=$HOME/python
export PATH=$PYTHONHOME/bin:$PATH
export LD_LIBRARY_PATH=$PYTHONHOME/lib:$LD_LIBRARY_PATH

[oracle@localhost ~]$ vi .bashrc
[oracle@localhost ~]$ cat .bashrc
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi

...略...

export TWO_TASK=FREEPDB1
fi


# Environment variables for Python

export PYTHONHOME=$HOME/python
export PATH=$PYTHONHOME/bin:$PATH
export LD_LIBRARY_PATH=$PYTHONHOME/lib:$LD_LIBRARY_PATH

# Note: If Python is used to load models to the database, set this environment variable for the Oracle Instant Client.

export LD_LIBRARY_PATH=$HOME/instantclient_23_4:$LD_LIBRARY_PATH


requirements.txtの作成

[oracle@localhost ~]$ vi requirements.txt
[oracle@localhost ~]$ cat requirements.txt
--extra-index-url https://download.pytorch.org/whl/cpu
pandas==2.1.1
setuptools==68.0.0
scipy==1.12.0
matplotlib==3.8.4
oracledb==2.2.0
scikit-learn==1.4.1post1
numpy==1.26.4
onnxruntime==1.17.0
onnxruntime-extensions==0.10.1
onnx==1.16.0
torch==2.2.0+cpu
transformers==4.38.1
sentencepiece==0.2.0

pip3のアップグレード

[oracle@localhost ~]$ pip3 install --upgrade pip

Requirement already satisfied: pip in ./python/lib/python3.12/site-packages (23.2.1)
Collecting pip
Obtaining dependency information for pip
from https://files.pythonhosted.org/packages/29/a2/d40fb2460e883eca5199c62cfc2463fd261f760556ae6290f88488c362c0/pip-25.1.1-py3-none-any.whl.metadata
Downloading pip-25.1.1-py3-none-any.whl.metadata (3.6 kB)
Downloading pip-25.1.1-py3-none-any.whl (1.8 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 6.3 MB/s eta 0:00:00
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 23.2.1
Uninstalling pip-23.2.1:
Successfully uninstalled pip-23.2.1
Successfully installed pip-25.1.1

[oracle@localhost ~]$ pip3 install -r requirements.txt

Looking in indexes: https://pypi.org/simple, https://download.pytorch.org/whl/cpu
Collecting pandas==2.1.1 (from -r requirements.txt (line 2))
Downloading pandas-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (18 kB)
Collecting setuptools==68.0.0 (from -r requirements.txt (line 3))

...略...

Successfully installed MarkupSafe-3.0.2 certifi-2025.4.26 cffi-1.17.1 charset-normalizer-3.4.2 coloredlogs-15.0.1 contourpy-1.3.2
cryptography-44.0.3 cycler-0.12.1 filelock-3.18.0 flatbuffers-25.2.10 fonttools-4.58.0 fsspec-2025.3.2 hf-xet-1.1.1 huggingface-hub-0.31.1
humanfriendly-10.0 idna-3.10 jinja2-3.1.6 joblib-1.5.0 kiwisolver-1.4.8 matplotlib-3.8.4 mpmath-1.3.0 networkx-3.4.2 numpy-1.26.4 onnx-1.16.0
onnxruntime-1.17.0 onnxruntime-extensions-0.10.1 oracledb-2.2.0 packaging-25.0 pandas-2.1.1 pillow-11.2.1 protobuf-6.30.2 pycparser-2.22
pyparsing-3.2.3 python-dateutil-2.9.0.post0 pytz-2025.2 pyyaml-6.0.2 regex-2024.11.6 requests-2.32.3 safetensors-0.5.3 scikit-learn-1.4.1.post1
scipy-1.12.0 sentencepiece-0.2.0 setuptools-68.0.0 six-1.17.0 sympy-1.14.0 threadpoolctl-3.6.0 tokenizers-0.15.2 torch-2.2.0+cpu
tqdm-4.67.1 transformers-4.38.1 typing-extensions-4.13.2 tzdata-2025.2 urllib3-2.4.0


あ”!
oml-2.1をインストールしてしまっていたので、oml-2.0をインストールしなおし. (oml-2.1でやってもよかったけどw

Oracle Machine Learning for Python Downloads
https://www.oracle.com/database/technologies/oml4py-downloads.html

から、2.0をダウンロード(oml4py-client-linux-x86_64-2.0.zip)して、と。。。。

ということでreinstall

[oracle@localhost ~]$ unzip oml4py-client-linux-x86_64-2.0.zip
Archive: oml4py-client-linux-x86_64-2.0.zip
inflating: client/client.pl
inflating: client/OML4PInstallShared.pm
inflating: client/oml-2.0-cp312-cp312-linux_x86_64.whl
extracting: client/oml4py.ver

[oracle@localhost ~]$ pip3 install client/oml-2.0-cp312-cp312-linux_x86_64.whl
Processing ./client/oml-2.0-cp312-cp312-linux_x86_64.whl
Requirement already satisfied: numpy>=1.26.4 in ./python/lib/python3.12/site-packages (from oml==2.0) (2.2.5)

...略...

Requirement already satisfied: six>=1.5 in ./python/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib>=3.7.2->oml==2.0) (1.17.0)
Requirement already satisfied: joblib>=1.2.0 in ./python/lib/python3.12/site-packages (from scikit-learn>=1.2.1->oml==2.0) (1.5.0)
Requirement already satisfied: threadpoolctl>=3.1.0 in ./python/lib/python3.12/site-packages (from scikit-learn>=1.2.1->oml==2.0) (3.6.0)
Installing collected packages: oml
Attempting uninstall: oml
Found existing installation: oml 2.1
Uninstalling oml-2.1:
Successfully uninstalled oml-2.1
Successfully installed oml-2.0

なにか怒られてるな。なぜだ。。。。numpy 1.xが必要っぽい。。。

[oracle@localhost ~]$ python3
Python 3.12.0 (main, May 13 2025, 02:54:53) [GCC 8.5.0 20210514 (Red Hat 8.5.0-20.0.3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import oml

A module that was compiled using NumPy 1.x cannot be run in
NumPy 2.2.5 as it may crash. To support both 1.x and 2.x
versions of NumPy, modules must be compiled with NumPy 2.0.
Some module may need to rebuild instead e.g. with 'pybind11>=2.12'.

If you are a user of the module, the easiest solution will be to
downgrade to 'numpy<2' or try to upgrade the affected module.
We expect that some modules will need time to support NumPy 2.

Traceback (most recent call last): File "", line 1, in
File "/home/oracle/python/lib/python3.12/site-packages/oml/__init__.py", line 77, in
from oml.utils import *
File "/home/oracle/python/lib/python3.12/site-packages/oml/utils/__init__.py", line 23, in
from .embeddings import EmbeddingModelConfig,EmbeddingModel
File "/home/oracle/python/lib/python3.12/site-packages/oml/utils/_pipeline/__init__.py", line 22, in
from .PipelineBuilder import PipelineBuilder

...略...

/home/oracle/python/lib/python3.12/site-packages/torch/nn/modules/transformer.py:20: UserWarning:
Failed to initialize NumPy: _ARRAY_API not found (Triggered internally at ../torch/csrc/utils/tensor_numpy.cpp:84.)
device: torch.device = torch.device(torch._C._get_default_device()), # torch.device('cpu'),

A module that was compiled using NumPy 1.x cannot be run in
NumPy 2.2.5 as it may crash. To support both 1.x and 2.x
versions of NumPy, modules must be compiled with NumPy 2.0.
Some module may need to rebuild instead e.g. with 'pybind11>=2.12'.

If you are a user of the module, the easiest solution will be to
downgrade to 'numpy<2' or try to upgrade the affected module.
We expect that some modules will need time to support NumPy 2.

Traceback (most recent call last): File "", line 1, in
File "/home/oracle/python/lib/python3.12/site-packages/oml/__init__.py", line 77, in
from oml.utils import *

...略...

from onnxruntime.capi._pybind_state import ExecutionMode # noqa: F401
File "/home/oracle/python/lib/python3.12/site-packages/onnxruntime/capi/_pybind_state.py", line 32, in
from .onnxruntime_pybind11_state import * # noqa
AttributeError: _ARRAY_API not found
/home/oracle/python/lib/python3.12/site-packages/oml/__init__.py:80: UserWarning: oml.utils import failed
warn('oml.utils import failed')
>>>

ということで、 numpy 1.x にする。。。うまくいった

[oracle@localhost ~]$ pip3 install --upgrade numpy==1.26.4
Collecting numpy==1.26.4
Using cached numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (61 kB)
Using cached numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (18.0 MB)
Installing collected packages: numpy
Attempting uninstall: numpy
Found existing installation: numpy 2.2.5
Uninstalling numpy-2.2.5:
Successfully uninstalled numpy-2.2.5
Successfully installed numpy-1.26.4


やっふーーーーーーーー!

[oracle@localhost ~]$ python3
Python 3.12.0 (main, May 13 2025, 02:54:53) [GCC 8.5.0 20210514 (Red Hat 8.5.0-20.0.3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
>>> import oml
>>>
>>> from oml.utils import EmbeddingModel, EmbeddingModelConfig
>>> EmbeddingModelConfig.show_preconfigured()
['sentence-transformers/all-mpnet-base-v2', 'sentence-transformers/all-MiniLM-L6-v2'
, 'sentence-transformers/multi-qa-MiniLM-L6-cos-v1', 'ProsusAI/finbert'
,'medicalai/ClinicalBERT', 'sentence-transformers/distiluse-base-multilingual-cased-v2'
, 'sentence-transformers/all-MiniLM-L12-v2', 'BAAI/bge-small-en-v1.5'
, 'BAAI/bge-base-en-v1.5', 'taylorAI/bge-micro-v2', 'intfloat/e5-small-v2', 'intfloat/e5-base-v2'
, 'prajjwal1/bert-tiny', 'thenlper/gte-base'
, 'thenlper/gte-small', 'TaylorAI/gte-tiny', 'infgrad/stella-base-en-v2'
, 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'
, 'intfloat/multilingual-e5-base', 'intfloat/multilingual-e5-small'
, 'sentence-transformers/stsb-xlm-r-multilingual']
>>>


DBMS_VECTOR.LOAD_ONNX_MODELを使用してデータベースに手動でアップロード可能な、ONNXファイルを生成
"事前構成済の埋込みモデルをローカル・ファイルにエクスポートします。oml.utilsからEmbeddingModelをインポートします。これにより、ONNX形式モデルがローカル・ファイル・システムにエクスポートされます。"

ディレクトリオプジェクト向けのディレクトリを先に作成し、そのディレクトリで行うと便利ですね。

[oracle@localhost ~]$ cd work4vector/
[oracle@localhost work4vector]$ pwd
/home/oracle/work4vector
[oracle@localhost work4vector]$ python3
Python 3.12.0 (main, May 13 2025, 02:54:53) [GCC 8.5.0 20210514 (Red Hat 8.5.0-20.0.3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from oml.utils import EmbeddingModel
>>> em = EmbeddingModel(model_name="sentence-transformers/all-MiniLM-L6-v2")
>>> em.export2file("all-MiniLM-L6-v2", output_dir=".")
/home/oracle/python/lib/python3.12/site-packages/huggingface_hub/file_download.py:943: FutureWarning: `resume_download` is deprecated
and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
tokenizer_config.json: 100%|████████████████████████████████████████████████| 350/350 [00:00<00:00, 821kB/s]
vocab.txt: 100%|████████████████████████████████████████████████████████████| 232k/232k [00:00<00:00, 565kB/s]
special_tokens_map.json: 100%|██████████████████████████████████████████████| 112/112 [00:00<00:00, 463kB/s]
tokenizer.json: 100%|███████████████████████████████████████████████████████| 466k/466k [00:00<00:00, 1.47MB/s]
config.json: 100%|██████████████████████████████████████████████████████████| 612/612 [00:00<00:00, 3.88MB/s]
model.safetensors: 100%|████████████████████████████████████████████████████| 90.9M/90.9M [00:13<00:00, 6.72MB/s]
>>>

[oracle@localhost work4vector]$ ll all-MiniLM-L6-v2.onnx
-rw-rw-r--. 1 oracle oracle 90621438 5月 13 05:47 all-MiniLM-L6-v2.onnx

[oracle@localhost work4vector]$ sqlplus sys@localhost:1521/freepdb1 as sysdba

...略...

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.4.0.24.05
に接続されました。
SYS@localhost:1521/freepdb1> create directory onnx_dir as '/home/oracle/work4vector';

ディレクトリが作成されました。

経過: 00:00:00.08
SYS@localhost:1521/freepdb1> !pwd
/home/oracle/work4vector

SYS@localhost:1521/freepdb1> grant read,write on directory onnx_dir to scott;

権限付与が成功しました。

経過: 00:00:00.05
SYS@localhost:1521/freepdb1> grant create mining model to scott;

権限付与が成功しました。

経過: 00:00:00.02

DBMS_VECTOR.LOAD_ONNX_MODELプロシージャを使用して、OMLユーザー・スキーマにモデルをロードする

コードは以下

BEGIN
DBMS_VECTOR.LOAD_ONNX_MODEL(
directory => 'ONNX_DIR',
file_name => 'all-MiniLM-L6-v2.onnx',
model_name => 'ALL_MINILM_L6');
END;
/

SCOTT@localhost:1521/freepdb1> l
1 BEGIN
2 DBMS_VECTOR.LOAD_ONNX_MODEL(
3 directory => 'ONNX_DIR',
4 file_name => 'all-MiniLM-L6-v2.onnx',
5 model_name => 'ALL_MINILM_L6');
6* END;
SCOTT@localhost:1521/freepdb1> /

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:08.93


SQLを使用してモデルを確認

SQL文は以下

SELECT
model_name
, algorithm
, mining_function
FROM
user_mining_models
WHERE
model_name = 'ALL_MINILM_L6'
/

SCOTT@localhost:1521/freepdb1> l
1 SELECT
2 model_name
3 , algorithm
4 , mining_function
5 FROM
6 user_mining_models
7 WHERE
8* model_name = 'ALL_MINILM_L6'
SCOTT@localhost:1521/freepdb1> /

MODEL_NAME ALGORITHM MINING_FUNCTION
------------------------------ ------------------------------ ------------------------------
ALL_MINILM_L6 ONNX EMBEDDING

経過: 00:00:00.01


ユーザーがアクセスできるモデルを確認するビューからも確認しておきます。

SQLはこんな感じ

SELECT
view_name
, view_type
FROM
user_mining_model_views
WHERE
model_name = 'ALL_MINILM_L6'
ORDER BY
view_name
/

SCOTT@localhost:1521/freepdb1> l
1 SELECT
2 view_name
3 , view_type
4 FROM
5 user_mining_model_views
6 WHERE
7 model_name = 'ALL_MINILM_L6'
8 ORDER BY
9* view_name
SCOTT@localhost:1521/freepdb1> /

VIEW_NAME VIEW_TYPE
------------------------------ ------------------------------
DM$VJALL_MINILM_L6 ONNX Metadata Information
DM$VMALL_MINILM_L6 ONNX Model Information
DM$VPALL_MINILM_L6 ONNX Parsing Information

経過: 00:00:00.01

上記ビューを問い合わせモデルの情報を確認:)

SCOTT@localhost:1521/freepdb1> SELECT * FROM dm$vmall_minilm_l6;

NAME VALUE
------------------------------ -----------------------------------------------
Producer Name onnx.compose.merge_models
Graph Name tokenizer_main_graph
Graph Description Graph combining tokenizer and main_graph
tokenizer
main_graph
Version 1
Input[0] input:string[?]
Output[0] embedding:float32[?,384]

6行が選択されました。

経過: 00:00:00.00

VECTOR_EMBEDDING SQLスコアリング関数を軽く試す!

SQL文はこんな感じ

SELECT VECTOR_EMBEDDING(ALL_MINILM_L6 USING 'RES' as DATA) AS embedding;

SCOTT@localhost:1521/freepdb1> r
1* SELECT VECTOR_EMBEDDING(ALL_MINILM_L6 USING 'RES' as DATA) AS embedding

EMBEDDING
----------------------------------------------------------------------------------------------------
[-1.16423056E-001,1.54331746E-002,-4.69262414E-002,7.16730766E-003,3.50234732E-002,-4.02988419E-002,
.08232533E-002,4.99225073E-002,-1.86311249E-002,-2.62796488E-002,-3.2601878E-002,5.22731952E-002,-9.

...略...

-003,6.00763485E-002,1.91014066E-001,7.64457136E-002,1.46513591E-002,3.13854888E-002]


経過: 00:00:00.36


関数しか実行していないので面白い実行計画は現れませんが一応、確認だけw

SCOTT@localhost:1521/freepdb1> set autot trace exp stat
SCOTT@localhost:1521/freepdb1> r
1* SELECT VECTOR_EMBEDDING(ALL_MINILM_L6 USING 'RES' as DATA) AS embedding

経過: 00:00:00.33

実行計画
----------------------------------------------------------
Plan hash value: 1388734953

-----------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
| 1 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
0 consistent gets
0 physical reads
0 redo size
2261 bytes sent via SQL*Net to client
473 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed


user_mining_models 、user_mining_model_attributesビューなんてのありますね。ふむふむ

SCOTT@localhost:1521/freepdb1> SELECT model_name, mining_function, algorithm, algorithm_type, model_size FROM user_mining_models;

MODEL_NAME MINING_FUNCTION ALGORITHM ALGORITHM_ MODEL_SIZE
------------------------------ ------------------------------ ------------------------------ ---------- ----------
ALL_MINILM_L6 EMBEDDING ONNX NATIVE 90621438

経過: 00:00:00.02

SCOTT@localhost:1521/freepdb1> l
1 SELECT model_name, attribute_name, attribute_type, data_type, vector_info
2* FROM user_mining_model_attributes
SCOTT@localhost:1521/freepdb1> /

MODEL_NAME ATTRIBUTE_NAME ATTRIBUTE_TY DATA_TYPE VECTOR_INFO
------------------------------ ------------------------------ ------------ ------------------------------ ------------------------------
ALL_MINILM_L6 ORA$ONNXTARGET VECTOR VECTOR VECTOR(384,FLOAT32)
ALL_MINILM_L6 DATA TEXT VARCHAR2

経過: 00:00:00.01


ONNX形式の埋込みモデルを使用して、ユーザーの入力テキスト文字列「hello」をベクトルに変換してみる。ベクトル眺めても意味わからないけども、とりあえず、できてるみたいw

SCOTT@localhost:1521/freepdb1> col EMBEDDING for a200
SCOTT@localhost:1521/freepdb1> SELECT TO_VECTOR(VECTOR_EMBEDDING(all_minilm_l6 USING 'hello' as data)) AS EMBEDDING;

EMBEDDING
-----------------------------------------------------------------------------------------------
[-6.27717897E-002,5.49588911E-002,5.21648414E-002,8.57899487E-002,-8.27489197E-002,-7.45729804E-
002,6.85546845E-002,1.83963589E-002,-8.20114315E-002,-3.73847559E-002,1.21248914E-002,3.51829384
E-003,-4.13423125E-003,-4.37844135E-002,2.18073577E-002,-5.10276016E-003,1.95467062E-002,-4.2348

...略...

-4.67414372E-002,-1.34112127E-002,6.51347339E-002,5.09059429E-002,5.1483497E-002,7.09215924E-003]


経過: 00:00:00.67

ということで、準備完了(このモデルだと日本語は対応してないみたいだけど、ひとまず、軽く遊べる小さなOracle Database 23ai Free on VirtualBOXの環境の準備完了!

SQL文もここまでくると、何やっているのか理解しながら進めないと、迷子になりそうな気がしないでもないw

次回へつづく。

| | | コメント (0)

2025年4月28日 (月)

とあるJargon絡みのおはなし

久々のDatabaseネタですw

先日、ぐるぐる系がほんとうに嫌いなんだなぁ、というアトモスフィアを感じる会話に交ぜてもらいww、
ちょっと思い出したことがあったので書いてみようと思います。
(悪いとか良いとかそういうくくりの話に振るつもりはないので、どの方法をどう使うか、よーく考えて判断しましょうね、ぐらいの気持ちで書いいてますw)

 

世の中には、意図的にぐるぐる系バッチだけ、index unique scanや、access by rowidだけになることを狙い、統計情報も意図的に設定(e.g.  https://docs.oracle.com/cd/F82042_01/arpls/DBMS_STATS.html#GUID-F993ADFE-85A9-4939-84D1-CC7A6E5C56D2  など)したり、あたまを振り絞って考え抜かれた ぐるぐる系 の世界があったります。
待機イベントは、DB cpu, db file sequential readが主体で。(私は、やりたいとは思わないけどw)

 

ところで、

ぐるぐる系ってのは、いわゆるN+1問題に類する処理。
フレームワーク起因でそうなりやすいやつだけでなく、意図的にぐるぐるしちゃうというのもすべて含めたもの。基本的にネガティブな意味で使われていますw。
そんなJargonが存在する現場では、的確に意図が伝わるってのがメリットだったり。。。
N+1がさぁ〜より、ぐるぐるがさぁ〜ってほうが日本語の脳みそにはイメージしやすくてグっと来るw 方は多い感じはしますw。現場でうまれたJargonですのでw 英語3文字略語よりわかりやすくて良い(個人差ありw)

 

もう一つ、 ぐるぐる系の反対語のJargonって知ってます?

答え: ガツン系

ここまでの用語解説部分はツッコミいれるところではないので、かるくスルーしてくださいませ。 前説ですから。 m(_ _)m

(ちょっと脱線しすぎた感じはするけどもwwww)

 

ということで(どう言うことだ!w)、ながーーーーーーい前置きはこれぐらいにして本題へ。

ぐるぐる系とガツン系の予習をしていただいたところでww
ちょっとしたサンプルを紹介し、どちらを選ぶかは、アナタしだいです。という意味で、打ちっぱなしというか投げっぱなしの話を書いておこう思います。

Image-1

 

今回の例ではストアドプロシージャ化してあります。
ストアドプロシージャでぐるぐるしちゃった場合、ネットワークレイテンシーの影響は受けないのですが繰り返し部分のオーバーヘッド有無の差異は見えるはずです。
SQL文の繰り返しもそうですが、それ以外のオーバーヘッドも積もると山になるわけで。

なお、SQL文がものすごく軽くて速い、かつ、バッチ専用の別サーバーで実行される場合は繰り返し実行されるロジックだけでなく、サーバー間のネットワークレイテンシーの影響が見過ごせないぐらい(回避不能でもありますが)目立ってくることも思い出してくださいね。
以下の図をイメージしておくと危険な香りを感じ取れるのではないか思います。(特にクラウド上では)

パートの処理時間は記載の時間程度かかった場合のイメージで書いています。処理時間の延び具合も実際の処理だったり環境でも異なるのであくまで、そんなイメージにしてあります。

 

普通のぐるぐる系のイメージ(オンプレミスで隣接しているような環境だネットワークレイテンシーは短いのは言うまでもないですが)
20250420-161431

 

普通のぐるぐる系がネットワークレイテンシーの遅い環境に乗せられてしまった場合のイメージ(オンプレミス環境で熟成されたぐるぐる系がクラウドに乗ったらこうなったなんて話は、稀によく聞きますw。しかも自分たちではどうしようもないですよね。こればかりは。)
20250420-161442

 

普通のグルグル系のがネットワークレイテンシーの遅い環境に乗せられてしまった上に、実行計画がぶれて大変なことになってしまったイメージ
20250420-161450

 

最後に、がツン系にして回避したイメージ
20250420-161501

 

この辺り大切なので、試験にでるよ!(ないないw

 

利用したリリースは以下の通り。


Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0

 

差分マージを例ネタに、ぐるぐる系とガツン系の例、そして、ぐるぐる系のループ内で実行されるSQL文の実行計画が最悪の状態でぐるぐるしている例を載せておきますね。


準備

 

以下ディレクトリにcsvファイルを作成してあります。連番で100000行のデータが入っています。


[oracle@localhost ~]$ ll /home/oracle/rowbyrow_or_batch/*.csv
-rw-r--r--. 1 oracle oinstall 588894 1月 16 14:39 /home/oracle/rowbyrow_or_batch/data.csv
[oracle@localhost ~]$ cat /home/oracle/rowbyrow_or_batch/data.csv
1
2
3
4

...中略...

99996
99997
99998
99999
100000
[oracle@localhost ~]$

 

差分データを外部表で提供するためにディレクトリオブジェクト、対応するディレクトリを作成します。


SCOTT@orclpdb1> create directory rowbyrow_or_batch as '/home/oracle/rowbyrow_or_batch';

ディレクトリが作成されました。

経過: 00:00:00.24
SCOTT@orclpdb1> select directory_name, directory_path from dba_directories where directory_name = upper('rowbyrow_or_batch');

DIRECTORY_NAME DIRECTORY_PATH
------------------------------ -------------------------------------------------
ROWBYROW_OR_BATCH /home/oracle/rowbyrow_or_batch

経過: 00:00:00.09
SCOTT@orclpdb1>

 

次に外部表を定義します。この時点では該当ディレクトリにcsvファイルは実在しなくても問題ありませんが、実行前には対応するファイルシステムのディレクトとcsvファイルを作成、配置しておく必要はあります。
(外部表作成時にそれらの存在は確認されていないだけです)


SCOTT@orclpdb1> DROP TABLE csv_data PURGE;
SCOTT@orclpdb1> CREATE TABLE csv_data
(
id NUMBER NOT NULL
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY rowbyrow_or_batch
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY '|'
(
id
)
)
LOCATION (
'data.csv'
)
);

表が作成されました。

経過: 00:00:00.03

SCOTT@orclpdb1> desc csv_data
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER

SCOTT@orclpdb1> select min(id),max(id),count(1) from csv_data;

MIN(ID) MAX(ID) COUNT(1)
---------- ---------- ----------
1 100000 100000

SCOTT@orclpdb1> select count(distinct id) from csv_data;

COUNT(DISTINCTID)
-----------------
100000

 

差分データをマージする表を作成してデータを登録しておきます。(統計情報取得も忘れずに)


SCOTT@orclpdb1> CREATE TABLE tmp
(
ID NUMBER NOT NULL
, CONSTRAINT pk_tmp PRIMARY KEY (id) USING INDEX
);

表が作成されました。

経過: 00:00:00.17
SCOTT@orclpdb1> BEGIN
FOR i in 50001..200000 LOOP
INSERT INTO tmp VALUES(i);
IF MOD(i,100) = 0 THEN COMMIT; END IF;
END LOOP;
END;
/

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:08.57
SCOTT@orclpdb1> EXEC DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'TMP',cascade=>true, no_invalidate=>false);

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:01.86
SCOTT@orclpdb1> select min(id),max(id),count(1) from tmp;

MIN(ID) MAX(ID) COUNT(1)
---------- ---------- ----------
50001 200000 150000

経過: 00:00:00.03

 

はい! 準備完了。

上記表へ、csvファイルというか外部表との差分を追加する処理をぐるぐる系と、ガツン系。
そして、おまけで、荒れ狂う実行計画のうるぐるぐる系(ヒントを使って最悪の実行計画にしてるだけですが)w 実行してみましょう。

特に落ちはないですが、打ちっぱなしというか、投げぱなしにしますので、
みなさん、遠ーーーくを見ながら、あーだーこーだビール片手のおつまみにでもして考えてみるのも良いか、と。。

 

まず、ぐるぐる系から。


SCOTT@orclpdb1> select min(id),max(id),count(id) from tmp;

MIN(ID) MAX(ID) COUNT(ID)
---------- ---------- ----------
50001 200000 150000

経過: 00:00:00.01
SCOTT@orclpdb1> exec rowbyrow_proc;

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:04.69
SCOTT@orclpdb1> select min(id),max(id),count(id) from tmp;

MIN(ID) MAX(ID) COUNT(ID)
---------- ---------- ----------
1 200000 200000

経過: 00:00:00.01

 

次は、ガツン系で。


SCOTT@orclpdb1> select min(id),max(id),count(id) from tmp;

MIN(ID) MAX(ID) COUNT(ID)
---------- ---------- ----------
50001 200000 150000

経過: 00:00:00.01
SCOTT@orclpdb1> exec allrows_batch_proc;

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.76
SCOTT@orclpdb1> select min(id),max(id),count(id) from tmp;

MIN(ID) MAX(ID) COUNT(ID)
---------- ---------- ----------
1 200000 200000

 

最後に、おまけ。 荒れ狂うぐるぐる系w。(繰り返し実行されるSQL文の実行計画が最悪という設定)
こうなっちゃた経験のある方も少ないないはず。荒れ狂わないように最良の実行計画に固定しちゃう予防策を取っておく方が安全ではあります。


SCOTT@orclpdb1> select min(id),max(id),count(id) from tmp;

MIN(ID) MAX(ID) COUNT(ID)
---------- ---------- ----------
50001 200000 150000

経過: 00:00:00.18
SCOTT@orclpdb1> exec badboy_rowbyrow_proc;

PL/SQLプロシージャが正常に完了しました。

経過: 00:28:02.30
SCOTT@orclpdb1> select min(id),max(id),count(id) from tmp;

MIN(ID) MAX(ID) COUNT(ID)
---------- ---------- ----------
1 200000 200000

経過: 00:00:00.31

 

それぞれの実行計画も載せておきますね。

ぐるぐる系のrowbyrow_procプロシージャで実行したSQL文
(実際に3つの方法で確認してますが、monitor/gather_plan_statisticsヒントつけちゃってるのに、autotraceの結果だけのせているのでヒントが気になるかたは外しても問題ないです。多少処理時間は速くなるはずですが。)


MERGE
/*+
monitor
gather_plan_statistics
index(tmp pk_tmp)
*/
INTO tmp
USING (
SELECT
:l_id AS id
FROM
dual
) diff_data
ON
( tmp.id = diff_data.id )
WHEN NOT MATCHED THEN
INSERT VALUES
(
diff_data.id
)
;

SCOTT@orclpdb1> l
1 MERGE
2 /*+
3 monitor
4 gather_plan_statistics
5 index(tmp pk_tmp)
6 */
7 INTO tmp
8 USING (
9 SELECT
10 :l_id AS id
11 FROM
12 dual
13 ) diff_data
14 ON
15 ( tmp.id = diff_data.id )
16 WHEN NOT MATCHED THEN
17 INSERT VALUES
18 (
19 diff_data.id
20* )
SCOTT@orclpdb1> /

1行がマージされました。

経過: 00:00:00.02

実行計画
----------------------------------------------------------
Plan hash value: 1857829212

-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 1 | 23 | 3 (0)| 00:00:01 |
| 1 | MERGE | TMP | | | | |
| 2 | VIEW | | | | | |
| 3 | NESTED LOOPS OUTER| | 1 | 23 | 3 (0)| 00:00:01 |
| 4 | VIEW | | 1 | 18 | 3 (0)| 00:00:01 |
| 5 | FAST DUAL | | 1 | | 3 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN| PK_TMP | 1 | 5 | 0 (0)| 00:00:01 |
-------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

6 - access("TMP"."ID"(+)=TO_NUMBER("DIFF_DATA"."ID"))

統計
----------------------------------------------------------
1 recursive calls
28 db block gets
3 consistent gets
3 physical reads
2432 redo size
204 bytes sent via SQL*Net to client
41 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

 

ガツン系(allrows_batch_procプロシージャで実行)


MERGE
/*+
monitor
gather_plan_statistics
index_ffs(tmp pk_tmp)
*/
INTO tmp
USING (
SELECT
id
FROM
csv_data
) diff_data
ON ( tmp.id = diff_data.id )
WHEN NOT MATCHED THEN
INSERT VALUES
(
diff_data.id
)
;

SCOTT@orclpdb1>
1 MERGE
2 /*+
3 monitor
4 gather_plan_statistics
5 index_ffs(tmp pk_tmp)
6 */
7 INTO tmp
8 USING (
9 SELECT
10 id
11 FROM
12 csv_data
13 ) diff_data
14 ON ( tmp.id = diff_data.id )
15 WHEN NOT MATCHED THEN
16 INSERT VALUES
17 (
18 diff_data.id
19* )
SCOTT@orclpdb1> /

50000行がマージされました。

経過: 00:00:00.60

実行計画
----------------------------------------------------------
Plan hash value: 1601203627

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 100K| 1757K| 289 (15)| 00:00:01 |
| 1 | MERGE | TMP | | | | |
| 2 | VIEW | | | | | |
|* 3 | HASH JOIN OUTER | | 100K| 976K| 289 (15)| 00:00:01 |
|* 4 | EXTERNAL TABLE ACCESS FULL| CSV_DATA | 100K| 488K| 57 (20)| 00:00:01 |
| 5 | INDEX FAST FULL SCAN | PK_TMP | 150K| 732K| 214 (6)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - access("TMP"."ID"(+)="ID")
4 - filter(SYS_OP_XTNN("CSV_DATA"."ID"))

統計
----------------------------------------------------------
44 recursive calls
101323 db block gets
593 consistent gets
0 physical reads
12691800 redo size
204 bytes sent via SQL*Net to client
41 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
50000 rows processed

 

荒れ狂うぐるぐる系badboy_rowbyrow_procプロシージャで実行したSQL文
計測回数が少ないので参考程度ですが、荒れ狂ってない場合の同一SQLと比較して、4倍も遅い状況。(一回当たりは 0.08秒でも積み上がると笑えないですね)


MERGE
/*+
monitor
gather_plan_statistics
index_ffs(tmp pk_tmp)
*/
INTO tmp
USING (
SELECT
:l_id AS id
FROM
dual
) diff_data
ON
( tmp.id = diff_data.id )
WHEN NOT MATCHED THEN
INSERT VALUES
(
diff_data.id
)
;

SCOTT@orclpdb1> l
1 MERGE
2 /*+
3 monitor
4 gather_plan_statistics
5 index_ffs(tmp pk_tmp)
6 */
7 INTO tmp
8 USING (
9 SELECT
10 :l_id AS id
11 FROM
12 dual
13 ) diff_data
14 ON
15 ( tmp.id = diff_data.id )
16 WHEN NOT MATCHED THEN
17 INSERT VALUES
18 (
19 diff_data.id
20* )
SCOTT@orclpdb1> /

1行がマージされました。

経過: 00:00:00.08

実行計画
----------------------------------------------------------
Plan hash value: 1739044970

----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 1 | 23 | 226 (10)| 00:00:01 |
| 1 | MERGE | TMP | | | | |
| 2 | VIEW | | | | | |
|* 3 | HASH JOIN OUTER | | 1 | 23 | 226 (10)| 00:00:01 |
| 4 | VIEW | | 1 | 18 | 3 (0)| 00:00:01 |
| 5 | FAST DUAL | | 1 | | 3 (0)| 00:00:01 |
| 6 | INDEX FAST FULL SCAN| PK_TMP | 150K| 732K| 214 (6)| 00:00:01 |
----------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - access("TMP"."ID"(+)=TO_NUMBER("DIFF_DATA"."ID"))

統計
----------------------------------------------------------
1 recursive calls
8 db block gets
503 consistent gets
494 physical reads
780 redo size
204 bytes sent via SQL*Net to client
41 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

 

 

本日利用したコードは以下の通り(csvファイルを作成したコードは略)


create directory rowbyrow_or_batch as '/home/oracle/rowbyrow_or_batch';
select directory_name, directory_path from dba_directories where directory_name = upper('rowbyrow_or_batch');

DROP TABLE csv_data PURGE;
CREATE TABLE csv_data
(
id NUMBER NOT NULL
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY rowbyrow_or_batch
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ','
(
id
)
)
LOCATION (
'data.csv'
)
);

DROP TABLE tmp PURGE;
CREATE TABLE tmp
(
ID NUMBER NOT NULL
, CONSTRAINT pk_tmp PRIMARY KEY (id) USING INDEX
);

BEGIN
FOR i in 50001..200000 LOOP
INSERT INTO tmp VALUES(i);
IF MOD(i,100) = 0 THEN COMMIT; END IF;
END LOOP;
END;
/

EXEC DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'TMP',cascade=>true, no_invalidate=>false);


CREATE OR REPLACE PROCEDURE allrows_batch_proc AS
BEGIN
MERGE
/*+
monitor
gather_plan_statistics
index_ffs(tmp pk_tmp)
*/
INTO tmp
USING (
SELECT
id
FROM
csv_data
) diff_data
ON ( tmp.id = diff_data.id )
WHEN NOT MATCHED THEN
INSERT VALUES
(
diff_data.id
);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/

CREATE OR REPLACE PROCEDURE rowbyrow_proc AS
C_DIR CONSTANT VARCHAR2(30) := 'ROWBYROW_OR_BATCH';
C_FILE_NAME CONSTANT VARCHAR2(30) := 'data.csv';
C_BUFFERSIZE CONSTANT BINARY_INTEGER := 32767;
C_OPENMODE CONSTANT VARCHAR2(2) := 'r';
fileHandle UTL_FILE.FILE_TYPE;
line VARCHAR2(32767);
l_id NUMBER;
rowcount NUMBER;
BEGIN
fileHandle := UTL_FILE.FOPEN(C_DIR, C_FILE_NAME, C_OPENMODE, C_BUFFERSIZE);
BEGIN
LOOP
UTL_FILE.GET_LINE(fileHandle, line, C_BUFFERSIZE);
l_id := TO_NUMBER(line);

MERGE
/*+
monitor
gather_plan_statistics
index(tmp pk_tmp)
*/
INTO tmp
USING (
SELECT
l_id AS id
FROM
dual
) diff_data
ON
( tmp.id = diff_data.id )
WHEN NOT MATCHED THEN
INSERT VALUES
(
diff_data.id
);

rowcount := rowcount + 1;
IF MOD(rowcount, 100) = 0 THEN
COMMIT;
NULL;
END IF;
END LOOP;

EXCEPTION
WHEN NO_DATA_FOUND THEN /* EOF */
NULL;
END;
COMMIT;
UTL_FILE.FCLOSE(fileHandle);
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
IF UTL_FILE.IS_OPEN(fileHandle) THEN
UTL_FILE.FCLOSE(fileHandle);
END IF;
RAISE;
END;
/

CREATE OR REPLACE PROCEDURE badboy_rowbyrow_proc AS
C_DIR CONSTANT VARCHAR2(30) := 'ROWBYROW_OR_BATCH';
C_FILE_NAME CONSTANT VARCHAR2(30) := 'data.csv';
C_BUFFERSIZE CONSTANT BINARY_INTEGER := 32767;
C_OPENMODE CONSTANT VARCHAR2(2) := 'r';
fileHandle UTL_FILE.FILE_TYPE;
line VARCHAR2(32767);
l_id NUMBER;
rowcount NUMBER;
BEGIN
fileHandle := UTL_FILE.FOPEN(C_DIR, C_FILE_NAME, C_OPENMODE, C_BUFFERSIZE);
BEGIN
LOOP
UTL_FILE.GET_LINE(fileHandle, line, C_BUFFERSIZE);
l_id := TO_NUMBER(line);

MERGE
/*+
monitor
gather_plan_statistics
index_ffs(tmp pk_tmp)
*/
INTO tmp
USING (
SELECT
l_id AS id
FROM
dual
) diff_data
ON
( tmp.id = diff_data.id )
WHEN NOT MATCHED THEN
INSERT VALUES
(
diff_data.id
);

rowcount := rowcount + 1;
IF MOD(rowcount, 100) = 0 THEN
COMMIT;
NULL;
END IF;
END LOOP;

EXCEPTION
WHEN NO_DATA_FOUND THEN /* EOF */
NULL;
END;
COMMIT;
UTL_FILE.FCLOSE(fileHandle);
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
IF UTL_FILE.IS_OPEN(fileHandle) THEN
UTL_FILE.FCLOSE(fileHandle);
END IF;
RAISE;
END;
/

Enjoy SQL!

 

今年は、桜吹雪を2回も見ることができたのですが、おそらく、20年ぶりw
では、また。

 


 

| | | コメント (0)

2022年7月 1日 (金)

explain plan文 De 索引サイズ見積 / FAQ

久々の投稿ですw

というか、Oracle ACEのKPIを考えるとどうしても、こうなってしまう大人の事情。

今期一発目の投稿は、意外と知られていない? explain plan文 De 索引サイズ見積。

統計情報などに依存はしますが、100億年に一度ぐらい、索引サイズどれぐらいになるかねぇ。みたいな聞かれかたしたときに、サクっとタイプして、ほれ!

と、Slackでなげかえしちゃって、飲みに行きましょうね。そこ必死にやるところじゃない時代なわけで。

では、21cもあるのですが、データ仕込むのめんどくさかったので、ありもの 19cの環境で試してみましょう。ちなみに、explain plan で索引サイズを見積もるのは私の記憶によると10gぐらいから使ってた記憶はあるので、昔からのOraclerだと知ってる方は多いはず。(もっと前からあるよーというツッコミ歓迎w)

表の存在とデータを大量に登録してあるtest表を使います。統計は最新化

[oracle@localhost ~]$ sqlplus scott/tiger

SQL*Plus: Release 19.0.0.0.0 - Production on Wed Jun 29 22:59:46 2022
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle. All rights reserved.

Last Successful login time: Mon Jun 06 2022 21:39:58 -04:00

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL> desc test
Name   Null? Type
------- ------------------------ -------- ----------------------------
NUM NUMBER

SQL> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'TEST',cascade=>true,no_invalidate=>false);

PL/SQL procedure successfully completed.

explain plan文でcreate index文を解析します。索引は作成されないので、躊躇なくタイプしちゃってくださいw
解析が終わったら、utlxpls.sqlを実行すれば見積もりサイズを確認できます。

10m行登録してるのでそれなりのサイズになるようですね。243MB という見積もりがでました!

SQL> explain plan for create index test on test(num);

Explained.

SQL> @?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2829245909

-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | CREATE INDEX STATEMENT | | 10M| 57M| 9958 (1) | 00:00:01 |
| 1 | INDEX BUILD NON UNIQUE| TEST | | | | |
| 2 | SORT CREATE INDEX | | 10M| 57M| | |
| 3 | TABLE ACCESS FULL | TEST | 10M| 57M| 4414 (2) | 00:00:01 |
-------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Note
-----
- estimated index size: 243M bytes

14 rows selected.

SQL>

Explain plan文以外では、使い勝手が悪いというかタイプする文字数多くて嫌いな、DBMS_SPACE.CREATE_INDEX_COST() があります。
以下のような無名PL/SQLブロックを書いておくか、あらかじめ俺俺関数(UDFね)として登録しておくと便利ですが、explain planでいいかなぁ。私はw

set serveroutput on
DECLARE
used_bytes NUMBER;
segment_bytes NUMBER;
BEGIN
DBMS_SPACE.CREATE_INDEX_COST (
ddl=> 'CREATE INDEX test ON test(num)'
, used_bytes => used_bytes
, alloc_bytes => segment_bytes
);
DBMS_OUTPUT.PUT_LINE('Segment Size (MB) :'||segment_bytes/1024/1024);
DBMS_OUTPUT.PUT_LINE('Index data Size (MB) :'||used_bytes/1024/1024);
END;
/

Explain planより情報量は多いですが、セグメントサイズがどれぐらいになるか知りたいわけなので、他の情報は捨てちゃうことが多い感じはします。
DBMS_SPACE.CREATE_INDEX_COST()パッケージプロシージャでは 232MB という見積もり結果となりました。

SQL> set serveroutput on
SQL> l
1 DECLARE
2 used_bytes NUMBER;
3 segment_bytes NUMBER;
4 BEGIN
5 DBMS_SPACE.CREATE_INDEX_COST (
6 ddl=> 'CREATE INDEX test ON test(num)'
7 , used_bytes => used_bytes
8 , alloc_bytes => segment_bytes
9 );
10 DBMS_OUTPUT.PUT_LINE('Segment Size (MB) :'||segment_bytes/1024/1024);
11 DBMS_OUTPUT.PUT_LINE('Index data Size (MB) :'||used_bytes/1024/1024);
12* END;
SQL> /
Segment Size (MB) :232
Index data Size (MB) :57.220458984375

PL/SQL procedure successfully completed.

SQL>

実際のセグメントサイズはどれぐらいでしょう? 実際に索引を作ったあとセグメントサイズをみてみました。

SQL> select segment_name,bytes/1024/1024 "MB" from user_segments where segment_name='TEST' and segment_type='INDEX';

SEGMENT_NAME MB
------------------------------ ----------
TEST 192

SQL>



今年も半年すぎたけど、アドベントカレンダー全部俺をやるべきか悩む。まとめてアウトプットするので、ちびちびアウトプットするのとどちらがよいか。。。w

では、また。

| | | コメント (0)

2022年3月21日 (月)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #13

随分間が空いてしまったのです、覚えてますかねw 前回のネタ。私もほぼ忘れてました orz. w

思い出しながら進めましょう:)

Previously on Mac De Oracle
前回は、CTASだと、内部的にdirect path writeされてしまうのでクリーンアウトが必要な状況にはならないという動きを確認しました。

scattered read同様に、sequential readだとどうなの?(という遠くから声が聞こえたきがしたのでw)

とはいえ、遅延ブロッククリーンアウトって、自分の理解だと、全表走査時の動きだと思っているので、多分、起きないよなーと。頭の上に雲型の吹き出した出た状態でイメージしているところ。


では、試してみましょう。

これまでと異なる箇所は、sequential read でぐるぐるループさせたいので、該当表に主キー索引を作成します。また、table full scanではなく、index unique scan で全行アクセスさせてみます。(この動きが大きく異なる部分です)





strong>0) 対象表のdrop/create と主キー作成
対象表のHOGE2は削除して作り直し。このテストケースでは主キーアクセスさせるため、合わせて主キーも作成しておきます。
SCOTT@orcl> @droppurge_create_hoge2_with_pk.sql
1* drop table hoge2 purge

Table dropped.

Elapsed: 00:00:00.51
1* create table hoge2 (id number, data varchar2(2000), constraint pk_hoge2 primary key (id) using index)

Table created.

Elapsed: 00:00:00.07
1* select segment_name,blocks from user_segments where segment_name like '%HOGE2%'

no rows selected

Elapsed: 00:00:00.13


1) 統計をクリアするためOracle再起動


$ sudo service oracle restart

[sudo] password for oracle:

Restarting oracle (via systemctl): [ OK ]



2) PDBのscottでログインしてclient_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションを特定するため。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('Target Session');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


3) CDBのSYSで統計取得(初回)
内容は省略! (思い出したいかたはこの辺りを参照ください。。


4) PDBのSCOTTユーザーでNOAPPENDヒント付きIASを実行(データサイズは、コミットクリーンアウトではクリーンアウト仕切れないサイズ)/ 未コミット

NOAPPENDヒントを利用している理由は前回までの内容を見ていただければわかりますが、direct path writeさせたないためです。これによりコミットクリーンアウトではクリーンアウトできない状況、つまり、遅延ブロッククリーンアウトが必要となる状態を作っています。

SCOTT@orcl> @ias_noappend_from_hoge.sql
1* insert /*+ noappend */ into hoge2 select * from hoge

200000 rows created.

Elapsed: 00:00:19.63
14:32:40 SCOTT@orcl>


5) CDBのSYSで統計取得(未コミット)

検証中に統計値が変動したもののみ記載

狙い通りdirect path writeは抑止できていることは確認できます。
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 22,918
sysstat DBWR checkpoints 72
sysstat DBWR thread checkpoint buffers written 22,918
sysstat DBWR transaction table writes 22
sysstat DBWR undo block writes 983
sysstat cleanouts only - consistent read gets 0
sysstat commit cleanout failures: block lost 0
sysstat commit cleanout failures: callback failure 0
sysstat commit cleanouts 1,601
sysstat commit cleanouts successfully completed 1,601
sysstat consistent gets 122,604
sysstat db block changes 564,554
sysstat deferred (CURRENT) block cleanout applications 12
sysstat free buffer requested 70,607
sysstat immediate (CR) block cleanout applications 0
sysstat immediate (CURRENT) block cleanout applications 1,752
sysstat no work - consistent read gets 67,108
sysstat physical reads 66,750
sysstat physical reads direct 66,709
sysstat physical writes 22,918
sysstat physical writes from cache 22,918
sysstat physical writes non checkpoint 22,918
sysstat redo blocks written 967,108
sysstat redo size 479,412,088
sysstat redo writes 378

(PDB) SCOTTのセッション統計
こちらから見てもdirect path writeは発生していません

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 0
sesstat commit cleanout failures: block lost 0
sesstat commit cleanouts 1,589
sesstat commit cleanouts successfully completed 1,589
sesstat consistent gets 122,204
sesstat db block changes 564,506
sesstat deferred (CURRENT) block cleanout applications 7
sesstat free buffer requested 70,595
sesstat immediate (CR) block cleanout applications 0
sesstat immediate (CURRENT) block cleanout applications 1,748
sesstat no work - consistent read gets 66,884
sesstat physical reads 66,740
sesstat physical reads direct 66,709
sesstat redo size 478,945,336


該当期間の待機イベント記録されていないですよね。狙い通りなので準備としては問題なしです。

EVENT                                                            WAIT_CLASS
---------------------------------------------------------------- -----------------
SQL*Net message from client Idle
log buffer space Configuration
events in waitclass Other Other
direct path read User I/O
log file switch (private strand flush incomplete) Configuration
log file switch completion Configuration
log file sync Commit
db file sequential read User I/O
SQL*Net message to client Network
Disk file operations I/O User I/O
db file scattered read User I/O

6) CDB or PDBのSYSユーザで接続し、buffer cacheをflushする

SYS@orclcdb> @flush_buffercache.sql
1* alter system flush buffer_cache

System altered.

Elapsed: 00:00:09.04
14:36:26 SYS@orclcdb>


7) CDBのSYSで統計取得(buffer cacheをflush後)
strong>(CDB)システム統計

検証中に統計値が変動したもののみ記載

バッファキャッシュをフラッシュしたのでキャッシュから書き出されたことを示す physical writes from cache および関連する統計値が上昇しています。これも想定通りの挙動ですね。

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 0
sysstat DBWR checkpoints 0
sysstat DBWR thread checkpoint buffers written 0
sysstat DBWR transaction table writes 8
sysstat DBWR undo block writes 1912
sysstat cleanouts only - consistent read gets 0
sysstat commit cleanout failures: block lost 0
sysstat commit cleanout failures: callback failure 0
sysstat commit cleanouts 43
sysstat commit cleanouts successfully completed 43
sysstat consistent gets 20793
sysstat db block changes 429
sysstat deferred (CURRENT) block cleanout applications 21
sysstat free buffer requested 1013
sysstat immediate (CR) block cleanout applications 0
sysstat immediate (CURRENT) block cleanout applications 9
sysstat no work - consistent read gets 12321
sysstat physical reads 977
sysstat physical reads direct 0
sysstat physical writes 47900
sysstat physical writes from cache 47900
sysstat physical writes non checkpoint 47900
sysstat redo blocks written 2017
sysstat redo size 994652
sysstat redo writes 23

(PDB) SCOTTのセッション統計

キャッシュをフラッシュしただけなので、該当セッションの統計には変化がありません。(まあ、そうですよねw)


SOURCE NAME VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistentread gets 0
sesstat commit cleanout failures: block lost 0
sesstat commit cleanouts 0
sesstat commit cleanouts successfully completed 0
sesstat consistent gets 0
sesstat db block changes 0
sesstat deferred (CURRENT) block cleanout applications 0
sesstat free buffer requested 0
sesstat immediate (CR) block cleanout applications 0
sesstat immediate (CURRENT) block cleanout applications 0
sesstat no work - consistent read gets 0
sesstat physical reads 0
sesstat physical reads direct 0
sesstat physical writes 0
sesstat physical writes direct 0
sesstat physical writes from cache 0
sesstat physical writes non checkpoint 0
sesstat redo blocks written 0
sesstat redo size 0

8) PDBのSCOTTユーザーでコミットの実行

SCOTT@orcl> commit;

Commit complete.

Elapsed: 00:00:00.01


9) CDBのSYSで統計取得(コミット後)
strong>(CDB)システム統計
コミット時点ではクリーンアウト仕切れないほどの更新ブロック数かつ、コミット前にバッファキャッシュをフラッシュしているので、コミット時のクリーンアウトもできない状態なっています(これも事前の準備の通りの結果。なので問題なし)
commit cleanouts と commit cleanout failures: block lost がポイントです。コミット時にクリーンアウトできる量(過去の記事を参照のこと)の閾値以下の量ですら、事前にキャッシュからフラッシュしまっているので、クリーンアウトに失敗(できないようにしたので当然ですが)しています。両方の統計値が同一であるとくことは、コミットクリーンアウトしようとした全てでくりアウトできなかった。理由はキャッシュに該当ブロックがキャッシュから落ちていたから(意図的に落としたわけですが)ということを意味します。予定通りですね。

コミットの時点で最低現おこなうクリーンアウトもさせていないですが、このケースでは 11140 ブロックをコミット時にクリーンアウトしようとしているので、コミット時のクリーンアウトが行われていたとしても 50,000 ブロック以上は遅延されていたことになりますね。今回は、 66,667 ブロックがク後続のSQLで参照されるついでにクリーンアウトされることになります。

検証中に統計値が変動したもののみ記載

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 0
sysstat DBWR checkpoints 0
sysstat DBWR thread checkpoint buffers written 0
sysstat DBWR transaction table writes 0
sysstat DBWR undo block writes 0
sysstat cleanouts only - consistent read gets 0
sysstat commit cleanout failures: block lost 11140
sysstat commit cleanout failures: callback failure 0
sysstat commit cleanouts 11140
sysstat commit cleanouts successfully completed 0
sysstat consistent gets 34
sysstat db block changes 1
sysstat deferred (CURRENT) block cleanout applications 0
sysstat free buffer requested 8
sysstat immediate (CR) block cleanout applications 0
sysstat immediate (CURRENT) block cleanout applications 0
sysstat no work - consistent read gets 5
sysstat physical reads 8
sysstat physical reads direct 0
sysstat physical writes 0
sysstat physical writes from cache 0
sysstat physical writes non checkpoint 0
sysstat redo blocks written 3
sysstat redo size 456
sysstat redo writes 3

(PDB) SCOTTのセッション統計
セッションレベルでみるよりはっきり見るとことができます。

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 0
sesstat commit cleanout failures: block lost 11140
sesstat commit cleanouts 11140
sesstat commit cleanouts successfully completed 0
sesstat consistent gets 0
sesstat db block changes 1
sesstat deferred (CURRENT) block cleanout applications 0
sesstat free buffer requested 1
sesstat immediate (CR) block cleanout applications 0
sesstat immediate (CURRENT) block cleanout applications 0
sesstat no work - consistent read gets 0
sesstat physical reads 1
sesstat physical reads direct 0
sesstat physical writes 0
sesstat physical writes direct 0
sesstat physical writes from cache 0
sesstat physical writes non checkpoint 0
sesstat redo blocks written 0
sesstat redo size 176


10) PDBのSCOTTユーザーで遅延ブロッククリーンアウト有無確認(対象表を主キーによる一意検索で全行アクセス)

さて、事前に想定している実行計画になっているかプランだけ確認しておきましょう。主キーによる索引ユニークスキャンであることが確認できます。想定通りです。この実行計画で、200,000 行をグルグル参照した場合、遅延されたブロッククリーンアウトが行われるでしょうか。。。。(してくれないと思いますが。。。試してみましょう)

SCOTT@orcl> set autot trace exp
14:41:47 SCOTT@orcl> select * from hoge2 where id = 1;
Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 3319133482

----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1015 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| HOGE2 | 1 | 1015 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_HOGE2 | 1 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ID"=1)

SCOTT@orcl> set autot off
SCOTT@orcl>
SCOTT@orcl> @table_unique_scan_hoge2.sql
1 begin
2 for i in 1..&1 loop
3 for hoge2_rec in (select * from hoge2 where id = i) loop
4 null;
5 end loop;
6 end loop;
7* end;
Enter value for 1: 200000
old 2: for i in 1..&1 loop
new 2: for i in 1..200000 loop

PL/SQL procedure successfully completed.

Elapsed: 00:00:14.94
14:39:36 SCOTT@orcl>


11) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認。対象表を主キーによる一意検索で全行アクセス)
strong>(CDB)システム統計

クリーンアウトが行われていれば、データブロックの総数 66,667 ブロックは少なくともクリーンアップの対象になっているはずですが。。。。。
cleanout系の統計値に動きはありませんよね。

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 21
sysstat DBWR checkpoints 0
sysstat DBWR thread checkpoint buffers written 0
sysstat DBWR transaction table writes 5
sysstat DBWR undo block writes 5
sysstat cleanouts only - consistent read gets 0
sysstat commit cleanout failures: block lost 0
sysstat commit cleanout failures: callback failure 15
sysstat commit cleanouts 366
sysstat commit cleanouts successfully completed 351
sysstat consistent gets 632775
sysstat db block changes 7848
sysstat deferred (CURRENT) block cleanout applications 169
sysstat free buffer requested 76662
sysstat immediate (CR) block cleanout applications 0
sysstat immediate (CURRENT) block cleanout applications 169
sysstat no work - consistent read gets 21822
sysstat physical reads 69501
sysstat physical reads direct 0
sysstat physical writes 21
sysstat physical writes from cache 21
sysstat physical writes non checkpoint 10
sysstat redo blocks written 5132
sysstat redo size 2536084
sysstat redo writes 28

(PDB) SCOTTのセッション統計

physical reads は、67706 ブロックなのでほ、HOGE2表の索引と表のセグメントサイズほどは物理読み込みされたということになります。ただ、cleanouts only - consistent read gets などのクリーンアウトを示す統計値はほぼ動いていません。ようするに、db file sequential readとなる index unique scan ではブロッククリーンアウトは発生しないということを意味しています。

おおおーーー。だったら、グルグル系の処理で一行ごとに処理するのが最高じゃん! という単純な話ではないので要注意ですからねw

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 0
sesstat commit cleanout failures: block lost 0
sesstat commit cleanouts 4
sesstat commit cleanouts successfully completed 4
sesstat consistent gets 600005
sesstat db block changes 14
sesstat deferred (CURRENT) block cleanout applications 2
sesstat free buffer requested 74540
sesstat immediate (CR) block cleanout applications 0
sesstat immediate (CURRENT) block cleanout applications 2
sesstat no work - consistent read gets 0
sesstat physical reads 67706
sesstat physical reads direct 0
sesstat physical writes 0
sesstat physical writes direct 0
sesstat physical writes from cache 0
sesstat physical writes non checkpoint 0
sesstat redo blocks written 0
sesstat redo size 2152


10) PDBのSCOTTユーザーで遅延ブロッククリーンアウト有無確認(sacattered read)

single block readを行うindex unique scanではクリーンアウトが発生しなかったということは、全表走査させる table full scan かつ scattered readさせれば発生するのではないでしょうか? という確認です。
auto traceで統計情報をみると、なにやら、REDOログが大量に生成されてますよね。。。これは!!!

SCOTT@orcl> @table_full_scan_hoge2.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.01
1* alter session set "_very_large_object_threshold" = 20400

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge2

200000 rows selected.

Elapsed: 00:00:06.55

Execution Plan
----------------------------------------------------------
Plan hash value: 1530105727

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 172K| 167M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE2 | 172K| 167M| 18223 (1)| 00:00:01 |
---------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)


Statistics
----------------------------------------------------------
10 recursive calls
1 db block gets
147157 consistent gets
377 physical reads
5866968 redo size
406775148 bytes sent via SQL*Net to client
147265 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off

14:56:56 SCOTT@orcl>


11) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認。対象表を主キーによる一意検索で全行アクセス)
strong>(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 1121
sysstat DBWR checkpoints 0
sysstat DBWR thread checkpoint buffers written 0
sysstat DBWR transaction table writes 36
sysstat DBWR undo block writes 347
sysstat cleanouts only - consistent read gets 66676
sysstat commit cleanout failures: block lost 0
sysstat commit cleanout failures: callback failure 0
sysstat commit cleanouts 806
sysstat commit cleanouts successfully completed 806
sysstat consistent gets 174296
sysstat db block changes 80773
sysstat deferred (CURRENT) block cleanout applications 383
sysstat free buffer requested 2095
sysstat immediate (CR) block cleanout applications 66676
sysstat immediate (CURRENT) block cleanout applications 75
sysstat no work - consistent read gets 27953
sysstat physical reads 1842
sysstat physical reads direct 0
sysstat physical writes 1121
sysstat physical writes from cache 1121
sysstat physical writes non checkpoint 981
sysstat redo blocks written 16550
sysstat redo size 8074260
sysstat redo writes 351

(PDB) SCOTTのセッション統計
HOGE2の表のクリーンアウト対象ブロック数、66,667 きっちり同じ数字が現れました。しかも cleanoutに関わる統計でかつ、大量のREDOログも生成されています。でました〜〜〜w クリーンアウト。全表走査かつ、scattered readで。

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 66667
sesstat commit cleanout failures: block lost 0
sesstat commit cleanouts 1
sesstat commit cleanouts successfully completed 1
sesstat consistent gets 153731
sesstat db block changes 66718
sesstat deferred (CURRENT) block cleanout applications 0
sesstat free buffer requested 690
sesstat immediate (CR) block cleanout applications 66667
sesstat immediate (CURRENT) block cleanout applications 0
sesstat no work - consistent read gets 16317
sesstat physical reads 680
sesstat physical reads direct 0
sesstat redo size 5877252




まとめ

index unique scanでグルグルさせつつ、全行アクセスしても発生しないですよね!
single block readになるunique index scanでは、遅延ブロッククリーンアウトは発生しない。(想像通りでよかったw)
で、そのあとで全表走査(scattered read)させてみると、やはり、遅延ブロッククリーンアウトは発生するわけですよ。面白いです。ほんと。



さあ、5月末まで追い込みだw ブログ書かないとw (頑張って、アドベントカレンダー全部俺やっとくほうが実は楽なのかもしれないw)

では、また。



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #6
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #7
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #8
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #9
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #10
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #11
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #12



| | | コメント (0)

2021年9月25日 (土)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #12

Previously on Mac De Oracle
前回、前々回とIASでdirect path writeであるか否かで、そのコミットおよび遅延ブロッククリーンアウトの有無が決まるという動きを確認しました。

今回は、DDLですが、親戚みたいな挙動のCTASではどうなるか確認しておきます。CTASはDDLなのでコミットは不要ですよね。また、 direct path writeが前提になっていることも皆さんご存知の通り。

ということは、IAS + APPENDヒントで、direct path write させた時と同じような挙動になるはず。。。。ですよね。

手順はいつもの図でご確認ください。(DDLなので手順も単純になっています)
Ctas_steps




0) 対象表のdrop
対象表のHOGE2は削除しておきます。CTASで作成することになるので。

SCOTT@orcl> @droppurge_hoge2.sql
1* drop table hoge2 purge

Table dropped.

Elapsed: 00:00:02.79


1) 統計をクリアするためOracle再起動

$ sudo service oracle restart


2) PDBのscottでログインしてclient_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションを特定するため。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('Target Session');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


3) CDBのSYSで統計取得(初回)
内容は省略!(ベースラインを取得しているだけなので)

SYS$orclcdb> @show_stat scott
...略...


4) PDBのSCOTTユーザーでCTAS(データサイズは、コミットクリーンアウトではクリーンアウト仕切れないサイズ

SCOTT@orcl> @ctas_from_hoge.sql
1* create table hoge2 as select * from hoge

Table created.

Elapsed: 00:00:07.41


5) CDBのSYSで統計取得(CTAS後)
DDLなのでコミットはありませんが、念の為に確認すると、commit cleanouts、commit cleanouts successfully completed はほんの少しだけ。このテストケースではノイズ程度の量です。
DDL終了時にクリーンアウトは発生していないと読み取れます。(この後の手順で遅延クリーンアウトも発生していなければ。direct path writeではクリーンアウトが必要な状態にはならないということは間違いないと判断するできますよね)

参考程度ですが、physical writes 、physical writes direct、physical writes non checkpoint が同数です。物理書き込みが発生し、かつ、direct path writeでチェックポイントで書き出されたものではないということが確認できます。想定通り、CTASは direct path writeで書き出されているということがわかります。
(HOGE表のデータが載っているブロック数は、66667 ブロックであることは以前確認した通りの値です)

また、physical reads と physical reads direct は、HOGE表のブロック数以上あり、HOGE表は direct path readでお読み込まれていることがわかります。このときのfree buffer requested は非常に低いことは、バッファキャッシュを介さず、ストレージへ書き出されていることを示しています。

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat commit cleanouts 21
sysstat commit cleanouts successfully completed 21
sysstat consistent gets 72654
sysstat db block changes 3898
sysstat deferred (CURRENT) block cleanout applications 7
sysstat free buffer requested 819
sysstat immediate (CURRENT) block cleanout applications 8
sysstat no work - consistent read gets 69569
sysstat physical reads 67150
sysstat physical reads direct 66709
sysstat physical writes 66667
sysstat physical writes direct 66667
sysstat physical writes non checkpoint 66667

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 21
sesstat commit cleanouts successfully completed 21
sesstat consistent gets 72366
sesstat db block changes 3898
sesstat deferred (CURRENT) block cleanout applications 7
sesstat free buffer requested 818
sesstat immediate (CURRENT) block cleanout applications 8
sesstat no work - consistent read gets 69415
sesstat physical reads 67149
sesstat physical reads direct 66709
sesstat physical writes 66667
sesstat physical writes direct 66667
sesstat physical writes non checkpoint 66667


6) PDBのSCOTTユーザーで遅延ブロッククリーンアウト有無確認(対象表をscattered readで全表走査)

IASで direct path writeさせた場合同様に、direct path writeで書き出された場合、クリーンアウトするブロックは存在しないため、遅延ブロッククリーンアウトも発生しないという状況になります。
REDOは多少生成あれていますが、recursive call 等によるものと考えられ、このテストケースではノイズの類程度です。システム/セッション統計値をみることでその点も確認できます。

SCOTT@orcl> @table_full_scan_hoge2.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 20400

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge2

200000 rows selected.

Elapsed: 00:00:06.66

Execution Plan
----------------------------------------------------------
Plan hash value: 1530105727

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200K| 382M| 18174 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE2 | 200K| 382M| 18174 (1)| 00:00:01 |
---------------------------------------------------------------------------

Statistics
----------------------------------------------------------
14 recursive calls
12 db block gets
80022 consistent gets
66669 physical reads
2108 redo size
406775148 bytes sent via SQL*Net to client
147265 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off


7) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認。対象表をscattered readで全表走査)
deferred (CURRENT) block cleanout applications や、immediate (CURRENT) block cleanout applicationsなど遅延ブロッククリーンアウトで動く統計に極わずかに動きがありますが、数ブロックなので気にする程度ではないです。遅延ブロッククリーンアウトは発生していないと読み取れます。

また、physical reads は該当表のブロック数程度のブロック数となっており、physical reads direct が発生していないことから、 scattered read でバッファキャッシュに載せられたことが確認できます。free buffer requested もほぼ同じ値になっていることからも同様のことが言えます。

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat commit cleanouts 16
sysstat commit cleanouts successfully completed 16
sysstat consistent gets 105725
sysstat db block changes 255
sysstat deferred (CURRENT) block cleanout applications 8
sysstat free buffer requested 67608
sysstat immediate (CURRENT) block cleanout applications 2
sysstat no work - consistent read gets 94138
sysstat physical reads 67567

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 86719
sesstat db block changes 43
sesstat deferred (CURRENT) block cleanout applications 3
sesstat free buffer requested 66791
sesstat immediate (CURRENT) block cleanout applications 2
sesstat no work - consistent read gets 82976
sesstat physical reads 66783




まとめ

CTASはDDLで、 direct path write を伴うため、IASのAPPEND同様、対象データブロックはクリーンアウトが必要な状態にはならず、コミットおよび遅延ブロッククリーンアウトは発生しない!
ことが確認できました。 :)

次回は、一旦中締めにしますか。

つづく。


目がショボショボしてるから、なにか浮遊物の影響を受けてる気がする。ブタクサやヨモギの季節だもんな。(目だけなんだよなー。アレルギーの酷方のように鼻水でたりとかではなくて。疲れ目用の目薬では、効かないw)



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #6
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #7
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #8
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #9
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #10
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #11



| | | コメント (0)

2021年9月24日 (金)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #11

Previously on Mac De Oracle
前回は、クリーンアウトの後始末をさせられる側ではなく、そもそも、後始末不要な状況もあるという確認でした。
IAS(Insert as Select)でdirect path writeで書き込まれたブロックは、クリーンアウトする必要がない状態なので、コミットクリーンアウトも遅延ブロッククリーンアウトも発生していないことはシステム統計やセッション統計からも明らかでした。

今回は、今一度確認ということで、IASでも非direct path writeだったら、やはりコミットクリーンアウトや遅延ブロッククリーンアウトは発生するよね! というところだけは見ておこうと思います。

手順は前回と同じ。前回と異なる点は、4) の部分。IASで NOAPPEND ヒントを使い direct path writeを抑止している部分のみ。
Iasstep





0) 対象表のdrop/create

SCOTT@orcl> @droppurge_create_hoge2.sql
1* drop table hoge2 purge

Table dropped.

Elapsed: 00:00:00.67
1* create table hoge2 (id number, data varchar2(2000))

Table created.

Elapsed: 00:00:00.05
1* select segment_name,blocks from user_segments where segment_name like '%HOGE2%'

no rows selected

Elapsed: 00:00:00.16


1) 統計をクリアするためOracle再起動

$ sudo service oracle restart


2) PDBのscottでログインしてclient_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションを特定するため。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('Target Session');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


3) CDBのSYSで統計取得(初回)
内容は省略!(ベースラインを取得しているだけなので)

SYS$orclcdb> @show_stat scott
...略...


4) PDBのSCOTTユーザーでIAS(データサイズは、コミットクリーンアウトではクリーンアウト仕切れないサイズ、コミットなし)
このケースでは 非direct path write でINSERTしたいので NOAPPEND ヒントで direct path write を抑止しています。

SCOTT@orcl> @ias_noappend_from_hoge.sql
1* insert /*+ noappend */ into hoge2 select * from hoge

200000 rows created.

Elapsed: 00:00:16.79


5) CDBのSYSで統計取得(APPENDヒント付きのIAS後、未コミット)

IASでHOGE表を読み込み、HOGE2表へ非direct path write している様子が確認できますよね。HOGE表の読み込みは direct path read 、HOGE2表へは 非direct path write していることが読み取れます。
free buffer requested で読み込みブロック相当のブロックがバッファキャッシュへ載せられているように見えますが、読み込みは direct path read なのでバッファキャッシュには載りません。ようするに、HOGE2表向けのデータと考えることができますよね。このキャッシュされたブロックが本当にHOGE2表向けブロックであるかどうかは、これ以降の操作で物理読み込みが発生しないということで確認することができます。


(HOGE表のデータが載っているブロック数は、66667 ブロックであることは前々回も確認した通りの値です)

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 19158
sysstat DBWR checkpoints 2
sysstat DBWR thread checkpoint buffers written 19158
sysstat DBWR transaction table writes 22
sysstat DBWR undo block writes 299
sysstat commit cleanouts 6
sysstat commit cleanouts successfully completed 6
sysstat consistent gets 110098
sysstat db block changes 443304
sysstat deferred (CURRENT) block cleanout applications 5
sysstat free buffer requested 67998
sysstat immediate (CURRENT) block cleanout applications 1
sysstat no work - consistent read gets 66823
sysstat physical reads 66730
sysstat physical reads direct 66709
sysstat physical writes 19158
sysstat physical writes from cache 19158
sysstat physical writes non checkpoint 19158
sysstat transaction tables consistent reads - undo records applied 2

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 109971
sesstat db block changes 443304
sesstat deferred (CURRENT) block cleanout applications 5
sesstat free buffer requested 67996
sesstat immediate (CURRENT) block cleanout applications 1
sesstat no work - consistent read gets 66781
sesstat physical reads 66730
sesstat physical reads direct 66709


6) PDBのSCOTTユーザーでコミットの実行

SCOTT@orcl> commit;

Commit complete.


7) CDBのSYSで統計取得(コミット後)
APPENDモードのIASと異なり、NOAAPENDモードでは、はやり、コミットクリーンアウトが発生しています。バッファキャッシュの10%-15%の範囲のブロックがコミット時にクリーンアウトされている状況が commit cleanouts successfully completed および、commit cleanouts から確認することができます。

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat commit cleanouts 55709
sysstat commit cleanouts successfully completed 55709
sysstat consistent gets 18547
sysstat db block changes 289
sysstat deferred (CURRENT) block cleanout applications 4
sysstat free buffer requested 791
sysstat no work - consistent read gets 10937
sysstat physical reads 758
sysstat transaction tables consistent reads - undo records applied 1

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 55700
sesstat commit cleanouts successfully completed 55700
sesstat db block changes 1


8) PDBのSCOTTユーザーで遅延ブロッククリーンアウト有無確認(対象表をscattered readで全表走査)

direct path writeと非direct path writeの違いはハッキリでました。REDOログは大量に生成され、遅延ブロッククリーンアウトは発生しているようです。
また、物理読み込みは発生していないので、バッファキャッシュにヒットしているという状況も読み取れます。

SCOTT@orcl> @table_full_scan_hoge2.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 20400

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge2

200000 rows selected.

Elapsed: 00:00:05.74

Execution Plan
----------------------------------------------------------
Plan hash value: 1530105727

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 172K| 167M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE2 | 172K| 167M| 18223 (1)| 00:00:01 |
---------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
19 recursive calls
13 db block gets
91462 consistent gets
2 physical reads
967388 redo size
406775148 bytes sent via SQL*Net to client
147265 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200000 rows processed
¥
set autot off


9) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認。対象表をscattered readで全表走査)
実行統計の示す通り、遅延ブロッククリーンアウトが発生していることは、immediate (CR) block cleanout applications、cleanouts only - consistent read getsのブロック数からもハッキリ確認することができます!(コミットクリーンアウトできなかったブロック数です)

physical readsはほんの少しありますが、発生していないとみなしても良い程度です。このケースはscattered readが発生していない。つまり、非direct path writeでバッファキャッシュに載ったブロックにヒットしていることで、scattered readの必要がなかった! ということを意味しています。もし、この時、バッファキャッシュから該当表のブロックがある程度落ちていれば、physical readsが表のブロック数程度まで増加していたはずです。(多数の同時実行トランザクションが存在する状況であればキャッシュからエージアウトされ、物理読み込みが大量に発生するというケースは珍しくありません。その分処理時間も長くなるわけで、程度とデータサイズサイズしだいで処理時間も延びることが予想できます。場合よっては、処理時間がかかりすぎて、ザワザワしたり。。。w)

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat cleanouts only - consistent read gets 10967
sysstat commit cleanouts 19
sysstat commit cleanouts successfully completed 19
sysstat consistent gets 98619
sysstat db block changes 11065
sysstat deferred (CURRENT) block cleanout applications 8
sysstat free buffer requested 180
sysstat immediate (CR) block cleanout applications 10967
sysstat immediate (CURRENT) block cleanout applications 5
sysstat no work - consistent read gets 72340
sysstat physical reads 169

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 10967
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 98015
sesstat db block changes 11011
sesstat deferred (CURRENT) block cleanout applications 2
sesstat free buffer requested 121
sesstat immediate (CR) block cleanout applications 10967
sesstat immediate (CURRENT) block cleanout applications 2
sesstat no work - consistent read gets 72013
sesstat physical reads 113




まとめ

IASで 非direct path write してINSERTされた場合、ブロッククリーンアウトが通常通り発生する(コミット時でも遅延でも)
direct path write時との統計値の違いからも明らかですね。

前回のIAS(APPEND)時の遅延ブロッククリーンアウトがなかったケースの統計を再掲しておきます。統計値の違いをよーく確認してみてください。(試験に。。。でないですけどw)

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat cleanouts only - consistent read gets 1
sysstat commit cleanouts 16
sysstat commit cleanouts successfully completed 16
sysstat consistent gets 105046
sysstat db block changes 225
sysstat deferred (CURRENT) block cleanout applications 9
sysstat free buffer requested 67576
sysstat immediate (CR) block cleanout applications 1
sysstat immediate (CURRENT) block cleanout applications 2
sysstat no work - consistent read gets 93908
sysstat physical reads 67536

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 1
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 86566
sesstat db block changes 44
sesstat deferred (CURRENT) block cleanout applications 3
sesstat free buffer requested 66778
sesstat immediate (CR) block cleanout applications 1
sesstat immediate (CURRENT) block cleanout applications 2
sesstat no work - consistent read gets 82891
sesstat physical reads 66770

次回は、CTAS ( create table as select ) ではどうなるか確かめます。(まだいくつかの関連統計を動かせていないケースありw)

次回へつづく。


東京は、自転車での移動や買い物のほうが渋滞や混雑のストレスがなくていいな。久々に coutry side側をドライブしてたが、ノーストレレスだった。一転機能は、買い物でドライブするも、いちいち渋滞w
WfHも年単位になると、すでに都心の満員電車や渋滞に耐えられない感じになってる気がしないでもないw (渋滞でスタックしている時間がもったいないw)



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #6
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #7
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #8
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #9
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #10



| | | コメント (0)

2021年9月22日 (水)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #10

Previously on Mac De Oracle
前回は、SELECT文であっても遅延ブロッククリーンアウトが発生すると該当ブロックは更新され、REDOログも生成される。ただし、direct path read で読み込まれた場合を除く。
という動きをみました。

今回もdirect pathがらみです。といっても direct path write だったら、コミットクリーンアウトや、遅延ブロッククリーンアウトはどういう扱いになるのだろう。。。と
(このシリーズの初回で紹介したいろいろなブログに答えはあるのですがw)

システム統計やセッション統計の統計値から、それをどう読み取るか。γGTP高いから肝臓あたりに問題があるか、検査前日に酒飲んじゃったでしょ! 的なところを診て、なにがおきているか診察していくシリーズなので、それぞれの統計と、auto traceによる実行計画と実行統計を診ていくわけですが。。w


ということで、今回は、前回利用していた表を元にIASで別表を作成し、コミット前後の状況を診ていきたいとおもいます。IASと言っても direct path write になるケースと従来型のロードがあるのは皆さんご存知だと思いますが、まずは、 direct path writeの方から挙動を診ていくことにします。手順はざっとこんな感じ
Iasstep

そういえば、以前、IASで、direct path write かどうか判別しやすくなったよねーというネタをやってましたね。実行計画だけでも違いがわかりやすくなっているので便利になりました。:)

実行計画は、SQL文のレントゲン写真だ! No.30より、LOAD TABL CONVENTIONAL vs. LOAD AS SELECTの実行計画の違い。
20210213-150833


20210213-150416





0) 対象表のdrop/create
SCOTT@orcl> @droppurge_create_hoge2.sql
1* drop table hoge2 purge

Table dropped.

Elapsed: 00:00:00.74
1* create table hoge2 (id number, data varchar2(2000))

Table created.

Elapsed: 00:00:00.07
1* select segment_name,blocks from user_segments where segment_name like '%HOGE2%'

no rows selected

Elapsed: 00:00:00.15

1) 統計をクリアするためOracle再起動

$ sudo service oracle restart


2) PDBのscottでログインしてclient_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションを特定するため。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('Target Session');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


3) CDBのSYSで統計取得(初回)
内容は省略!(ベースラインを取得しているだけなので)

SYS$orclcdb> @show_stat scott
...略...


4) PDBのSCOTTユーザーでIAS(データサイズは、コミットクリーンアウトではクリーンアウト仕切れないサイズ、コミットなし)
このケースでは direct path write でINSERTしたいので APPEND ヒントで direct path write を強制しています。 direct path read とは異なり、ヒントで制御できるのは楽w

SCOTT@orcl> @ias_from_hoge.sql
1* insert /*+ append */ into hoge2 select * from hoge

200000 rows created.

Elapsed: 00:00:10.42


5) CDBのSYSで統計取得(APPENDヒント付きのIAS後、未コミット)

IASでHOGE表を読み込み、HOGE2表へdirect path write している様子が確認できますよね。HOGE表の読み込みは direct path read 、HOGE2表へは direct path write している状況がはっきりでていてわかりやすい結果を得られました :)
(HOGE表のデータが載っているブロック数は、66667 ブロックであることは前回も確認した通りの値です)

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat commit cleanouts 16
sysstat commit cleanouts successfully completed 16
sysstat consistent gets 72460
sysstat db block changes 3131
sysstat deferred (CURRENT) block cleanout applications 12
sysstat free buffer requested 792
sysstat immediate (CURRENT) block cleanout applications 3
sysstat no work - consistent read gets 69328
sysstat physical reads 67121
sysstat physical reads direct 66709
sysstat physical writes 66667
sysstat physical writes direct 66667
sysstat physical writes non checkpoint 66667

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 16
sesstat commit cleanouts successfully completed 16
sesstat consistent gets 72373
sesstat db block changes 3131
sesstat deferred (CURRENT) block cleanout applications 12
sesstat free buffer requested 792
sesstat immediate (CURRENT) block cleanout applications 3
sesstat no work - consistent read gets 69291
sesstat physical reads 67121
sesstat physical reads direct 66709
sesstat physical writes 66667
sesstat physical writes direct 66667
sesstat physical writes non checkpoint 66667


6) PDBのSCOTTユーザーでコミットの実行

SCOTT@orcl> commit;

Commit complete.


7) CDBのSYSで統計取得(コミット後)
direct path writeでバッファキャッシュを経由せず書き出されたブロックはコミット時にはクリーンアウトの対象にはなっていないようですね。。。。統計をみる限りノイズ程度ですね。
ということは全てのブロックが遅延ブロッククリーンアウト対象になってしまうのか、または、その逆で、最初からクリーンアウト対象にもなっていないかということになります。次の全表走査の結果でどちらであるか、わかるはずです!!!!(ニヤニヤ、それ、ねらってやってるので、答えは知っているわけですがw)

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat commit cleanouts 25
sysstat commit cleanouts successfully completed 25
sysstat consistent gets 12104
sysstat db block changes 1557
sysstat deferred (CURRENT) block cleanout applications 12
sysstat free buffer requested 714
sysstat immediate (CURRENT) block cleanout applications 4
sysstat no work - consistent read gets 7192
sysstat physical reads 705

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 2
sesstat commit cleanouts successfully completed 2
sesstat consistent gets 864
sesstat db block changes 1474
sesstat deferred (CURRENT) block cleanout applications 1
sesstat free buffer requested 37
sesstat no work - consistent read gets 351
sesstat physical reads 32


8) PDBのSCOTTユーザーで遅延ブロッククリーンアウト有無確認(対象表をscattered readで全表走査)

REDOログは多少生成されていますが、実際に遅延ブロッククリーンアウトが発生した場合REDOログ量この程度では少なすぎますよね。
今回の検証目的からするとノイズの類ですね。むむむ。これは。。。。
(物理読み込みは発生しているので、direct path read か、scattered readのどちらかということにはなります。期待している動きは、Scattered read 。)

SCOTT@orcl> @table_full_scan_hoge2.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 20400

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge2

200000 rows selected.

Elapsed: 00:00:06.84

Execution Plan
----------------------------------------------------------
Plan hash value: 1530105727

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200K| 382M| 18174 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE2 | 200K| 382M| 18174 (1)| 00:00:01 |
---------------------------------------------------------------------------

Statistics
----------------------------------------------------------
13 recursive calls
12 db block gets
80022 consistent gets
66668 physical reads
2284 redo size
406775148 bytes sent via SQL*Net to client
147265 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off


9) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認。対象表をscattered readで全表走査)
physical reads は想定通りのブロック以上になっていますが、physical reads direct は発生していないので、scattered readによる全表走査であると読み取ることができます。
ただ、この状態でも、遅延ブロッククリーンアウトの発生を示す統計値はノイズ程度の値です。

つまり、direct path write でINSERTされたデータブロックはクリーンアウトが必要な状態だということになりますね。興味深い動きですよね。メモしておいたほうが良さそうです :)

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sysstat cleanouts only - consistent read gets 1
sysstat commit cleanouts 16
sysstat commit cleanouts successfully completed 16
sysstat consistent gets 105046
sysstat db block changes 225
sysstat deferred (CURRENT) block cleanout applications 9
sysstat free buffer requested 67576
sysstat immediate (CR) block cleanout applications 1
sysstat immediate (CURRENT) block cleanout applications 2
sysstat no work - consistent read gets 93908
sysstat physical reads 67536

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                           VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 1
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 86566
sesstat db block changes 44
sesstat deferred (CURRENT) block cleanout applications 3
sesstat free buffer requested 66778
sesstat immediate (CR) block cleanout applications 1
sesstat immediate (CURRENT) block cleanout applications 2
sesstat no work - consistent read gets 82891
sesstat physical reads 66770




まとめ

IASで direct path write してINSERTされた場合、ブロッククリーンアウトは発生しない!(コミット時でも遅延でも)
そもそもクリーンアウトが必要な状態になっていないというのが正しいのでしょうね。統計値としては全く動いてないに等しいので。

これも試験にでますよ(嘘w

次回は、同じIASもdirect path write ではないケースではどうなるでしょう。。。(ここまでのエントリーを読んでいる方は、結果は想像できそうではありますが)

次回へつづく。


遅めの夏休みですが、まあ、普段とちがうのは、いろいろなタスクの締め切りに追われずに、マイペースな時間の過ごし方になるぐらいだな。この状況下ではw



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #6
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #7
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #8
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #9



| | | コメント (0)

2021年9月21日 (火)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #9

Previously on Mac De Oracle
バッファキャッシュから溢れ出る程度のデータを登録しコミットした場合は、コミットクリーンアウトがどうなるか、遅延ブロッククリーンアウトされるブロック数はどの程度になるかという、寄り道でしたw

今回は、寄り道し過ぎて忘れるところだった、前回まで、scattered readを伴うtable full scanで発生していた遅延ブロッククリーンアウトが direct path read だったらどうなるか、というシリーズ :) 
(まだ続くのかーーーっ! はいw いろんなケースがありますから。シンプルなケースであっても。それぞれの基本的な挙動を知っいて損はないとおもいます。 複雑なケースだと脳汁出過ぎるくらい複雑なので考えたくもなくなるのでw)

では、早速再現してみましょう。手順はこれまで行なってきたとおりで、違いは遅延ブロッククリーンアウトを発生させるためのtable full scanでdirect path readさせるという部分のみ。手順はscattered readとの比較も入れるので長くなってしまうので、追加ステップを追記した図を見てもらうと何やっているか、流れは理解しやすいかもしれません。Steps


事前準備
バッファキャッシュのサイズは元のサイズに戻してあります

SYS@orclcdb> show sga

Total System Global Area 4294963960 bytes
Fixed Size 9143032 bytes
Variable Size 805306368 bytes
Database Buffers 3472883712 bytes
Redo Buffers 7630848 bytes

200,000行登録したデータ(セグメントサイズ農地純粋にデータが乗っているブロック数)が乗っているブロック数はこんなところ。セグメントサイズはこれより多いですよ。行データが載っているブロックだけカウントしているので。

SCOTT@orcl> select count(distinct dbms_rowid.rowid_block_number(rowid)) as "blocks" from hoge;

blocks
----------
66667

ということで、セグメントサイズも確認。

SCOTT@orcl> select segment_name,blocks from user_segments where segment_name = 'HOGE';

SEGMENT_NAME BLOCKS
------------------------------ ----------
HOGE 67584

バッファキャッシュの10-15%程度はコミットクリーンアウトされるので、間をとってこれぐらいはコミットクリーンアウトされる。。。

SCOTT@orcl> select 3472883712 / 8192 * 0.13 from dual;

3472883712/8192*0.13
--------------------
55111.68

残りは遅延ブロッククリーンアウトする。だいだいこんなもん。

SCOTT@orcl> select 66667 - 55112 from dual;

66667-55112
-----------
11555

direct path read狙いの全表走査(シリアル実行で発動させることを意図していますが、言うこときいてくれるかあなぁ)

$ cat table_full_scan_with_dpr.sql
alter session set "_serial_direct_read" = always
.
l
/
alter session set "_very_large_object_threshold" = 512
.
l
/

!echo set autot trace exp stat
set autot trace exp stat

select * from hoge
.
l
/

!echo set autot off
set autot off

Scattered Read狙いの全表走査のスクリプト

$ cat table_full_scan.sql
alter session set "_serial_direct_read" = never
.
l
/
alter session set "_very_large_object_threshold" = 20400
.
l
/

!echo set autot trace exp stat
set autot trace exp stat


select * from hoge
.
l
/

!echo set autot off
set autot off




さて、うまく再現できるかどうか。。(今回もやったことをほぼすべて載せているので長いです)


0) 対象表をdrop/create
オブジェクトを作り直し前提合せ

SCOTT@orcl> @droppurge_create_hoge

Table dropped.

Table created.

SCOTT@orcl> select segment_name,blocks from user_segments where segment_name like '%HOGE%';

no rows selected


1) 統計をクリアためOracle再起動

$ sudo service oracle restart


2) PDBのscottでログインしてclient_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションを特定するため。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


3) CDBのSYSで統計取得(初回)
内容は省略!(ベースラインを取得しているだけなので)

SYS$orclcdb> @show_stat scott
...略...


4) PDBのSCOTTユーザーでINSERT(データ量2倍、コミットなし)
データサイズはバッファキャッシュに載るサイズ、コミットクリーンアウトではクリーンアウト仕切れないサイズで、ある程度の遅延ブロッククリーンアウトが発生するサイズになっているのは以前と同じ。

SCOTT@orcl> @insert_each_rows_2
1* begin for i in 1..200000 loop insert into hoge values(i, lpad('*', 2000, '*')); end loop; end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:29.48
SCOTT@orcl>


5) CDBのSYSで統計取得(INSERT後、未コミット)
insertしただけなので、insertしたデータ量に応じたブロック数がバッファキャッシュに確保されたという程度の情報( free buffer requested = 68766 なので事前に確認していた 66667 以上になっています)は確認できます。
(バッファキャッシュに収まるデータ量ですし)ただ、checkpointの発生でいくらか書き出されているのも見えますよね( DBWR checkpoint buffers written )

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 22909
sysstat DBWR checkpoints 3
sysstat DBWR thread checkpoint buffers written 22902
sysstat DBWR transaction table writes 6
sysstat DBWR undo block writes 573
sysstat commit cleanouts 6
sysstat commit cleanouts successfully completed 6
sysstat consistent gets 49682
sysstat db block changes 744727
sysstat deferred (CURRENT) block cleanout applications 4
sysstat free buffer requested 68766
sysstat immediate (CURRENT) block cleanout applications 1
sysstat no work - consistent read gets 82
sysstat physical reads 9
sysstat physical writes 22909
sysstat physical writes from cache 22909
sysstat physical writes non checkpoint 22892

(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 49561
sesstat db block changes 744727
sesstat deferred (CURRENT) block cleanout applications 4
sesstat free buffer requested 68766
sesstat immediate (CURRENT) block cleanout applications 1
sesstat no work - consistent read gets 40
sesstat physical reads 9


6) PDBのSCOTTユーザーでコミットの実行

SCOTT@orcl> commit;

Commit complete.


7) CDBのSYSで統計取得(コミット後)
コミットクリーンアウトされているブロック数を見ると、事前に計算していた バッファキャッシュの13%( 55112 blocks )に近い 55700 ブロックがコミットのタイミングでクリーンアウトされていることがわかります。ここまでは想定通りの動きです。

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat commit cleanout failures: callback failure 15
sysstat commit cleanouts 56077
sysstat commit cleanouts successfully completed 56062
sysstat consistent gets 50494
sysstat db block changes 7894
sysstat deferred (CURRENT) block cleanout applications 158
sysstat free buffer requested 3217
sysstat immediate (CURRENT) block cleanout applications 154
sysstat no work - consistent read gets 32764
sysstat physical reads 2962

(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 55700
sesstat commit cleanouts successfully completed 55700
sesstat db block changes 1


8) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表をdirect path readで全表走査)
全表走査させてコミットクリーンアウトされなかったブロックがクリーンアウトを確認します。ただし、全表走査ではありますが、direct path read で読み込ませるように工夫しています。

さて狙い通りになるかどうか。。。パラレルクエリーでない場合の強制はちょいとむずいのですが、見る限り、REDOログは生成されています。

ただ、以前のscattered read ( db file sequential read )で発生させた遅延ブロッククリーンアウトの検証結果に比べると明らかに少ない。。
なにかが違いますね。。。。むむむ。なんだろう?
scattered readでほぼ同じバッファキャッシュサイズで、遅延ブロッククリーンアウトさせた際、967432 redo size というサイズが生成されていたのを思い出してみてください!!! 明らかに少ないです。。。。

SCOTT@orcl> @table_full_scan_with_dpr.sql
1* alter session set "_serial_direct_read" = always

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 512

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

200000 rows selected.

Elapsed: 00:00:10.84

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 214K| 207M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 214K| 207M| 18223 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
23 recursive calls
13 db block gets
113559 consistent gets
66712 physical reads
2996 redo size
4539159 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off


9) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)(対象表をdirect path readで全表走査)
direct path read、遅延ブロッククリーンアウトの有無をシステム統計およびセッション統計から読み取ってみます!

物理読み込み( physical writes )とダイレクトパスリードを示す( physical writes from cache )が同じであることから、間違いなく direct path readが発生しています。ブロック数も 66667 ブロックを超えていることは確認できます。
ただ、immediate (CR) block cleanout applications が想定している量の3倍ぐらいあります:)
遅延ブロッククリーンアウトは行われているのは間違いないですが、前述の通りREDOサイズが異常に少ない。どういうことだろう。。(想定通りの結果に、ニヤニヤなわけですがw)

真相を探るため、われわれはアマゾンの奥深くへ入っていくのであった。。。W

少々本題からそれますが、DBWR parallel query checkpoint buffers written で 44793 ブロックほど書き出されています。これが発生するのは direct path readの影響です。direct path read バッファキャッシュを介ず、常にストレージからデータを読み込む必要があります。この検証では、INSERTでバッファキャッシュに載っているデータであるため一旦書き出す必要があります。書き出されたデータを direct path read で読み込むのでこんな動きになっているというわけですね。。。。。

これ、よくよく考えると、コミットクリーンアウトされていないブロックもそのままの状態で書き出されてますよね。。。ここ試験にでますよ(嘘ですw

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 44793
sysstat DBWR checkpoints 2
sysstat DBWR object drop buffers written 2
sysstat DBWR parallel query checkpoint buffers written 44793
sysstat cleanouts only - consistent read gets 33057
sysstat commit cleanouts 18
sysstat commit cleanouts successfully completed 18
sysstat consistent gets 120519
sysstat db block changes 97
sysstat deferred (CURRENT) block cleanout applications 8
sysstat free buffer requested 127
sysstat immediate (CR) block cleanout applications 33057
sysstat immediate (CURRENT) block cleanout applications 5
sysstat no work - consistent read gets 50145
sysstat physical reads 66827
sysstat physical reads direct 66710
sysstat physical writes 44795
sysstat physical writes from cache 44795
sysstat physical writes non checkpoint 44795

(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 33057
sesstat commit cleanouts 5
sesstat commit cleanouts successfully completed 5
sesstat consistent gets 120131
sesstat db block changes 48
sesstat deferred (CURRENT) block cleanout applications 1
sesstat free buffer requested 120
sesstat immediate (CR) block cleanout applications 33057
sesstat immediate (CURRENT) block cleanout applications 2
sesstat no work - consistent read gets 49952
sesstat physical reads 66823
sesstat physical reads direct 66710


10) Oracle再起動
Oracleを再起動して、諸々綺麗にした状態で、今一度、direct path readで全表走査させてみましょう。

$ sudo service oracle restart


11) PDBのscottでログインしてclient_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションを特定するため。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


12) CDBのSYSで統計取得(再起動後初回)
内容は省略!(ベースラインを取得しているだけなので)

SYS$orclcdb> @show_stat scott
...略...


13) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表をdirect path readで全表走査)2回目
お!!! REDOが生成されていないですね。Scattered Readの場合でも、コミット時でも一度クリーンアウトされたブロックはクリーンアウト済みなので、クリーンアウトされるような挙動は発生しませんでしたが、 direct path read でもおなじかなーー。

と。。。。とりあえず、システム統計とセッション統計も確認しておきましょう!

SCOTT@orcl> @table_full_scan_with_dpr.sql
1* alter session set "_serial_direct_read" = always

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 512

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

200000 rows selected.

Elapsed: 00:00:03.90

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 214K| 207M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 214K| 207M| 18223 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
33 recursive calls
0 db block gets
91159 consistent gets
66938 physical reads
0 redo size
4539159 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off


14) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)(対象表をdirect path readで全表走査)2回目
セッション統計のphysical readsとhysical reads direct は同一であることから direct path readになっていることは間違いありません。また、ブロック数も 66667 ブロック以上にはなっているので全ブロック読み込まれているようですね。
ただ、妙な値を示している統計があります。

immediate (CR) block cleanout applications  10959 

遅延ブロッククリーンアウトが行われている時に上がる統計です。しかも、コミットクリーンアウトされたブロック数を差し引いたブロック数にほぼ一致します。(1回目の実行ではこの3倍ぐらいに跳ね上がっていましたが。。一度クリーンアウトされたのでは??)

さらに不思議なことに、REDO生成されないんですね。。。。

ん? ちょっと待ってください。一度、クリーンアウトされたブロックがなぜ、再度クリーンアウトされているのでしょう? scattered readで遅延ブロッククリーンアウトされたケースと動きが違います!!!!!!

Oracleを再起動する前のステップでREDOログが異常に少ないにも関わらず、遅延クリーンアウトされていた統計値が高くなった。Oracleを再起動した後でも、同様に、direct path read で読み込み、遅延ブロッククリーンアウト発生。しかもREDOログはありません。。。これって、クリーンアウト行われているようですが、実際にはメモリー上だけで実祭のブロックはクリーンアウトされずに残っているということですよね。なんども発生しているわけですから。
(ブロックダンプしなくても統計値から状況は見えてきましたよね!!)

direct path readはその名の通り、バッファキャッシュを介さず、常にストレージからデータブロックを読み込み、PGAへ。このケースだとSELECT文なので単純にPGAへ直接読み込み、メモリ上ではクリーンアップは行なっているようですが、クエリーが終了すれば、単に捨てられるのみ。。。なので、クリーンアウトの結果は永続化されない。。。ということになりますよね!

ということは、SELECT文の場合は、scattered read等でバッファキャッシュを経由させないと、遅延ブロッククリーンアウトは、ずーっと先延ばしされる。。。direct path readのSELECT文を2回実行してクリーンアウトさせたわけだが、この後、scattered readで全表走査させれば、遅延ブロッククリーンアウトが発生して、大量のREDOログが生成されるて、完全にクリーンアウトされる。。。。。。はず。。。ですよね。

試してみよう!!!!

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoints 1
sysstat DBWR object drop buffers written 2
sysstat cleanouts only - consistent read gets 10959
sysstat commit cleanouts 36
sysstat commit cleanouts successfully completed 36
sysstat consistent gets 106766
sysstat db block changes 166
sysstat deferred (CURRENT) block cleanout applications 18
sysstat free buffer requested 1130
sysstat immediate (CR) block cleanout applications 10959
sysstat immediate (CURRENT) block cleanout applications 10
sysstat no work - consistent read gets 77392
sysstat physical reads 67827
sysstat physical reads direct 66710
sysstat physical writes 2
sysstat physical writes from cache 2
sysstat physical writes non checkpoint 2

(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 10959
sesstat commit cleanouts 2
sesstat commit cleanouts successfully completed 2
sesstat consistent gets 98223
sesstat db block changes 30
sesstat deferred (CURRENT) block cleanout applications 1
sesstat free buffer requested 587
sesstat immediate (CR) block cleanout applications 10959
sesstat no work - consistent read gets 72324
sesstat physical reads 67289
sesstat physical reads direct 66710


15) Oracle再起動
諸々情報を綺麗にするので再起動!!

$ sudo service oracle restart


16) PDBのscottでログインしてclient_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションを特定するため。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


17) CDBのSYSで統計取得(再々起動後初回)
内容は省略!(ベースラインを取得しているだけなので)

SYS$orclcdb> @show_stat scott
...略...


18) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表をscattered readで全表走査)3回目

キターーーーーーーーーーーーーーっ!。 予想的中!!。(競馬ならいいのにw) 

大量のREDOログが生成され、物理読み込みも初生しています。direct path readで全表走査させた時とは明らかに違う!!!(以前、見た光景!!w

SCOTT@orcl> @table_full_scan.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 20400

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

200000 rows selected.

Elapsed: 00:00:04.84

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 214K| 207M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 214K| 207M| 18223 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
33 recursive calls
0 db block gets
91170 consistent gets
66720 physical reads
964436 redo size
4539159 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off


19) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)(対象表をscattered readで全表走査)3回目

physical reads が想定ブロック数以上あるため、物理読み込みされ全ブロックが読み込まれていると読み取れます。また、physical reads direct は変化していません。(変化のない統計は記載していません)
つまり direct path read ではなく scattered read で全表走査が行われたことを示しています。

immediate (CR) block cleanout applications                     10959

という統計から、遅延ブロッククリーンアウトが発生し、ほぼ想定していたブロック数であることも確認できます。

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat cleanouts only - consistent read gets 10959
sysstat commit cleanouts 3
sysstat commit cleanouts successfully completed 3
sysstat consistent gets 98846
sysstat db block changes 10992
sysstat deferred (CURRENT) block cleanout applications 2
sysstat free buffer requested 67078
sysstat immediate (CR) block cleanout applications 10959
sysstat no work - consistent read gets 72660
sysstat physical reads 67069

(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat cleanouts only - consistent read gets 10959
sesstat commit cleanouts 2
sesstat commit cleanouts successfully completed 2
sesstat consistent gets 98234
sesstat db block changes 10988
sesstat deferred (CURRENT) block cleanout applications 1
sesstat free buffer requested 67071
sesstat immediate (CR) block cleanout applications 10959
sesstat no work - consistent read gets 72324
sesstat physical reads 67063


20) Oracle再起動
以前の検証で、scattered readでブロッククリーンアウトされた場合のSELECT文であっても結果は永続化されるので、再度読み込ませた場合はクリーンアウト済みなので再度遅延クリーンアウトが発生しないことは確認確認済みですが、今一度確認しておきましょうw

$ sudo service oracle restart


21) PDBのscottでログインしてclient_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションを特定するため。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


22) CDBのSYSで統計取得(再再々起動後初回)
内容は省略!(ベースラインを取得しているだけなので)

SYS$orclcdb> @show_stat scott
...略...


23) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表をscattered readで全表走査)4回目

想定通り、遅延ブロッククリーンアウトは発生せず、REDOログも生成されていません! めでたしめでたしw

SCOTT@orcl> @table_full_scan.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.01
1* alter session set "_very_large_object_threshold" = 20400

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

200000 rows selected.

Elapsed: 00:00:04.32

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 214K| 207M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 214K| 207M| 18223 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
33 recursive calls
0 db block gets
80211 consistent gets
66719 physical reads
0 redo size
4539159 bytes sent via SQL*Net to clientyoutub
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off

24) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)(対象表をscattered readで全表走査)4回目
physical reads はありますが、 physical reads direct は変化していません。これは scattered read で全データを読み込んだと見て良いでしょうね。読み込んだブロックサイズも該当表の想定データブロック数程度です。
また、遅延ブロッククリーンアウトが発生したことを示す統計は変化していないことから、遅延ブロッククリーンアウトは発生していないことも読み取れます。(^^)

統計値が変動したもののみ記載
(CDB)システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat commit cleanouts 3
sysstat commit cleanouts successfully completed 3
sysstat consistent gets 87659
sysstat db block changes 34
sysstat deferred (CURRENT) block cleanout applications 2
sysstat free buffer requested 67058
sysstat no work - consistent read gets 83510
sysstat physical reads 67048

(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 2
sesstat commit cleanouts successfully completed 2
sesstat consistent gets 87049
sesstat db block changes 30
sesstat deferred (CURRENT) block cleanout applications 1
sesstat free buffer requested 67051
sesstat no work - consistent read gets 83175
sesstat physical reads 67042



まとめ

SELECT文であっても、遅延ブロッククリーンアウトが発生すると該当ブロックは更新され、REDOログが生成される。ただし、direct path read で読み込まれた場合を除く。
ということのようですね。

ふむふむという興味深い動きですよね。これ。:)

では、次回は direct path に関わる別の動きも確認してみましょうか。。。
このシリーズ、まだまだ引っ張れそうw

ということで次回へつづく。


台風の影響を心配したけどなんとか良い天気の遅い夏休みで。暑くも寒くもないく散歩の気持ちいい秋空 :)



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #6
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #7
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #8



| | | コメント (0)

2021年9月14日 (火)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #8

Previously on Mac De Oracle
前回からのつづき(ちょいと寄り道中)
です。

では、とっとと試してみましょうw

バッファキャッシュから溢れ出る程度のデータ量だったら、どうなるのかなーーーー、という検証です。やりたいことは図の通りです。
Photo_20210911234101

検証方法を考えていたのですが、自動共有メモリー管理になっているのと、sga_max_size/sga_targetを小さくしすぎるとOracle Databaseが起動しないなど諸々引きそうなので、shared_pool_sizeを大きく設定して、バッファキャッシュに回せるメモリーを減らすことで、バッファキャッシュを小さく、バッファから溢れる程度のデータ量も少なくて済むようにして試してみることにします。

準備段階から書いてます。再現させる環境をどうセットアップしたかっていうことも重要だと思うのですよね。少々長くなっちゃいますが。



検証準備

CDBに接続して初期化パラメータを調整!!

SGAコンポーネントの状況
Database Buffersが、3G以上になってます。検証データ量も多くなってしまうので、これを1GB程度まで下げたいですね。検証時間も節約できますし、最小の手数で検証できるほうが良いですから:)

SYS@orclcdb> show sga
Total System Global Area 4294963960 bytes
Fixed Size 9143032 bytes
Variable Size 805306368 bytes
Database Buffers 3472883712 bytes
Redo Buffers 7630848 bytes

SYS@orclcdb> select 3472883712 / 1024 / 1024 AS "MB" from dual;

MB
----------
3312

Elapsed: 00:00:00.00


sga_max_size,sga_min_sizeが4GBですが、ここはそのままにします。あまり小さくしすぎると起動しなくなったり。(^^;;;;

SYS@orclcdb> show parameter sga
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
allow_group_access_to_sga boolean FALSE
lock_sga boolean FALSE
pre_page_sga boolean TRUE
sga_max_size big integer 4G
sga_min_size big integer 0
sga_target big integer 4G
unified_audit_sga_queue_size integer 1048576


自動SGA管理なので、Shared Pool Sizeに大きめの値を設定。
自動SGA管理下で自動管理対象メモリーコンポーネントパラメータに値を設定した場合、その値が下限値となり最低でもその値は確保されるという仕組みを利用します!

SYS@orclcdb> show parameter shared_pool

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
shared_pool_reserved_size big integer 39426457
shared_pool_size big integer 0


起動しなくなっても戻しやすいようにspfileをpfileに書き出して退避後に、shared_pool_sizeを3GBへ増やします。これで4Gのうちの3G程度がshared poolに割り当てられ、バッファキャッシュは1GBぐらいになるはず。(VirtualBoxなのでスナップショット取得しておいて戻すのもありですけどw)

SYS@orclcdb> create pfile='pfile20210912.ora' from spfile;

File created.

Elapsed: 00:00:00.00
SYS@orclcdb> alter system set shared_pool_size = 3g scope=both;

System altered.

Elapsed: 00:00:00.77


Database Buffersがいい感じにシュリンクしました。1GBぐらいになりました。これで進めますよー。

SYS@orclcdb> show sga
Total System Global Area 4294963960 bytes
Fixed Size 9143032 bytes
Variable Size 3238002688 bytes
Database Buffers 1040187392 bytes
Redo Buffers 7630848 bytes

SYS@orclcdb> select 1040187392 / 1024 / 1024 AS "MB" from dual;

MB
----------
992

Elapsed: 00:00:00.01


ざっくりとブロック数を計算

SYS@orclcdb> select 1040187392 / 8192 AS "blocks" from dual;

blocks
---------------
126976

Elapsed: 00:00:00.01


前回のHOGE表に200,000 rowsで、66,667 blocks のデータを生成したので、126,976 blocks を満たすデータ量にしようとすると 400,000 rowsほど必要になりそうですね。。。。少々多めで、バッファキャッシュから溢れる程度の量で、 500,000 rowsのデータを登録することにしましょう!!!!

SYS@orclcdb> select ceil(126976 / 66667) * 200000 AS "rows" from dual;

rows
----------
400000

Elapsed: 00:00:00.00


これまでの検証から 10%-15%程度がCOMMITクリーンアウトされ、残りが遅延されるのは確認できたので、126976 blocks のバッファキャッシュだと、 17,777 blocks ぐらいがコミットクリーンアウトされそうですね。(今回のテストケースではコミットクリーンアウトされないけど。。された場合は最大でこの程度。。。というメモです。はい)

SYS@orclcdb> select ceil(126976 * 0.14) AS "blocks for commit cleanout" from dual;

blocks for commit cleanout
--------------------------
17777

Elapsed: 00:00:00.00

前回作成したデータは、200,000rowsで、66,667 blocksだったので、500,000 rows だと、ざっくり 166,668 blocks ほど。

SCOTT@orcl> select ceil(66667 / 2 * 5) AS "blocks" from dual;

blocks
----------
166668

Elapsed: 00:00:00.00


なので、遅延ブロッククリーンアウトされると想定される(コミットクリーンアウト分を覗くと)ブロック数は、148,891 blocks 程度にはなりそう。

SYS@orclcdb>  select ceil((66667 / 2 * 5) - 17777) AS "blocks" from dual;
blocks
----------
148891

Elapsed: 00:00:00.01


それに加えて、バッファキャッシュに収まらず、コミットする前にバッファキャッシュから落とされ、ストレージへかきだされてしまうブロック数は、これまた、ざっくり計算すると 39,692 blocks ほどですかね。バッファキャッシュのサイズから全てのブロックは乗り切らないので、最初に読み込まれていたブロックから落とされていくことにはなりますね。。
とはいえ、この検証ではキャッシュ落とされるブロック数は特に気にしてなくて、バッファキャッシュ以上のブロック数が生成されていればいいので、落とされそうなのが確認できればOK.

SYS@orclcdb>  select ceil((66667 / 2 * 5) - 126976) AS "blocks" from dual;

blocks
----------
39692

Elapsed: 00:00:00.00


とりあえず、生成するデータ量(行数)の算出とバッファキャッシュサイズの調整はおわり。


次に、実際にデータを生成してブロック数とセグメントサイズを確認して、実行用スクリプトの調整を行なっておきます。



PDBでテストデータの実サイズの確認
SCOTT@orcl> @droppurge_create_hoge.sql
1* drop table hoge purge

Table dropped.

Elapsed: 00:00:00.25
1* create table hoge (id number, data varchar2(2000))

Table created.

Elapsed: 00:00:00.04
1* select segment_name,blocks from user_segments where segment_name like '%HOGE%'

no rows selected

Elapsed: 00:00:00.11


500,000行登録!!

SCOTT@orcl> @insert_each_rows_5.sql
1* begin for i in 1..500000 loop insert into hoge values(i, lpad('*', 2000, '*')); end loop; end;

PL/SQL procedure successfully completed.

Elapsed: 00:02:37.68


データが登録されているブロック数は、166,667 blocks で、事前に計算していた 166,668 blocks にほぼおなじ。(狙い通り)
セグメントサイズは、約 1344 MB ですね。

SCOTT@orcl> select count(distinct dbms_rowid.rowid_block_number(rowid)) as "blocks" from hoge;

blocks
----------
166667

Elapsed: 00:00:05.03
SCOTT@orcl> select segment_name,blocks,bytes/1024/1024 AS "MB" from user_segments where segment_name = 'HOGE';

SEGMENT_NAME BLOCKS MB
------------------------------ ---------- ----------
HOGE 172032 1344

Elapsed: 00:00:00.17


Scattered read でTable Full Scanできるように少々隠しパラメータを調整しておきますね。念の為。(セッションレベルで調整してます)
セグメントサイズが、1344 MBなので、_very_large_object_threshold は、2048 MBぐらい設定しておけば、Scattered readのまま行けそうですね。

$ cat table_full_scan.sql
alter session set "_serial_direct_read" = never
.
l
/
alter session set "_very_large_object_threshold" = 2040
.
l
/

!echo set autot trace exp stat
set autot trace exp stat


select * from hoge
.
l
/

!echo set autot off
set autot off


準備完了!!!!!





準備長かったけどw やっと本題です!!!w 実行している内容はいままでと同じなのでかなり端折ってポイントだけ記載。

バッファキャッシュから溢れるほどのデータ量で。コミットクリーンアウトはどうなるのだろうか。。。。想定では、ほぼコミットクリーンアウトできないはずではあるのだが。。。。

CDBのSYSで統計取得(コミット後)

commit cleanouts successfully completedはどれぐらいだったのか。。。。。ありません。commit cleanouts successfully completedに差分がなかったので、コミットクリーンアウトしようして失敗、commit cleanout failures: block lostと同数なので、1ブロックもコミットクリーンアウトできない!  commit cleanout failures: block lostがバッファキャッシュに対象ブロックがなかったことを示しています。

つまり、バッファキャッシュに乗り切らなため、最初にINSERTされたブロックはそのままストレージへ物理書き込みされて追い出された結果。。ということになりますね。

登録したブロック全てが遅延クリーンアウト対象になってしまった、ということになります。コミット時にクリーンアウトできてないわけだから!!!!!!!! (イメージ図でざっくり書いたとおりの感じに。。。

差分のあった統計のみ記載

CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat commit cleanout failures: block lost 3028
sysstat commit cleanouts 3028
sysstat consistent gets 274
sysstat db block changes 1
sysstat no work - consistent read gets 149
sysstat physical reads 60

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat commit cleanout failures: block lost 3028
sesstat commit cleanouts 3028
sesstat db block changes 1


次に、Scattered Readが実行される全表走査を行わせ、遅延ブロッククリーンアウトどれだけ発生するか結果確認!

PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表を全表走査)
想定通り、物理読み込み(この時点ではscattered readなのか、direct path readなのかわかりませんが)になっています。また、大量のREDOログが生成されているので遅延ブロッククリーンアウトが発生しています。

SCOTT@orcl> @table_full_scan.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 2040

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

500000 rows selected.

Elapsed: 00:01:03.08

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 669K| 647M| 46462 (1)| 00:00:02 |
| 1 | TABLE ACCESS FULL| HOGE | 669K| 647M| 46462 (1)| 00:00:02 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
46 recursive calls
13 db block gets
367852 consistent gets
160269 physical reads
14670268 redo size
1016952118 bytes sent via SQL*Net to client
367706 bytes received via SQL*Net from client
33335 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
500000 rows processed

set autot off


物理読み込みを伴う全表走査でどの程度の遅延ブロッククリーンアウトが発生したか統計を確認!!!!

CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)

想定どおり、INSERTした全ブロックが、immediate (CR) block cleanout applications = 166667 で遅延ブロッククリーンアウトされたことがわかります。(冒頭に記載していますが、データが格納されているブロック数は、 166667 blocks でしたよね)
physical readsは意図通り発生していますが、physical reads directが変化していないので、狙い通りScattered Readになったようですね

差分のあった統計のみ記載

CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat DBWR checkpoint buffers written 241
sysstat DBWR thread checkpoint buffers written 241
sysstat DBWR undo block writes 1109
sysstat cleanouts only - consistent read gets 166667
sysstat commit cleanouts 55
sysstat commit cleanouts successfully completed 55
sysstat consistent gets 377331
sysstat db block changes 166904
sysstat deferred (CURRENT) block cleanout applications 29
sysstat immediate (CR) block cleanout applications 166667
sysstat immediate (CURRENT) block cleanout applications 15
sysstat no work - consistent read gets 38449
sysstat physical reads 160805
sysstat physical writes 137551
sysstat physical writes from cache 137551
sysstat physical writes non checkpoint 137466

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat cleanouts only - consistent read gets 166667
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 374538
sesstat db block changes 166710
sesstat deferred (CURRENT) block cleanout applications 3
sesstat immediate (CR) block cleanout applications 166667
sesstat immediate (CURRENT) block cleanout applications 1
sesstat no work - consistent read gets 36779
sesstat physical reads 160513


念の為、今一度、物理読み込みを伴う全表走査を行なって、クリーンアウトされたのか確認してみましょうw(疑い深いw)
もう一度、PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表を全表走査)

redo size0 なのでクリーンアウトは発生してない。想定通り

SCOTT@orcl> @table_full_scan.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 2040

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

500000 rows selected.

Elapsed: 00:00:33.17

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 669K| 647M| 46462 (1)| 00:00:02 |
| 1 | TABLE ACCESS FULL| HOGE | 669K| 647M| 46462 (1)| 00:00:02 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
200399 consistent gets
148198 physical reads
0 redo size
1016952118 bytes sent via SQL*Net to client
367706 bytes received via SQL*Net from client
33335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
500000 rows processed

set autot off

同様に、統計でも確認してみます!
CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)

クリーンアウトを示す統計値は上昇していません!!!(うんうんw)

差分のあった統計のみ記載

CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat DBWR checkpoint buffers written 732
sysstat commit cleanouts 1
sysstat commit cleanouts successfully completed 1
sysstat consistent gets 200522
sysstat db block changes 13
sysstat deferred (CURRENT) block cleanout applications 1
sysstat no work - consistent read gets 200403
sysstat physical reads 148200
sysstat physical writes 68570
sysstat physical writes from cache 68570
sysstat physical writes non checkpoint 68331

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat consistent gets 200414
sesstat db block changes 9
sesstat no work - consistent read gets 200366
sesstat physical reads 148198



OK. Done. ということで、まとめ!

バッファキャッシュには収まりきれないデータ量の場合、コミットクリーンアウトしようとしていたブロックも追い出されてしまうので、結果的に、全ブロックが遅延ブロッククリーンアウトになった。というイメージしていた結果の通りでした。
(今回のケースもシンプルケースなので比較的予想しやすい結果ですが、クリーンアウトに関わる統計は以外に多く、複雑な動きになるものもあります。再現するののめんどくさいのでしませんがw)
Photo_20210911234101

寄り道はここまで、次回は、こんどこそ、direct path readと遅延ブロッククリーンアウトの関係をみていきたいと思います。


来週天気いいかなー。遅い夏休みなのに。微妙な気がしてきた。。。。。



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #6
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #7


| | | コメント (0)

2021年9月12日 (日)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #7

Previously on Mac De Oracle
前回は、コミットクリーンアウトと遅延クリーンアウト、そして、そこにTable Full ScanでScattered Read (待機イベントだと db file scattered read) を絡めてストレージへ永続化されたクリーンアウトが遅延されてしまったブロックを物理読み込みませつつ遅延ブロッククリーンアウトを再現させてみました。
また、次回は、図中のscattered read 部分を direct path read にしつつ、最後の最後で、scattered read にしてみる、とか、そんなイメージをぼやーーーーんと浮かべながら、発生させる方法をどうするか考えてますw。つづく。なんてことを言っていましたが、またまた、ちょいと寄り道ですしますw

バッファキャッシュから溢れるぐらいのデータをぐるぐる系INSERTで、しかも1回のコミットにしたら、コミット前にあふれたデータはストレージへ書き出され、かつ、クリーンアウトも遅延されるよなー。という予想を元に、ちょいと遊んでから次に進みたいw と思います。

これまでの流れから、基本的なクリーンアウトおよび遅延ブロッククリーンアウトとしては以下ようなパターンを確認してきました。

バッファキャッシュの上でコミットクリーンアウトおよび、遅延ブロッククリーンアウト(単純なタイプ)が行われているケース
Photo_20210911234001

ここからが想像というか、私が理解している範囲から想像した動き。バッファキャッシュから溢れはしないけど、いっぱいいっぱいな場合は、クリーンアウトされるブロックがキャッシュ上に多くあるだろうな。と.
とは言っても、バッファキャッシュ上ではあるわけです。
Photo_20210911234002


そこで、ちょいと意地悪をして、バッファキャッシュから溢れ出る程度のデータ量だったどうなるのかなーーーーと。冒頭ですでコメントしているわけですけどもw 多分、以下のような動きだよねー、と。
そういえば、以前、DBTSで行なったセッションの「バッファキャッシュ欠乏症」の部分で、似たようなバッファキャッシュから溢れ出したブロックの挙動をなんとかするみたいな資料も今回の動きを想像するにはよいかもしれないですね。
Photo_20210911234101


と、思い、頭の中のイメージを Pagesでざざっと作ったところで、本日はここまで。次回へつづく。


Beat SaberとWalkingの合わせ技で、効果的な減量継続中w



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #6



| | | コメント (0)

2021年9月10日 (金)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #6

Previously on Mac De Oracle
前々回と前回はバッファキャッシュの10%を超えるデータ量のINSERT文の実行とCOMMITの実行で、バッファキャッシュの10%-15%程度は、COMMIT時にクリーンアウトされ、残ったブロックのクリーンアウトは先送りされる。という検証を2つのパターンで確認してみました。

どのような流れで発生するかを各ステップ毎にシステム統計(CDB)とクエリーを実行するセッションのセッション統計(PDB)を取得し、どのように統計値が変化すれば、コミットクリーンアウトや遅延ブロッククリーンアウトが起きているのかを見ながらすすめました。以下2つのエントリーで確認した動きの違いはイメージできたのか少々不安ではありますがw (そこそこ長いエントリーなのでw)

こちら前々回は、クリーンアウトが遅延されたブロックが永続化される前に、遅延ブロッククリーンアウトさせてみたケース
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
で、

前回は、クリーンアウトが遅延されたブロックが永続化された後に、遅延ブロッククリーンアウトさせてみたケース
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5

なんです :) 


まだ、イメージつかめない方もいるかもしれないので

超ざっくりした絵が頭の中に浮かばない方向けに、上記検証を行う前に、私の頭の中にうかんだ、ラフイメージをほぼそのまま

(こまけーとこは気にしないでくださいね。ラフイメージですから、こうだろうなーというのを想像している状態そのままのイメージですのでw)

クリーンアウトが遅延されたブロックが永続化される前に、遅延ブロッククリーンアウトさせてみたケース
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
No4


クリーンアウトが遅延されたブロックが永続化された後に、遅延ブロッククリーンアウトさせてみたケース
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5
No5

再現させてるケースはシンプルなものなので処理時間云々を比較してはいないですが、複雑なケースになると、本来スマートスキャンさせたいのにシングルブロックリードが多くなったりするケースなど、以前紹介したURLを見ていただければ参考になるかもしれないですね。
クエリーやDMLの処理時間が伸びてビビるぐらいに仕事量が増えてたり、先送りされたことで、もろもろ後処理が複雑化する場合もあるわけで)

ということで、こんな図をイメージしながら、ネタ作ってます。はいw


次回は、図中のscattered read 部分を direct path read にしつつ、最後の最後で、scattered read にしてみる、とか、そんなイメージをぼやーーーーんと浮かべながら、発生させる方法をどうするか考えてますw。つづく。



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5



| | | コメント (0)

2021年9月 9日 (木)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #5

前回はバッファキャッシュの10%を超えるデータブロックへのINSERT文の実行とCOMMITの実行で、バッファキャッシュの13%-15%程度はCOMMIT時にクリーンアウトされ、残りは遅延ブロッククリーンアウト(先送り)される。
direct path readではないSELECT文による(前回のケースでは scattered read))遅延ブロッククリーンアウトは、1度のみ発生するという状況を確認しました。

ところで、
前回のエントリで、2度、全表走査(前回の検証ではscattered read)を実行しているのですが、物理読み込みは発生させていません。(INSERT→COMMIT→SELECT→SELECTという流れで、十分なバッファキャッシュがあるので、当然ではあるのですがw)
バッファキャッシュに乗ったままのブロックが遅延クリーンアウトされていたわけです。

前回のエントリ:古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 194K| 188M| 18189 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 194K| 188M| 18189 (1)| 00:00:01 |
--------------------------------------------------------------------------

1. 遅延ブロッククリーンアウトを発生させた場合。遅延ブロッククリーンアウト対象のデータがバッファキャッシュ上ににあるためクリーンアウトに伴う物理読み込みはない。

Statistics
----------------------------------------------------------
46 recursive calls
13 db block gets
91636 consistent gets
7 physical reads
967348 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
200000 rows processed

2. 直後に再度全表走査した場合も、キャッシュヒットしているので、物理読み込みはない

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
80061 consistent gets
0 physical reads
0 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200000 rows processed

前述の1.2.それぞれの実行で物理読み込みだったなにか変化はあるのだろうか。。。予想では、上記に加えてscattered readに伴う物理読み込みが増えるだけのはずです。その動きを見てみることにします。(こういう動きを見ていると楽しいですよねw)
手順は前回と同じですが、各全表走査の前にインスタンスを再起動してバッファキャッシュを空にしておきます。
(buffer cacheをflushすればいいじゃん。という声も聞こえてきそうですが、今回は再起動でクリアしました。はいw)


前回から多少変更したスクリプトも載せておきます(本文中にもありますが)、細かい解説は後述

$ cat table_full_scan.sql
alter session set "_serial_direct_read" = never
.
l
/
alter session set "_very_large_object_threshold" = 1056
.
l
/

!echo set autot trace exp stat
set autot trace exp stat


select * from hoge
.
l
/

!echo set autot off
set autot off




0) 対象表をdrop/create
オブジェクト作り直し

SCOTT@orcl> @droppurge_create_hoge

Table dropped.

Table created.

SCOTT@orcl> select segment_name,blocks from user_segments where segment_name like '%HOGE%';

no rows selected


1) 統計をクリアするのにインスタンス再起動

$ sudo service oracle restart


2) PDBのscottでログインし、client_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションと区別するため

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


3) CDBのSYSで統計取得(初回)

内容は省略!(統計差分取得のためのベースラインを取得しているだけ)

SYS$orclcdb> @show_stat scott


4) PDBのSCOTTユーザーでデータINSERT(データ量2倍、コミットなし)

SCOTT@orcl> @insert_each_rows_2
1* begin for i in 1..200000 loop insert into hoge values(i, lpad('*', 2000, '*')); end loop; end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:29.48
SCOTT@orcl>


5) CDBのSYSで統計取得(INSERT後、未コミット)

未コミットの状態なので特に、気にせず、ふーーーん。ぐらいの感じで眺めていただければいいですね。前回同様に、commit cleanouts, commit cleanouts successfully completed, deferred (CURRENT) block cleanout applications, immediate (CURRENT) block cleanout applicationsといったクリーンアウト関連統計が極わずかありますが、この時点で発生しているのは本題ではないので気にしなくてOK

差分のある統計のみ記載

(CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat DBWR checkpoint buffers written 23589
sysstat DBWR checkpoints 2
sysstat DBWR thread checkpoint buffers written 22643
sysstat DBWR transaction table writes 50
sysstat DBWR undo block writes 848
sysstat cleanouts and rollbacks - consistent read gets 5
sysstat commit cleanout failures: callback failure 20
sysstat commit cleanouts 1320
sysstat commit cleanouts successfully completed 1300
sysstat consistent gets 124041
sysstat db block changes 757165
sysstat deferred (CURRENT) block cleanout applications 715
sysstat immediate (CR) block cleanout applications 5
sysstat immediate (CURRENT) block cleanout applications 196
sysstat no work - consistent read gets 46398
sysstat physical reads 4063
sysstat physical writes 23589
sysstat physical writes from cache 23589
sysstat physical writes non checkpoint 23494

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 49596
sesstat db block changes 744727
sesstat deferred (CURRENT) block cleanout applications 4
sesstat immediate (CURRENT) block cleanout applications 1
sesstat no work - consistent read gets 49
sesstat physical reads 15


6) PDBのSCOTTユーザーでコミットの実行

SCOTT@orcl> commit;

Commit complete.


7) CDBのSYSで統計取得(コミット後)
前回同様ノイズもなく、綺麗にコミット時のクリーンアウトが発生しています。バッファキャッシュの約14-5%程度なのは前回と変わらずですね。

差分のある統計のみ記載

(CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat commit cleanouts 55700
sysstat commit cleanouts successfully completed 55700
sysstat db block changes 1

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat commit cleanouts 55700
sesstat commit cleanouts successfully completed 55700
sesstat db block changes 1


8) Oracle Databaseを再起動してバッファキャッシュをクリア

ここが前回と違う手順で、クリーンアウトされないブロックはずーーーーーっと残るよね。ということの確認でもあります。(alter system flush buffer_cacheでも同じことができるわけですが、ここでは再起動しています)

$ sudo service oracle restart
[sudo] password for oracle:
Restarting oracle (via systemctl): [ OK ]
$


9) PDBのscottでログインし、client_infoをセット

disconnectしたので再度、client infoをセットし直し!

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


10) CDBのSYSで統計取得(再起動後初回)

内容は省略!(再起動したので統計値の差分取得用ベースライン統計の取得)

SYS$orclcdb> @show_stat scott


11) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表を全表走査)- scattered read / table full scan の1回目

ここでは待機イベントまでは確認できませんが、Table full scanと実行統計よりRedoログがたっぷり生成されていることは確認できます。SELECT文ですが。。。つまり、遅延ブロッククリーンアウトが発生しているということですね。確認は後述の統計で。
狙い通り、物理読み込みも発生しています!

Note)
"_very_large_object_threshold" = 1056 としているのは、direct path readとなる上限セグメントサイズをhoge表が超えているためdirect path readを抑止するためにこの隠しパラメータで上限値を引き上げ、scattered readになるように強制しています。

SCOTT@orcl> @table_full_scan.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.01
1* alter session set "_very_large_object_threshold" = 1056

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

200000 rows selected.

Elapsed: 00:00:06.10

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 214K| 207M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 214K| 207M| 18223 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
38 recursive calls
13 db block gets
91519 consistent gets
67073 physical reads
967432 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off
SCOTT@orcl>


12) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)/ scattered read / table full scan の1回目

遅延ブロッククリーンアウト関連統計値が上昇しているので、遅延ブロッククリーンアウトの発生が確認できます。ここまでは前回と同じ。(同じじゃないと困りますがw)

違う点は、事前にインスタンスを再起動しているため、physical reads が上昇しています。これは hoge表を scattered readで全表走査しているからです。(phsical read directは発生していない)バッファキャッシュを経由するのでconsistent gets,no work - consistent read gets も上昇しています。狙い通りです。

そして、重量な遅延ブロッククリーンアウトですが、バッファキャッシュでヒットしていた時と同数のブロックで発生しています。(ニッコリ

インスタンスの停止や起動があったとしても、クリーンアウトが先送りされたブロックはアクセスされない限りクリーンアウトされず残っているということを示しています!!!!(ここ試験にでますよー。嘘)

(CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat cleanouts only - consistent read gets 10967
sysstat commit cleanouts 7
sysstat commit cleanouts successfully completed 7
sysstat consistent gets 98967
sysstat db block changes 11016
sysstat deferred (CURRENT) block cleanout applications 3
sysstat immediate (CR) block cleanout applications 10967
sysstat immediate (CURRENT) block cleanout applications 2
sysstat no work - consistent read gets 72543
sysstat physical reads 67403

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat cleanouts only - consistent read gets 10967
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 98357
sesstat db block changes 11012
sesstat deferred (CURRENT) block cleanout applications 2
sesstat immediate (CR) block cleanout applications 10967
sesstat immediate (CURRENT) block cleanout applications 2
sesstat no work - consistent read gets 72208
sesstat physical reads 67397


13) Oracle Databaseを再起動してバッファキャッシュをクリア

再度、インスタンスを再起動して、バッファキャッシュをクリアします。後続の全表走査では、遅延ブロッククリーンアウトは発生せず、物理読み込み(この検証では scattered readさせています)を伴うTable full scanが行われるだけのはずです。

$ sudo service oracle restart
[sudo] password for oracle:
Restarting oracle (via systemctl): [ OK ]
$


14) PDBのscottでログインし、client_infoをセット

disconnectしたので再度、client infoをセットし直し!

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


15) CDBのSYSで統計取得(再起動後初回)

内容は省略!(再起動したのでベースラインとなる統計を取得)

SYS$orclcdb> @show_stat scott


16) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表を全表走査)- scattered read / table full scan の2回目

実行統計から、physical readsが、発生しています。Redoは生成されていないことも読み取れるので、遅延ブロッククリーンアウトは発生していないことも確認できます。:) 想定通りですね。

SCOTT@orcl> @table_full_scan.sql
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.00
1* alter session set "_very_large_object_threshold" = 1056

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

200000 rows selected.

Elapsed: 00:00:06.44

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 214K| 207M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 214K| 207M| 18223 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
30 recursive calls
0 db block gets
80211 consistent gets
66719 physical reads
0 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off
SCOTT@orcl>


17) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)/ scattered read / table full scan の2回目

physical reads は発生していますが、physical reads directではないことが確認できます。Table full scanをscattered readで読み込んでいるという想定通りの結果。 
遅延ブロッククリーンアウトを示deferred (CURRENT) block cleanout applications 、immediate (CURRENT) block cleanout applicationsや、コミットクリーンアウトを示すcommit cleanouts 、commit cleanouts successfully completed という統計が極わずかに変動していますが、今回の検証ではノイズなので気にしたくてOK。

(CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat DBWR checkpoint buffers written 11
sysstat commit cleanouts 15
sysstat commit cleanouts successfully completed 15
sysstat consistent gets 87375
sysstat db block changes 79
sysstat deferred (CURRENT) block cleanout applications 12
sysstat immediate (CURRENT) block cleanout applications 3
sysstat no work - consistent read gets 83322
sysstat physical reads 67028
sysstat physical writes 11
sysstat physical writes from cache 11
sysstat physical writes non checkpoint 11

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat commit cleanouts 2
sesstat commit cleanouts successfully completed 2
sesstat consistent gets 86936
sesstat db block changes 29
sesstat deferred (CURRENT) block cleanout applications 1
sesstat no work - consistent read gets 83098
sesstat physical reads 67023




まとめ

遅延ブロッククリーンアウトは、インスタンスを停止して残ったままということが確認できました。(クリアされるまで残るのですよねー)
クリーンアウトが遅延されているブロックが物理読み込みされた(アクセスされた)タイミングで遅延ブロッククリーンアウトが発生することも確認できました。(物理読み込みの有無には関係しない)
バッファキャッシュ上の遅延ブロッククリーンアウト同様、クリーンアウトされたブロックでは、再度、クリーンアウト対象になるような更新が発生しなければ、遅延ブロッククリーンアウトは発生しない

久しぶりにシステム統計やセッション統計を見ててワクワクしてきましたよーーーーっw

ところで、
冒頭でも記載しましたが、バッファキャッシュ上の遅延ブロッククリーンアウト(再掲)に加え、物理読み込みを伴う遅延ブロッククリーンアウトのauto traceの結果をまとめて記載しておきます。SELECT文ですが、REDOログが生成されている場合は遅延ブロッククリーンアウトが発生しているということになります。

実行計画

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 194K| 188M| 18189 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 194K| 188M| 18189 (1)| 00:00:01 |
--------------------------------------------------------------------------

1. 遅延ブロッククリーンアウトを発生させた場合。遅延ブロッククリーンアウト対象のデータがバッファキャッシュ上ににあるためクリーンアウトに伴う物理読み込みはない。

Statistics
----------------------------------------------------------
46 recursive calls
13 db block gets
91636 consistent gets
7 physical reads
967348 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
200000 rows processed

2. 直後に再度全表走査した場合も、キャッシュヒットしているので、物理読み込みはない

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
80061 consistent gets
0 physical reads
0 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200000 rows processed


3. 遅延ブロッククリーンアウトを発生させた場合。遅延ブロッククリーンアウト対象のデータを物理読み込みし、バッファキャッシュに載せている動きが見えます。

Statistics
----------------------------------------------------------
38 recursive calls
13 db block gets
91519 consistent gets
67073 physical reads
967432 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
200000 rows processed

4. 3.の直後に再度全表走査した場合。事前にキャッシュをクリアしているため、物理読み込みがありますが、遅延ブロッククリーンアウトは発生していません。(Redoが生成されていないことで確認できます)

Statistics
----------------------------------------------------------
30 recursive calls
0 db block gets
80211 consistent gets
66719 physical reads
0 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
200000 rows processed

ではでは。
次回は、scattered readのtable full scanではなく、direct path readだったらどうなるのか調べてみましょうか。。(いろいろな再現方法があるわけですが、手間のかからないお手軽な再現方法で確認してみようと思いますw)


洗濯機の修理が終わって一安心w ということで、次回へつづく。



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4


| | | コメント (0)

2021年9月 3日 (金)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #4

Previously on Mac De Oracle
前回はバッファキャッシュの10%未満のデータブロックへのINSERT文の実行とCOMMITの実行で、遅延ブロッククリーンアウトは発生せず、COMMIT時にすべての対象ブロックがクリーンアウトされるということを確認しました。

今回は、そのデータ量を倍にして、バッファキャッシュの10%程度を超えるデータブロックが遅延ブロッククリーンアウトされるのかを見ていくことにします。手順は前回と同じですが、遅延ブロッククリーンアウトさせた後で、もう一度全表走査してクリーンアウトが繰り返されないことも確認しておきます(次回以降に予定している確認への伏線なのですがw)


0) 対象表をdrop/create
オブジェクトを作り直して前提条件を合わせておきます

SCOTT@orcl> @droppurge_create_hoge

Table dropped.

Table created.

SCOTT@orcl> select segment_name,blocks from user_segments where segment_name like '%HOGE%';

no rows selected


1) 統計をクリアするのにOracle再起動

$ sudo service oracle restart


2) PDBのscottでログインし、client_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションと区別できるようにしています

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('Target Session');
3* end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
SCOTT@orcl>


3) CDBのSYSで統計取得(初回)

内容は省略!(ベースラインを取得しているだけなので)

SYS$orclcdb> @show_stat

4) PDBのSCOTTユーザーでデータINSERT(データ量2倍、コミットなし)

SCOTT@orcl> @insert_each_rows_2
1* begin for i in 1..200000 loop insert into hoge values(i, lpad('*', 2000, '*')); end loop; end;

PL/SQL procedure successfully completed.

Elapsed: 00:00:29.48
SCOTT@orcl>


5) CDBのSYSで統計取得(INSERT後、未コミット)

INSERTしただけです。未コミットなので特に気になる情報は現れていません。この値からコミット後にどのように変化するのか? という部分に注目する必要があるんですよー。
deferred (CURRENT) block cleanout applications と immediate (CURRENT) block cleanout applications が僅かにありますが、この時点では気にする部分ではないです

(値の変化が1以上ある統計のみ表示)

(CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat DBWR checkpoint buffers written 22756
sysstat DBWR checkpoints 33
sysstat DBWR thread checkpoint buffers written 22756
sysstat DBWR transaction table writes 22
sysstat DBWR undo block writes 606
sysstat consistent gets 49761
sysstat db block changes 744980
sysstat deferred (CURRENT) block cleanout applications 4
sysstat immediate (CURRENT) block cleanout applications 1
sysstat no work - consistent read gets 192
sysstat physical reads 18
sysstat physical writes 22756
sysstat physical writes from cache 22756
sysstat physical writes non checkpoint 22756

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat consistent gets 49501
sesstat db block changes 744980
sesstat deferred (CURRENT) block cleanout applications 4
sesstat immediate (CURRENT) block cleanout applications 1
sesstat no work - consistent read gets 65
sesstat physical reads 17


6) PDBのSCOTTユーザーでコミットの実行

SCOTT@orcl> commit;

Commit complete.


7) CDBのSYSで統計取得(コミット後)

この結果、ノイズも少なく、綺麗に取れてます!!! w

前々回の事前確認の通り、2倍のデータブロック数は、 66667ブロック で、バッファキャッシュの10%は、ざっくり計算で、42394ブロック。つまり、想定では 42394ブロック ほどが、commit時のブロッククリーンアウトの対象と想定していました。

覚えてますか? みなさん!

実際にcommit時にクリーンアウトされたのはどれぐらいでしょう? 
結果は、55700ブロックとなりました。想定より多いですねw ほぼ合ってはいますが。
実際にはバッファキャッシュの13%〜15%程度が閾値になっているように見えます。とはいえ、commit時にcleanoutされたブロック数は 55700ブロック ですから、残る 10967ブロック のcleanoutは遅延されたということは確実です。commit対象のデータブロック全てをcleanoutするわけではない、ということは確認できたのではないでしょうか?

(差分が1以上ある統計のみ記載)
(CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat commit cleanouts 55700
sysstat commit cleanouts successfully completed 55700
sysstat db block changes 1

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat commit cleanouts 55700
sesstat commit cleanouts successfully completed 55700
sesstat db block changes 1


8) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表を全表走査)

SCOTT@orcl> sset autot trace exp stat
SCOTT@orcl> salter session set "_serial_direct_read" = never;

Session altered.

Elapsed: 00:00:00.00
SCOTT@orcl> select * from hoge;

200000 rows selected.

Elapsed: 00:00:05.14

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 194K| 188M| 18189 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 194K| 188M| 18189 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)


Statistics
----------------------------------------------------------
46 recursive calls
13 db block gets
91636 consistent gets
7 physical reads
967348 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
200000 rows processed


9) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)

遅延ブロッククリーンアウトは、事前の計算通り、 10967ブロック 発生しています。SELECT文では、immediate (CR) block cleanout applications として現れることも確認できます。
また、cleanouts only - consistent read gets として も同数計上されているところが見てます。綺麗に現れています。

commit cleanouts, ommit cleanouts successfully completed がでていますが、ここでは気にしなくてよいですね、極わずかで。SELECT文なので。
immediate (CURRENT) block cleanout applications、deferred (CURRENT) block cleanout applications もでていますが、同じく極わずかで、対象表のものではないと考えられるためここでは気にしなくて良いですね。

しかし、計算通りに発生してくれると確認が楽w (想定外の動きじゃなくてよかったw)

(CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat DBWR checkpoint buffers written 272
sysstat DBWR thread checkpoint buffers written 272
sysstat cleanouts only - consistent read gets 10967
sysstat commit cleanouts 16
sysstat commit cleanouts successfully completed 16
sysstat consistent gets 117000
sysstat db block changes 11207
sysstat deferred (CURRENT) block cleanout applications 10
sysstat immediate (CR) block cleanout applications 10967
sysstat immediate (CURRENT) block cleanout applications 2
sysstat no work - consistent read gets 83351
sysstat physical reads 939
sysstat physical writes 272
sysstat physical writes from cache 272
sysstat physical writes non checkpoint 260

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat cleanouts only - consistent read gets 10967
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 98162
sesstat db block changes 11011
sesstat deferred (CURRENT) block cleanout applications 2
sesstat immediate (CR) block cleanout applications 10967
sesstat immediate (CURRENT) block cleanout applications 2
sesstat no work - consistent read gets 72113
sesstat physical reads 122


10) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表を全表走査)

そして、ここからがおまけの確認ステップです

もう一度、同じ表を全表走査してみます。どうなると思いますか? 遅延されていたブロッククリーンアウトも行われたのですから、当然、該当オブジェクトで遅延ブロッククリーンアウトは発生しない。はず! ですよね。

確認してみましょう。(発生してたらどうしようw、もうしそうなったらバグレポートでも上げようかなw)

.......

Redoは生成されてない! (よかった! 想定どおりだ!w)

SCOTT@orcl> @table_full_scan
1* alter session set "_serial_direct_read" = never

Session altered.

Elapsed: 00:00:00.00
set autot trace exp stat

1* select * from hoge

200000 rows selected.

Elapsed: 00:00:05.43

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 214K| 207M| 18223 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 214K| 207M| 18223 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
80061 consistent gets
0 physical reads
0 redo size
406775148 bytes sent via SQL*Net to client
147264 bytes received via SQL*Net from client
13335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
200000 rows processed

set autot off

SCOTT@orcl>

11) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)2回目

これもノイズが少なく綺麗に取れました。該当セッションでは物理読み込みも発生していないので、キャッシュから全データを読み込んだようです。

そして、想定どおり、該当セッションでは遅延ブロッククリーンアウトは発生していません!

commit cleanouts、commit cleanouts successfully completed、deferred (CURRENT) block cleanout applicationsが1ブロックありますがCDB側の管理情報関連でしょうね。気にする部分ではないですね。

(差分のあった統計のみ記載)

(CDB)システム統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sysstat commit cleanouts 1
sysstat commit cleanouts successfully completed 1
sysstat consistent gets 80150
sysstat db block changes 13
sysstat deferred (CURRENT) block cleanout applications 1
sysstat no work - consistent read gets 80075

(PDB) SCOTTのセッション統計

SOURCE  NAME                                                             VALUE
------- ------------------------------------------------- --------------------
sesstat consistent gets 80076
sesstat db block changes 9
sesstat no work - consistent read gets 80043




まとめ

おおよそ、バッファキャッシュの10%程度が commit時にcleanout されるという点については、約15%程度と見ておいたほうが良さそうですが、まあ大きな違いはないので、その辺りに閾値があると考えて問題はなさそうです。
また、それを超えるブロックについては、cleanoutが先送りされ、最初に該当ブロックにアクセスしたSQLがその影響を受ける。

この検証ではSELECT文では、immediate (CR) block cleanout applications という形で統計に現れました。UPDATE文やDELETE文の場合は他の統計として現れそうですね。(CURRENT)関連のcleanoutの統計は今回動いていないのでSQL文を変えて同じような検証をしてみると面白い結果をえられそうです。

そして、SELECT文で、遅延ブロッククリーンアウトされてしまえば、その間に更新が発生しなければ、クリーンアウトは発生しない(おまけで検証した部分ですが、別検証では興味深い動きを紹介する予定です。その伏線でもあります)

次回へつづく


5年目を迎えた、パナソニックのドラム洗濯機がH故障した。慌てて近所のコインランドリーを検索w 近所にあってよかったw



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3



| | | コメント (0)

2021年9月 2日 (木)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #3

Previously on Mac De Oracle
前回は準備を終えたところまででした。

今日は、簡単なところから確認していきましょう。

もしも、「遅延ブロッククリーンアウトが起きない程度のブロック更新量だったなら。。。」。結果は遅延ブロッククリーンアウトは起きないはず。 ですよね。

ざっと手順を紹介しておきましょう。下図の 1)〜9)の順で行います
20210901-221016

前述の手順で、各操作後の統計の差分(変化量)を見る。マニュアルの統計の説明ってざっくり過ぎてよくわからないのが多いわけですがw、操作と値の変化を合わせて観察すると、それなりには理解できる程度に値が変化していることに気づきますw :)

1) 統計をクリアするのにOracle再起動

$ sudo service oracle restart


2) PDBのscottでログインし、client_infoをセット
v$sessionのclient_info列の'TargetSession'文字列で他のSCOTTユーザーのセッションと区別できるようにしています。

SCOTT@orcl> @set_client_info
1 begin
2 DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
3* end;

PL/SQL procedure successfully completed.


3) CDBのSYSで統計取得(初回)
初回なのですべてリストしていますが、CON_ID=0のCDBのシステム統計([g]v$sysstat)とCON_ID=3のPDBの2)のCLIENT_INFOを設定されたセッションのセッション統計([g]v$sesstat)を取得します。
今回のケースではcleanoutが含まれている統計の差異だけに着目すれば良いのですが。準備運動程度のテストケースなので一応すべて載せておきます :)
(マルチテナントだと、DBRWの動きを見るにはCDBのDBWR関連の統計を見る必要があるため、CDBのシステム統計とPDBの当該セッションのセッション統計を対象にしています)

SYS$orclcdb> @show_stat
SOURCE NAME VALUE CON_ID
------- ------------------------------------------------------------ -------------------- ----------
sysstat DBWR checkpoint buffers written 0 0
sysstat DBWR checkpoints 0 0
sysstat DBWR fusion writes 0 0
sysstat DBWR lru scans 0 0
sysstat DBWR object drop buffers written 0 0
sysstat DBWR parallel query checkpoint buffers written 0 0
sysstat DBWR revisited being-written buffer 0 0
sysstat DBWR tablespace checkpoint buffers written 0 0
sysstat DBWR thread checkpoint buffers written 0 0
sysstat DBWR transaction table writes 0 0
sysstat DBWR undo block writes 0 0
sysstat cleanouts and rollbacks - consistent read gets 6 0
sysstat cleanouts only - consistent read gets 0 0
sysstat commit cleanout failures: block lost 0 0
sysstat commit cleanout failures: buffer being written 0 0
sysstat commit cleanout failures: callback failure 0 0
sysstat commit cleanout failures: cannot pin 0 0
sysstat commit cleanout failures: hot backup in progress 0 0
sysstat commit cleanout failures: write disabled 0 0
sysstat commit cleanouts 570 0
sysstat commit cleanouts successfully completed 570 0
sysstat consistent gets 158,083 0
sysstat db block changes 3,247 0
sysstat deferred (CURRENT) block cleanout applications 316 0
sysstat immediate (CR) block cleanout applications 6 0
sysstat immediate (CURRENT) block cleanout applications 15 0
sysstat no work - consistent read gets 108,516 0
sysstat physical reads 12,551 0
sysstat physical reads direct 0 0
sysstat physical writes 0 0
sysstat physical writes direct 0 0
sysstat physical writes from cache 0 0
sysstat physical writes non checkpoint 0 0
sysstat transaction tables consistent read rollbacks 0 0
sysstat transaction tables consistent reads - undo records applied 0 0
sesstat DBWR checkpoint buffers written 0 3
sesstat DBWR checkpoints 0 3
sesstat DBWR fusion writes 0 3
sesstat DBWR lru scans 0 3
sesstat DBWR object drop buffers written 0 3
sesstat DBWR parallel query checkpoint buffers written 0 3
sesstat DBWR revisited being-written buffer 0 3
sesstat DBWR tablespace checkpoint buffers written 0 3
sesstat DBWR thread checkpoint buffers written 0 3
sesstat DBWR transaction table writes 0 3
sesstat DBWR undo block writes 0 3
sesstat cleanouts and rollbacks - consistent read gets 0 3
sesstat cleanouts only - consistent read gets 0 3
sesstat commit cleanout failures: block lost 0 3
sesstat commit cleanout failures: buffer being written 0 3
sesstat commit cleanout failures: callback failure 0 3
sesstat commit cleanout failures: cannot pin 0 3
sesstat commit cleanout failures: hot backup in progress 0 3
sesstat commit cleanout failures: write disabled 0 3
sesstat commit cleanouts 1 3
sesstat commit cleanouts successfully completed 1 3
sesstat consistent gets 374 3
sesstat db block changes 4 3
sesstat deferred (CURRENT) block cleanout applications 1 3
sesstat immediate (CR) block cleanout applications 0 3
sesstat immediate (CURRENT) block cleanout applications 0 3
sesstat no work - consistent read gets 220 3
sesstat physical reads 28 3
sesstat physical reads direct 0 3
sesstat physical writes 0 3
sesstat physical writes direct 0 3
sesstat physical writes from cache 0 3
sesstat physical writes non checkpoint 0 3
sesstat transaction tables consistent read rollbacks 0 3
sesstat transaction tables consistent reads - undo records applied 0 3


4) PDBのSCOTTユーザーでデータINSERT(コミットなし)
データを1行単位でインサートしています。バルクインサートも使ってないです。綺麗なぐるぐる系ですねw。コミット時の効果を確認しやすいようにコミットは後で実行します!
このインサートで、前回の事前準備の時に確認しておいた、33334ブロックが更新されることになります

SCOTT@orcl> @insert_each_rows
1* begin for i in 1..100000 loop insert into hoge values(i, lpad('*', 2000, '*')); end loop; end;

PL/SQL procedure successfully completed.


5) CDBのSYSで統計取得(INSERT後、未コミット)

注) 3)の統計との差分のみ記載

未コミットであるこの時点で、commitクリーンアウトが発生(該当セッション統計でも同数発生。commit cleanoutsとcommit cleanouts successfully completed)してますが、これは気にしなくてもよいですね。
実行したトランザクションは、未コミットなので、この実行による直接的な影響ではないので。
また、deferred (CURRENT) block cleanout applicationsやimmediate (CURRENT) block cleanout applications の遅延ブロッククリーンアウトを示す統計も微量ですがこれも同様とみて良いでしょう。

この時点で統計取得理由は、操作毎に変化する統計を追うためなので、ふーーん。ぐらいでの雰囲気でOKです :)
コミット後とその後に該当オブジェクトをアクセスさせた時の遅延ブロッククリーンアウトの有無部分の部分が主役ですので。

参考)
INSERTした行の含まれる全ブロックはバッファキャッシュに載り切るブロック数なので、バッファキャッシュから書き出されているような動きも観測されていないのは確認できると思います。
(physical writes from cache,physical writes non checkpoint,physical writesの統計に変化がないので未記載ですが、それらの統計が動いていないということがその理由です)


(CDB)システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoints 1
sysstat commit cleanouts 6
sysstat commit cleanouts successfully completed 6
sysstat consistent gets 22884
sysstat db block changes 373785
sysstat deferred (CURRENT) block cleanout applications 5
sysstat immediate (CURRENT) block cleanout applications 1
sysstat no work - consistent read gets 59
sysstat physical reads 12


(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 6
sesstat commit cleanouts successfully completed 6
sesstat consistent gets 22816
sesstat db block changes 373785
sesstat deferred (CURRENT) block cleanout applications 5
sesstat immediate (CURRENT) block cleanout applications 1
sesstat no work - consistent read gets 49
sesstat physical reads 12


6) PDBのSCOTTユーザーでコミットの実行

SCOTT@orcl> commit;

Commit complete.


7) CDBのSYSで統計取得(コミット後)

注)5)と7)で取得した統計の差分のみ記載

ここが主役ですよー。このケースではバッファキャッシュの10%に満たないブロック数になるようにしたINSERT文(繰り返し実行)で 33334ブロックになるようにしました。これらを1度でcommitした場合、すべてのブロックがcommit時にblock cleanoutされるはずです。

では見てみましょう。

SCOTTのセッション統計より、commit cleanouts および commit cleanouts successfully completed から想定どおり全ブロックがcommit時にcleanoutされていることがわかります!
システム統計はインスタンス全体なのでPDBのそれら統計より大きめにでているのも確認できます。CDB側ではほんの少し deferred (CURRENT) block cleanout applications がありますが、管理情報系の遅延ブロッククリーンアウトでしょうね。 
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1で紹介したとおり 10% 未満では commit 時点で該当ブロックすべてcleanoutされることが確認できました。


(CDB)システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 139
sysstat DBWR transaction table writes 22
sysstat DBWR undo block writes 55
sysstat commit cleanouts 33343
sysstat commit cleanouts successfully completed 33343
sysstat consistent gets 18111
sysstat db block changes 138
sysstat deferred (CURRENT) block cleanout applications 5
sysstat no work - consistent read gets 10853
sysstat physical reads 827
sysstat physical writes 139
sysstat physical writes from cache 139
sysstat physical writes non checkpoint 139


(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 33334
sesstat commit cleanouts successfully completed 33334
sesstat db block changes 1


8) PDBのSCOTTユーザーで、遅延ブロッククリーンアウト影響有無確認(対象表を全表走査)

_serial_direct_read = never はdirect path readさせないためのおまじないです。direct path readで読み込まれたケースの動きは別エントリーで見ていく予定なので、direct path readを発生させずtable full scan (ようするにバッファキャッシュに載せる動き)で読み込むよう強制しています。
また、行数が多いのでその時間の短縮のためにautot trace exp statを有効にしてSELECT文を実行させつつ、termout offと同じ効果とauto explainの機能でSELECT文の実行統計からredoの生成有無を確認しています。(redoがあるということはなんらかの更新が行われているわけで、SELECT文の場合は比較的容易に遅延ブロッククリーンアウトの発生を推測できる統計にもなります)
以下のケースでは多少redoが生成されていますが、おそらく recursive callによるもので、HOGE表のオブジェクトそのものに対するもではなさそうです。(このケースではhoge表のオブジェクトに対する遅延ブロッククリーンアウトや
コミット時のブロッククリーンアウトの観察が主題なので、周りのノイズはあまり気にしなくてもOK(追いかけたい場合は別ですがw)


SCOTT@orcl> set autot trace exp stat
SCOTT@orcl> alter session set "_serial_direct_read" = never;

Session altered.

Elapsed: 00:00:00.00
SCOTT@orcl> select * from hoge;

100000 rows selected.

Elapsed: 00:00:02.59

Execution Plan
----------------------------------------------------------
Plan hash value: 2339479017

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 57526 | 55M| 9098 (1)| 00:00:01 |
| 1 | TABLE ACCESS FULL| HOGE | 57526 | 55M| 9098 (1)| 00:00:01 |
--------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)

Statistics
----------------------------------------------------------
21 recursive calls
13 db block gets
40465 consistent gets
3 physical reads
2212 redo size
203382760 bytes sent via SQL*Net to client
73706 bytes received via SQL*Net from client
6668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
100000 rows processed

SCOTT@orcl> set autot off


9) CDBのSYSで統計取得(遅延ブロッククリーンアウト有無確認)

注)7)のコミット時点からとの差異のみ記載。

多少ですが、SELECT文でredoが生成されてはいます。これは、commit cleanouts/commit cleanouts successfully completed (commit時に実施されるblock cleanout)と遅延ブロッククリーンアウトの統計の一つ、deferred (CURRENT) block cleanout applications が現れている影響ですね。コミット時点でHOGE表の全ブロックはcleanout済みなので recursive callによる内部管理情報関連で定常的に現れるものと考えられ、この検証では気にするところではないのでスルーしてください。

また、physical reads は、ほぼないため、この全表走査では、物理読み込み(direct path read含む)は、発生していないことも確認できます。
HOGE表はバッファキャッシュに載ったままという意図通りの状態にはなっているようです。
これ、今後のテストケースでも利用するので、キャッシュからエージアウトされないという点が確認できてると、以降の検証やりやすいんです:)


(CDB) システム統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sysstat DBWR checkpoint buffers written 34849
sysstat DBWR transaction table writes 32
sysstat DBWR undo block writes 897
sysstat commit cleanouts 558
sysstat commit cleanouts successfully completed 558
sysstat consistent gets 69973
sysstat db block changes 2862
sysstat deferred (CURRENT) block cleanout applications 311
sysstat immediate (CURRENT) block cleanout applications 28
sysstat no work - consistent read gets 57908
sysstat physical reads 812
sysstat physical writes 34849
sysstat physical writes from cache 34849
sysstat physical writes non checkpoint 34738


(PDB) SCOTTのセッション統計

SOURCE  NAME                                            VALUE
------- ---------------------------------------------------- ---------------
sesstat commit cleanouts 5
sesstat consistent gets 47037
sesstat db block changes 38
sesstat deferred (CURRENT) block cleanout applications 4
sesstat no work - consistent read gets 43103
sesstat physical reads 117


まとめ

DMLにより更新されたブロック数が、バッファキャッシュの10%未満のブロック数である場合、commit時にすべてcleanoutされ、対象表では遅延ブロッククリーンアウトは発生しない。
(想定通りなのですが、ちょいと安心w 19cでどう動くか確認してなかってしので少々ドキドキしてたw のはナイショ)

というわけで、今回はここまで。

いきなり涼しくなって、なんだこりゃ。。。。



古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1
古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2



| | | コメント (0)

2021年8月31日 (火)

古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #2

Previously on Mac De Oracle

前回は、遅延ブロッククリーンアウトに限らず、大きく変わったわけでも、最近実装された機能でもないのに意外に知られてないのか、良いところ悪いところ含め、現場でロストしてしまっているような知識って意外と多いのかもねー。なんて感じたので遅延ブロッククリーンアウトネタのURLリンクをまとめてみた。

続編書くにしても、同じようなことやっても面白くないので、ブロックダンプのような方法は使わず、[g]v$sysstatや[g]v$sesstatなどの統計から、ちょいと血糖値や尿酸値高めだよね的な角度からどのような変化が起きるか見ていくことにした :)

 

環境はVirtualBox上の19cでこれからの主流になるマルチテナントで試してみます。(非マルチテナントでの変化見ててもこれからはあまり役に立たないので)

今日は準備編

VirtualBox
https://www.virtualbox.org/

Pre-Built Developer VMs (for Oracle VM VirtualBox)のDatabase App Development VMとか
https://www.oracle.com/downloads/developer-vm/community-downloads.html

 

 

事前に準備しておくスクリプトは以下のとおり。繰り返し実行するので作っておくと便利ですよ。:)

まず最初に、遅延ブロッククリーンアウトはバッファキャッシュの10%ほどのブロックをコミット時にクリーンアウトして、残りを先送りするという基本的なお約束があるので、上記環境のOracle 19cがどの程度のバッファキャッシュなのかとブロックサイズを確認。これ大切ですよ。スクリプト準備する上でも :)

メモリサイズは大きめですが。。。w うちのは。(^^;;;


$ VBoxManage -v
6.1.26r145957
$
$ VBoxManage showvminfo 'Oracle DB Developer VM 19.3' | grep -E 'Memory|CPUs'
Memory size: 16384MB
Number of CPUs: 4
$

コミット時にブロッククリーンアウトされそうなブロック数をざっくり算出すると 42394 ブロックぐらいになりそう。


SYS@orclcdb> show sga

Total System Global Area 4294963960 bytes
Fixed Size 9143032 bytes
Variable Size 805306368 bytes
Database Buffers 3472883712 bytes
Redo Buffers 7630848 bytes

SYS@orclcdb>
SYS@orclcdb> show parameter db_block_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_size integer 8192
SYS@orclcdb>
SYS@orclcdb> select ceil( 3472883712 / 8192 * 0.1 ) from dual;

CEIL(3472883712/8192*0.1)
-------------------------
42394

Elapsed: 00:00:00.00

42394ブロックを超える程度のサイズのデータを生成するINSERT文と、その範囲に収まるデータ量生成INSERT文スクリプトを作れば良さそうですね。

想定されるブロックに収まる程度の量。1ブロック 8KB でデフォルトのPCTFREEは10%なのでざっくり6000bytes/rec超えるぐらい。
で、1ブロックに3行ぐらい入るようにすれば面白いかな。。

ということで、

表はなんどもdrop/createするので以下のDDLで。初回は索引を作らず、表のみので影響をみることにする。


$ cat droppurge_create_hoge.sql

drop table hoge purge;
create table hoge (id number, data varchar2(2000));

データ作成(バッファキャッシュの10%未満のデータ登録)
なお、確実に遅延ブロッククリーンアウトの影響を見たいので1行ごとのINSERTを繰り返し、コミット前後の状態の変化も見たいのでcommitも含めていない。(commitは別途実行する)


$ cat insert_rows.sql

begin for i in 1..100000 loop insert into hoge values(i, lpad('*', 2000, '*')); end loop; end;
.
l
/

ブロック数の事前確認


SCOTT@ORCL> select count(distinct dbms_rowid.rowid_block_number(rowid)) as "blocks" from hoge;

blocks
----------
33334

データ作成(バッファキャッシュの10%を超えるのデータ登録)
単純にループ回数を倍にして増量。これで事前に算出したバッファキャッシュの10%以上のブロック数は更新される。。。はず。


$ cat insert_rows_2.sql

begin for i in 1..200000 loop insert into hoge values(i, lpad('*', 2000, '*')); end loop; end;
.
l
/

ブロック数の事前確認(20,000ブロックぐらい?は遅延される想定)


SCOTT@ORCL> select count(distinct dbms_rowid.rowid_block_number(rowid)) as "blocks" from hoge;

blocks
----------
66667

ここまでが遅延ブロッククリーンアウトを意図的に起こすためのデータ作成SQLスクリプト

以降は、遅延ブロッククリーンアウトの発生等を見るための[g]v$sysstatと[g]v$sestatを取得するスクリプトと、[g]v$sesstatから特定のセッションを取得するためのクライアント情報をセットするスクリプト。


$ cat set_client_info.sql
BEGIN
DBMS_APPLICATION_INFO.SET_CLIENT_INFO('TargetSession');
END;
.
l
/

システム統計とセッション統計を取得して差分を見ていく必要があるので各統計のスナップショット取得用スクリプトが必要なわけですが、今回はマルチテナント環境。なので、システム統計はCDB全体から、セッション統計は該当するPDBかつ、前述のスクリプトでClient Infoが設定されているセッションに限定するスクリプトを作る必要があるんですよね。DBWRの動きも含めてみたいときって。少し多めに統計名を取得していますが、実際に重要なのはcleanout系の統計ですね。いくつかのテストケースを実施する上で合わせてみておきたい統計も事前に入れてあります:)


$ cat show_stat.sql
set linesize 400
set tab off
set pagesize 1000
col name for a60
col value for 999,999,999,999,999
SELECT
'sysstat' AS "SOURCE"
, name
, value
, con_id
FROM
gv$sysstat
WHERE
name IN (
'physical writes direct'
, 'physical writes from cache'
, 'physical writes non checkpoint'
, 'consistent gets'
, 'no work - consistent read gets'
, 'cleanouts and rollbacks - consistent read gets'
, 'cleanouts only - consistent read gets'
, 'deferred (CURRENT) block cleanout applications'
, 'immediate (CR) block cleanout applications'
, 'immediate (CURRENT) block cleanout applications'
, 'commit cleanout failures: block lost'
, 'commit cleanout failures: buffer being written'
, 'commit cleanout failures: callback failure'
, 'commit cleanout failures: cannot pin'
, 'commit cleanout failures: hot backup in progress'
, 'commit cleanout failures: write disabled'
, 'commit cleanouts'
, 'commit cleanouts successfully completed'
, 'db block changes'
, 'physical read requests'
, 'physical reads'
, 'physical reads direct'
, 'physical write requests'
, 'physical writes'
, 'physical writes direct'
, 'DBWR checkpoint buffers written'
, 'DBWR thread checkpoint buffers written'
, 'DBWR tablespace checkpoint buffers written'
, 'DBWR parallel query checkpoint buffers written'
, 'DBWR object drop buffers written'
, 'DBWR transaction table writes'
, 'DBWR undo block writes'
, 'DBWR revisited being-written buffer'
, 'DBWR lru scans'
, 'DBWR checkpoints'
, 'DBWR fusion writes'
, 'transaction tables consistent reads - undo records applied'
, 'transaction tables consistent read rollbacks'
)
UNION ALL
SELECT
'sesstat' AS "SOURCE"
, name
, value
, vsesstat.con_id
FROM
gv$sesstat vsesstat
inner join gv$statname vstatnam
on
vsesstat.statistic# = vstatnam.statistic#
WHERE
name IN (
'physical writes direct'
, 'physical writes from cache'
, 'physical writes non checkpoint'
, 'consistent gets'
, 'no work - consistent read gets'
, 'cleanouts and rollbacks - consistent read gets'
, 'cleanouts only - consistent read gets'
, 'deferred (CURRENT) block cleanout applications'
, 'immediate (CR) block cleanout applications'
, 'immediate (CURRENT) block cleanout applications'
, 'commit cleanout failures: block lost'
, 'commit cleanout failures: buffer being written'
, 'commit cleanout failures: callback failure'
, 'commit cleanout failures: cannot pin'
, 'commit cleanout failures: hot backup in progress'
, 'commit cleanout failures: write disabled'
, 'commit cleanouts'
, 'commit cleanouts successfully completed'
, 'db block changes'
, 'physical read requests'
, 'physical reads'
, 'physical reads direct'
, 'physical write requests'
, 'physical writes'
, 'physical writes direct'
, 'DBWR checkpoint buffers written'
, 'DBWR thread checkpoint buffers written'
, 'DBWR tablespace checkpoint buffers written'
, 'DBWR parallel query checkpoint buffers written'
, 'DBWR object drop buffers written'
, 'DBWR transaction table writes'
, 'DBWR undo block writes'
, 'DBWR revisited being-written buffer'
, 'DBWR lru scans'
, 'DBWR checkpoints'
, 'DBWR fusion writes'
, 'transaction tables consistent reads - undo records applied'
, 'transaction tables consistent read rollbacks'
)
and sid = (
select
sid
from
gv$session
where
username = upper('&1')
and client_info = 'TargetSession'
)
order by
4, 1, 2
;

undefine 1

ちなみに、統計を使って状況を確認する方法って意外に利用されているんですよね。日本だとあまり活用されてないようにも感じることは多いのですが、日々の統計を追っかけてると、どのメトリックが高く跳ね上がるのか把握できるので知ってて損することはないと思います:)

そういえば、Tanel PoderのSnapperもその手のツールではありますね。
Session Snapper
http://tech.e2sn.com/oracle-scripts-and-tools/session-snapper

私の過去のセッションでもElappsed Timeを見せないでチューニング効果を見てもらうネタとしてシステム統計を使ってたりします。
db tech showcase Tokyo 2013 - A35 特濃JPOUG:潮溜まりでジャブジャブ、SQLチューニング
https://www.slideshare.net/discus_hamburg/db-tech-showcase-tokyo-2013-a35-sql

少々脱線しますが、
最近、VirtualBox、なつかしー。なんて言う方もいますが、古いバージョンのOracleを残しておけるので、リリース毎の動きの差などを見たい場合は便利なのですよーw(クラウドだと強制アップグレードされちゃうので旧バージョンとの動作比較をネタにしたいときなどには向いてないw)

次回へつづく


東京では救急車のサイレンがまだまだ通常より多く聞こえます。。。。

Stay home, Stay Safe and Stay Hydrated.


古くて新しい? 遅延ブロッククリーンアウト (deferred block cleanout) #1


 

| | | コメント (0)

2020年7月26日 (日)

RDS Oracle 雑多なメモ#21 / DBMS_DATAPUMPパッケージ Schema mode de expdp/impdp - metadata onlyとデータのインポート

Previously, Mac De Oracle...

RDS Oracle 雑多なメモ#20 / DBMS_DATAPUMPパッケージ Schema mode de expdp/impdpの準備 SQL_FILEモードでDDL抜き出し

でした。

今回は、RDS Oracleへスキーマ単位のインポートでメタデータだけをインポートするにはどうするかというおはなし。

メタデータだけをインポートするにはこれまでに何度か話題にしたエクスポートされたオブジェクトとその階層を表すオブジェクトパスが重要な意味を持っています。

まずはフルエクスポートログにリストされるオブジェクトパスの例をご覧ください。

Processing object type DATABASE_EXPORT/EARLY_OPTIONS/VIEWS_AS_TABLES/TABLE_DATA
Processing object type DATABASE_EXPORT/NORMAL_OPTIONS/TABLE_DATA
Processing object type DATABASE_EXPORT/NORMAL_OPTIONS/VIEWS_AS_TABLES/TABLE_DATA
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/TABLE_DATA
Processing object type DATABASE_EXPORT/SCHEMA/PACKAGE_BODIES/PACKAGE/PACKAGE_BODY
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/INDEX/STATISTICS/FUNCTIONAL_INDEX/INDEX_STATISTICS
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type DATABASE_EXPORT/STATISTICS/MARKER
Processing object type DATABASE_EXPORT/PRE_SYSTEM_IMPCALLOUT/MARKER
Processing object type DATABASE_EXPORT/PRE_INSTANCE_IMPCALLOUT/MARKER
Processing object type DATABASE_EXPORT/TABLESPACE
Processing object type DATABASE_EXPORT/PROFILE
Processing object type DATABASE_EXPORT/SCHEMA/USER
Processing object type DATABASE_EXPORT/ROLE
Processing object type DATABASE_EXPORT/RADM_FPTM
Processing object type DATABASE_EXPORT/GRANT/SYSTEM_GRANT/PROC_SYSTEM_GRANT
Processing object type DATABASE_EXPORT/SCHEMA/GRANT/SYSTEM_GRANT
Processing object type DATABASE_EXPORT/SCHEMA/ROLE_GRANT
Processing object type DATABASE_EXPORT/SCHEMA/DEFAULT_ROLE
Processing object type DATABASE_EXPORT/SCHEMA/ON_USER_GRANT
Processing object type DATABASE_EXPORT/SCHEMA/TABLESPACE_QUOTA
Processing object type DATABASE_EXPORT/RESOURCE_COST
Processing object type DATABASE_EXPORT/TRUSTED_DB_LINK
Processing object type DATABASE_EXPORT/SCHEMA/SEQUENCE/SEQUENCE
Processing object type DATABASE_EXPORT/DIRECTORY/DIRECTORY
Processing object type DATABASE_EXPORT/DIRECTORY/GRANT/OWNER_GRANT/OBJECT_GRANT
Processing object type DATABASE_EXPORT/DIRECTORY/GRANT/WITH_GRANT_OPTION/OBJECT_GRANT
Processing object type DATABASE_EXPORT/CONTEXT
Processing object type DATABASE_EXPORT/SCHEMA/PUBLIC_SYNONYM/SYNONYM
Processing object type DATABASE_EXPORT/SCHEMA/TYPE/TYPE_SPEC
Processing object type DATABASE_EXPORT/SYSTEM_PROCOBJACT/PRE_SYSTEM_ACTIONS/PROCACT_SYSTEM
Processing object type DATABASE_EXPORT/SYSTEM_PROCOBJACT/PROCOBJ
Processing object type DATABASE_EXPORT/SYSTEM_PROCOBJACT/POST_SYSTEM_ACTIONS/PROCACT_SYSTEM
Processing object type DATABASE_EXPORT/SCHEMA/PROCACT_SCHEMA
Processing object type DATABASE_EXPORT/EARLY_OPTIONS/VIEWS_AS_TABLES/TABLE
Processing object type DATABASE_EXPORT/EARLY_POST_INSTANCE_IMPCALLOUT/MARKER
Processing object type DATABASE_EXPORT/NORMAL_OPTIONS/TABLE

上記オブジェクトパスツリーを一部省略しつつ見やすくすると以下のようなツリー構造になっているのが理解しやすいはず。

20200726-10936

図にも書いていますが、メタデータのみをインポートするにはデータを除外(Exclude)してしまえばよいわけです。

DATABASE_EXPORT/SCHEMA/TABLE/TABLE_DATA

また、同じツリーの階層にあるオブジェクトもスキーマレベルのインポートではインポートされないということも理解しやすいはずです。:)

なお、DATAPUMPが扱うオブジェクトパスの詳細は 
データベース・リファレンス - 6.238 SCHEMA_EXPORT_OBJECTS
を参照してくださいね。知らない方は多いですが、DATAPUMPと仲良くなるためには必要なビューです。



では、スキーマモードのメタデータオンリーインポートの仕込みから
同じデータベースへスキーマをインポートするので事前に削除しておきます。

BILL> drop user hoge cascade;

User dropped.

BILL> drop profile fizzbuzz;

Profile dropped.

BILL> drop role fizzbuzz;

Role dropped.

BILL> drop tablespace fizzbuzz including contents and datafiles;

Tablespace dropped.

たとえばこの状態でスキーマレベルのインポートを行ってしまうと、もう、想像はできていると思いますが、依存オブジェクト不足によりインポートは失敗します。
ユーザーのデフォルト表領域や依存するプロファイルが存在しない状態ではインポート時に実行されるCREATE USER文がエラーとなりインポートはエラーで終了することになります。

BILL> @imp_schema_metadataonly data_pump_dir fullexp hoge
Master table "BILL"."SYS_IMPORT_SCHEMA_03" successfully loaded/unloaded
Starting "BILL"."SYS_IMPORT_SCHEMA_03":
Processing object type DATABASE_EXPORT/SCHEMA/USER
ORA-39083: Object type USER:"HOGE" failed to create with error:
ORA-02380: profile FIZZBUZZ does not exist

Failing sql is:
CREATE USER "HOGE" IDENTIFIED BY VALUES

...中略...

TABLESPACE "FIZZBUZZ" TEMPORARY TABLESPACE "TEMP" PROFILE "FIZZBUZZ"

Processing object type DATABASE_EXPORT/SCHEMA/ROLE_GRANT
ORA-39083: Object type ROLE_GRANT failed to create with error:
ORA-01917: user or role 'HOGE' does not exist

Failing sql is:
GRANT "CONNECT" TO "HOGE"

ORA-39083: Object type ROLE_GRANT failed to create with error:
ORA-01924: role 'FIZZBUZZ' not granted or does not exist

Failing sql is:
GRANT "FIZZBUZZ" TO "HOGE"

Processing object type DATABASE_EXPORT/SCHEMA/DEFAULT_ROLE
ORA-39083: Object type DEFAULT_ROLE:"HOGE" failed to create with error:
ORA-01918: user 'HOGE' does not exist

...中略...

Job "BILL"."SYS_IMPORT_SCHEMA_03" completed with 7 error(s) at Fri Jul 24 17:51:45 2020 elapsed 0 00:00:03

PL/SQL procedure successfully completed.


では、上記のようなエラーを回避するため、前回フルエクスポートから取り出したDDL を実行して作成します。

BILL> CREATE BIGFILE TABLESPACE "FIZZBUZZ" DATAFILE
2 SIZE 104857600
3 AUTOEXTEND ON NEXT 104857600 MAXSIZE 1073741824
4 LOGGING ONLINE PERMANENT BLOCKSIZE 8192
5 EXTENT MANAGEMENT LOCAL AUTOALLOCATE DEFAULT
6 NOCOMPRESS SEGMENT SPACE MANAGEMENT AUTO;

Tablespace created.

BILL> CREATE ROLE "FIZZBUZZ";

Role created.

BILL> CREATE PROFILE "FIZZBUZZ"
2 LIMIT
3 COMPOSITE_LIMIT DEFAULT
4 SESSIONS_PER_USER DEFAULT
5 CPU_PER_SESSION DEFAULT
6 CPU_PER_CALL DEFAULT
7 LOGICAL_READS_PER_SESSION DEFAULT
8 LOGICAL_READS_PER_CALL DEFAULT
9 IDLE_TIME UNLIMITED
10 CONNECT_TIME DEFAULT
11 PRIVATE_SGA DEFAULT
12 FAILED_LOGIN_ATTEMPTS DEFAULT
13 PASSWORD_LIFE_TIME DEFAULT
14 PASSWORD_REUSE_TIME DEFAULT
15 PASSWORD_REUSE_MAX DEFAULT
16 PASSWORD_VERIFY_FUNCTION DEFAULT
17 PASSWORD_LOCK_TIME DEFAULT
18 PASSWORD_GRACE_TIME DEFAULT
19 INACTIVE_ACCOUNT_TIME DEFAULT ;

Profile created.


準備ができたので、メタデータオンリーのスキーマモードインポートを試してみます。
スキーマ以下のオブジェクトはインポート(統計情報をフィルタするのを忘れましたがw)できました。hoge表には1行だけデータがありますがデータはインポートされていないことが確認できるはずです。

BILL> @imp_schema_metadataonly data_pump_dir fullexp hoge
Master table "BILL"."SYS_IMPORT_SCHEMA_04" successfully loaded/unloaded
Starting "BILL"."SYS_IMPORT_SCHEMA_04":
Processing object type DATABASE_EXPORT/SCHEMA/USER
Processing object type DATABASE_EXPORT/SCHEMA/ROLE_GRANT
Processing object type DATABASE_EXPORT/SCHEMA/DEFAULT_ROLE
Processing object type DATABASE_EXPORT/SCHEMA/TABLESPACE_QUOTA
Processing object type DATABASE_EXPORT/SCHEMA/PROCACT_SCHEMA
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/TABLE
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type DATABASE_EXPORT/STATISTICS/MARKER
Job "BILL"."SYS_IMPORT_SCHEMA_04" successfully completed at Fri Jul 24 17:56:34 2020 elapsed 0 00:00:13

PL/SQL procedure successfully completed.

BILL> select username from dba_users where username='HOGE';

USERNAME
------------------------------
HOGE

BILL> select count(1) from hoge.hoge;

COUNT(1)
----------
0


では次にデータだけをインポートしてみます。
TABLE_DATAのみをインポートする方法と、スキーマインポートで表が存在する場合に、データをTRUNCATE後、データインポートする方法があります。(表が存在している場合のデフォルトの動作はデータインポートのスキップです)
以下の例では 通常 のスキーマインポートで表が存在する場合 、既存データをTRUNCATEして データをインポートする方法で行ってます。ユーザーが存在する場合にはCREATE USERは失敗し表が存在した場合にtruncate後にデータがインポートされます

BILL> @imp_schema data_pump_dir fullexp hoge
Master table "BILL"."SYS_IMPORT_SCHEMA_01" successfully loaded/unloaded
Starting "BILL"."SYS_IMPORT_SCHEMA_01":
Processing object type DATABASE_EXPORT/SCHEMA/USER
ORA-31684: Object type USER:"HOGE" already exists

Processing object type DATABASE_EXPORT/SCHEMA/ROLE_GRANT
Processing object type DATABASE_EXPORT/SCHEMA/DEFAULT_ROLE
Processing object type DATABASE_EXPORT/SCHEMA/TABLESPACE_QUOTA
Processing object type DATABASE_EXPORT/SCHEMA/PROCACT_SCHEMA
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/TABLE
Table "HOGE"."HOGE" exists and has been truncated.
Data will be loaded but all dependent metadata will be skipped due to table_exists_action of truncate
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/TABLE_DATA
. . imported "HOGE"."HOGE" 5.046 KB 1 rows
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type DATABASE_EXPORT/STATISTICS/MARKER
Job "BILL"."SYS_IMPORT_SCHEMA_01" completed with 1 error(s) at Fri Jul 24 18:01:30 2020 elapsed 0 00:00:05

PL/SQL procedure successfully completed.

BILL> select count(1) from hoge.hoge;

COUNT(1)
----------
1

スクリプトの例は以下のとおり
DBMS_DATAPUMP.METADATA_FILTER()プロシージャでデータを示すオブジェクトパス(DATABASE_EXPORT/SCHEMA/TABLE/TABLE_DATA)を除外しているところがポイント

BILL> !cat imp_schema_metadataonly.sql
SET VERIFY OFF
SET SERVEROUTPUT ON
DECLARE
v4Debug VARCHAR2(200);
cDirectory CONSTANT VARCHAR2(30) := UPPER('&1');
cDumpFileName CONSTANT VARCHAR2(64) := '&2'||'.dmp';
cLogFileName CONSTANT VARCHAR2(64) := '&2'||'.log';
cSchemaName CONSTANT VARCHAR2(30) := UPPER('&3');
cExcludePath CONSTANT VARCHAR2(128) := 'DATABASE_EXPORT/SCHEMA/TABLE/TABLE_DATA';
i NUMBER;
vDataPumpJobHandle NUMBER;
vProgress_ratio NUMBER;
vJobState VARCHAR2(30);
oLogEntry ku$_LogEntry;
oStatus ku$_Status;
BEGIN
DBMS_OUTPUT.ENABLE;

v4Debug := 'OPEN';
vDataPumpJobHandle
:= DBMS_DATAPUMP.OPEN (
operation => 'IMPORT'
,job_mode => 'SCHEMA'
,job_name => NULL
,remote_link => NULL
);

v4Debug := 'ADD_FILE - dumpfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cDumpFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE
);

v4Debug := 'ADD_FILE - logfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cLogFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE
);

v4Debug := 'METADATA_FILTER - schema name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'SCHEMA_LIST'
,value => '''' || cSchemaName || ''''
);

v4Debug := 'EXCLUDE_PATH='||cExcludePath;
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'EXCLUDE_PATH_LIST'
,value => '''' || cExcludePath || ''''
);

v4Debug := 'START_JOB';
DBMS_DATAPUMP.START_JOB (
handle => vDataPumpJobHandle
);

v4Debug := 'JOB_STATE';
vProgress_ratio := 0;
vJobState := 'UNDEFINED';
WHILE (vJobState != 'COMPLETED') AND (vJobState != 'STOPPED') LOOP
DBMS_DATAPUMP.GET_STATUS (
vDataPumpJobHandle
,DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR
+ DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS
+ DBMS_DATAPUMP.KU$_STATUS_WIP
,-1
,vJobState
,oStatus
);

IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_WIP) != 0)
THEN
oLogEntry := oStatus.wip;
ELSE
IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR) != 0)
THEN
oLogEntry := oStatus.error;
ELSE
oLogEntry := NULL;
END IF;
END IF;
IF oLogEntry IS NOT NULL
THEN
i := oLogEntry.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(oLogEntry(i).LogText);
i := oLogEntry.NEXT(i);
END LOOP;
END IF;
END LOOP;

DBMS_DATAPUMP.DETACH(vDataPumpJobHandle);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm());
DBMS_OUTPUT.PUT_LINE(v4Debug);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
UNDEFINE 3
SET SERVEROUTPUT OFF
SET VERIFY ON

DBMS_DATAPUMP.SET_PARAMETER()でTABLE_EXISTS_ACTION (表が存在する場合)でTRUNCATE(既存データのトランケート後データをインポート)する支持をしています。

BILL> !cat imp_schema.sql
SET VERIFY OFF
SET SERVEROUTPUT ON
DECLARE
v4Debug VARCHAR2(200);
cDirectory CONSTANT VARCHAR2(30) := UPPER('&1');
cDumpFileName CONSTANT VARCHAR2(64) := '&2'||'.dmp';
cLogFileName CONSTANT VARCHAR2(64) := '&2'||'.log';
cSchemaName CONSTANT VARCHAR2(30) := UPPER('&3');
i NUMBER;
vDataPumpJobHandle NUMBER;
vProgress_ratio NUMBER;
vJobState VARCHAR2(30);
oLogEntry ku$_LogEntry;
oStatus ku$_Status;
BEGIN
DBMS_OUTPUT.ENABLE;

v4Debug := 'OPEN';
vDataPumpJobHandle
:= DBMS_DATAPUMP.OPEN (
operation => 'IMPORT'
,job_mode => 'SCHEMA'
,remote_link => NULL
);

v4Debug := 'ADD_FILE - dumpfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cDumpFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE
);

v4Debug := 'ADD_FILE - logfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cLogFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE
);

v4Debug := 'METADATA_FILTER - schema name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'SCHEMA_LIST'
,value => '''' || cSchemaName || ''''
);

v4Debug := 'TABLE_EXISTS_ACTION=TRUNCATE';
DBMS_DATAPUMP.SET_PARAMETER (
handle => vDataPumpJobHandle
,name => 'TABLE_EXISTS_ACTION'
,value => 'TRUNCATE'
);

v4Debug := 'START_JOB';
DBMS_DATAPUMP.START_JOB (
handle => vDataPumpJobHandle
);

v4Debug := 'JOB_STATE';
vProgress_ratio := 0;
vJobState := 'UNDEFINED';
WHILE (vJobState != 'COMPLETED') AND (vJobState != 'STOPPED') LOOP
DBMS_DATAPUMP.GET_STATUS (
vDataPumpJobHandle
,DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR
+ DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS
+ DBMS_DATAPUMP.KU$_STATUS_WIP
,-1
,vJobState
,oStatus
);

IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_WIP) != 0)
THEN
oLogEntry := oStatus.wip;
ELSE
IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR) != 0)
THEN
oLogEntry := oStatus.error;
ELSE
oLogEntry := NULL;
END IF;
END IF;
IF oLogEntry IS NOT NULL
THEN
i := oLogEntry.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(oLogEntry(i).LogText);
i := oLogEntry.NEXT(i);
END LOOP;
END IF;
END LOOP;

DBMS_DATAPUMP.DETACH(vDataPumpJobHandle);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm());
DBMS_OUTPUT.PUT_LINE(v4Debug);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
UNDEFINE 3
SET SERVEROUTPUT OFF
SET VERIFY ON


今回は、上記2つのスクリプトに分けましたが、1つにまとめるように作り変えればより便利できますよ! それはみなさんへの宿題w 

ということで今回のネタはおしまい :)




Data Pumpも癖モノだよね〜w その1 - queryパラメーターの解析タイミング
Data Pumpも癖モノだよね〜w その2 - Materialized ViewをTableとして移行する
Data Pumpも癖モノだよね〜w その3 - dbms_job と dbms_scheduler との複雑な関係
Data Pumpも癖モノだよね〜w その4 - schemaモードでMviewを他のPDBへ複製
Data Pumpも癖モノだよね〜w その4と1/2 - schemaモードでMviewを他のPDBへ複製 (紛らわしいステータスw)
Data Pumpも癖モノだよね〜w その5 - schemaモードでMviewを他のPDBへ複製(オプジェクトパス de 絞り込み)
Data Pumpも癖モノだよね〜w その6 - schemaモードでMviewを他のPDBへ複製(オプジェクトパスが不足すると...


RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ
RDS Oracle 雑多なメモ#13 / FAQ
RDS Oracle 雑多なメモ#14 - おまけ / FAQ
RDS Oracle 雑多なメモ#15 - おまけのおまけ / FAQ
RDS Oracle 雑多なメモ#16 - 再び:) / FAQ
RDS Oracle 雑多なメモ#17/ FAQ
RDS Oracle 雑多なメモ#18 / DBMS_DATAPUMPパッケージ de expdp/impdp
RDS Oracle 雑多なメモ#19 FAQ / DBMS_DATAPUMPパッケージ de ジョブの停止
RDS Oracle 雑多なメモ#20 / DBMS_DATAPUMPパッケージ Schema mode de expdp/impdpの準備 SQL_FILEモードでDDL抜き出し

| | | コメント (0)

2020年7月25日 (土)

RDS Oracle 雑多なメモ#20 / DBMS_DATAPUMPパッケージ Schema mode de expdp/impdpの準備 SQL_FILEモードでDDL抜き出し

RDS Oracle 雑多なメモ#20 / DBMS_DATAPUMPパッケージ Schema mode de expdp/impdpの準備


Previously, Mac De Oracle...

RDS Oracle 雑多なメモ#19 FAQ / DBMS_DATAPUMPパッケージ de ジョブの停止

でした。

今回は、RDS Oracleへスキーマ単位でインポートを行う準備に必要などを。

実は、RDS Oracleでは、フルインポートはしないようにと記載されています。ということは、つまりスキーマモードでのインポートと、それ以下の単位でのインポートのみを行うこと、となっています。ここがポイント

詳細は以下マニュアルを参照ください。
Amazon RDS での Oracle へのデータのインポート


フルモードでインポートできない場合の注意点として、スキーマレベルでインポートする際に必要となる可能性のあるオブジェクトは事前に何がしかの方法で作成しておく必要があるということを意味します。
代表的なオブジェクトは以下、

  • ユーザーのデフォルトTABLESPACE(USERS表をそのまま利用しているケースは希だと思います)
  • ユーザーのPROFILE(パスワード有効期限管理などで利用しているケースは多いかも)
  • ROLE(直接付与権限以外はロールを付与して権限を管理することは多いはずです)
  • DIRECTORY(Oracle側で事前に作成しているディレクトリ以外のディレクトリへのアクセスが必要な場合にはディレクトリオブジェクトも事前に作成しておく必要があります)

さらに、スキーマ間で依存している場合、特に、他のスキーマのオブジェクト権限を付与する必要がある場合は、スキーマをインポートする順番にも気を遣う必要も出てきます。
インポート時にエラーが出ないものもありますが。。それは今回のテーマではないのでこの辺でw
(依存しているオブジェクトは事前にALL/DBA_DEPENDENCIESビューを利用して調査しておくとよいでしょうね)



では、DDLを取得する前に、ネタを仕込んでおきましょう。

表領域 fizzbuzz をデフォルト表領域とし、プロファイル fizzbuzz が設定されたユーザー hoge を作成します。
また、connectロールとresourceロールを付与された  fizzbuzz ロールが hoge に付与されているとします。


表領域の追加

BILL> create tablespace fizzbuzz datafile size 100m autoextend on maxsize 1g;

Tablespace created.

プロファイルの追加

BILL> create profile fizzbuzz limit idle_time unlimited;

Profile created.

ロールの追加

BILL> create role fizzbuzz;

Role created.

BILL> grant connect to fizzbuzz;

Grant succeeded.

BILL> grant resource to fizzbuzz;

Grant succeeded.


ユーザーの作成(デフォルト表領域とプロファイルの指定)

BILL> create user hoge identified by xxxxxxxxxxxxxxxx
2 default tablespace fizzbuzz
3 temporary tablespace temp
4 quota unlimited on fizzbuzz
5 profile fizzbuzz;

User created.


ロールの付与

BILL> grant fizzbuzz to hoge;

Grant succeeded.


上記のようなユーザーだとするとスキーマ単位のインポート前に表領域の作成、プロファイルの作成、ロールの作成が必要になりますよね。
(スキーマレベルのインポートでは依存オブジェクトが自動的にインポートされるわけでは無いので)

また、元のデータベースでOracle Managed Fileを利用した表領域では無い場合にはOracle Managed Fileを利用した表領域として再作成してあげる必要あります。(ここも重要。元のデータベースもOracle Managed fileを利用していればそのまま利用できます)

ということで、DBMS_METADATA.GET_DDL()または、フルエクスポートしたData Pumpのダンプファイルファイルから上記に該当するDDLを抜き出し、事前にオブジェクトを作成しておく必要がありますよね。
(DDLソースコード管理されているのであれば、それらを再利用することも可能だとは思いますが、世の中広いのでそんな管理されていないところも多いでしょうし。DDLを取り出す複数の方法は覚えてて損は無いですよ :)


今回は、フルエクスポートダンプファイルがあるので、そこからData Pump APIを利用して取り出してみようと思います。(DBMS_METADATA.GET_DDL()の方が楽だとは思います。個人的にはw)

フルダンプからDDLを取り出す際でも、重要なのがオブジェクトパス(このパスの階層を意識していないと思わぬ失敗をするのは昔のエントリの通り)ですが、今回は比較的シンプルなので確認してみましょう。


事前にエクスポートのログから以下のパスを探し出しておきます。
(今回の例では、表領域とプロファイル、それにロールへのオブジェクトパスです。以下がそのオブジェクトパス)

Processing object type DATABASE_EXPORT/TABLESPACE
Processing object type DATABASE_EXPORT/PROFILE
Processing object type DATABASE_EXPORT/ROLE

余談ですが、同じ階層オブジェクトパスにスキーマオブジェクトのパス(DATABASE_EXPORT/SCHEMA)が存在しています。同じ階層なので、スキーマレベルのインポートではTABLESPACE/ROLE/PROFILEはインポートされることはありません。(この階層構造を理解できれば楽)
DATABASE_EXPORT/SCHEMA以下がインポートの対象になるので、メタデータフィルターでEXCLUDE または INCLUDEするか制御できることになります。TABLESPACE,PROFILEやROLEはSCHEMAと同レベルなのでそもそも対象外となりフィルタすることはできません。

Processing object type DATABASE_EXPORT/SCHEMA/

理屈がわかれば癖の強いDATAPUMPなんてw 

では、DDLを取り出してみましょう。

前提のオブジェクトを含むフルエクスポートダンプファイル fullexp.dmp は事前に取得済みであるとします。

BILL> @extract_ddl data_pump_dir fullexp DATABASE_EXPORT/TABLESPACE'',''DATABASE_EXPORT/PROFILE'',''DATABASE_EXPORT/ROLE
Master table "BILL"."SYS_SQL_FILE_FULL_17" successfully loaded/unloaded
Starting "BILL"."SYS_SQL_FILE_FULL_17":
Processing object type DATABASE_EXPORT/TABLESPACE
Processing object type DATABASE_EXPORT/PROFILE
Processing object type DATABASE_EXPORT/ROLE
Job "BILL"."SYS_SQL_FILE_FULL_17" successfully completed at Sun Jul 19 19:12:00 2020 elapsed 0 00:00:02

PL/SQL procedure successfully completed.

取り出したDDLを確認します

BILL> @cat_file data_pump_dir fullexp.txt

TEXT
-----------------------------------------------------------------------------

...中略...

-- new object type path: DATABASE_EXPORT/TABLESPACE
...中略...
CREATE BIGFILE TABLESPACE "FIZZBUZZ" DATAFILE
SIZE 104857600
AUTOEXTEND ON NEXT 104857600 MAXSIZE 1073741824
LOGGING ONLINE PERMANENT BLOCKSIZE 8192
EXTENT MANAGEMENT LOCAL AUTOALLOCATE DEFAULT
NOCOMPRESS SEGMENT SPACE MANAGEMENT AUTO;
-- new object type path: DATABASE_EXPORT/PROFILE
...中略...
CREATE PROFILE "FIZZBUZZ"
LIMIT
COMPOSITE_LIMIT DEFAULT
SESSIONS_PER_USER DEFAULT
CPU_PER_SESSION DEFAULT
CPU_PER_CALL DEFAULT
LOGICAL_READS_PER_SESSION DEFAULT
LOGICAL_READS_PER_CALL DEFAULT
IDLE_TIME UNLIMITED
CONNECT_TIME DEFAULT
PRIVATE_SGA DEFAULT
FAILED_LOGIN_ATTEMPTS DEFAULT
PASSWORD_LIFE_TIME DEFAULT
PASSWORD_REUSE_TIME DEFAULT
PASSWORD_REUSE_MAX DEFAULT
PASSWORD_VERIFY_FUNCTION DEFAULT
PASSWORD_LOCK_TIME DEFAULT
PASSWORD_GRACE_TIME DEFAULT
INACTIVE_ACCOUNT_TIME DEFAULT ;
-- new object type path: DATABASE_EXPORT/ROLE
...中略...
CREATE ROLE "FIZZBUZZ";
...中略...


スキーマ hogeインポート前に作成が必要なオブジェクトのDDLを取得できました。


これでスキーマレベルのインポート準備完了。
つづく。

参考)
DDLをダンプファイルから取り出すスクリプト例は以下の通り。DBMS_DATAPUMP.OPEN()関数でoperationに’SQL_FILE'を渡しているところとDBMS_DATAPUMP.METADATA_FILTER()プロシージャで取り出すDDLのオブジェクトパスリストを渡している箇所がポイントです
PL/SQLパッケージおよびタイプ・リファレンス - 47 DBMS_DATAPUMP

BILL> !cat extract_ddl.sql
SET VERIFY OFF
SET SERVEROUTPUT ON
DECLARE
v4Debug VARCHAR2(50);
cDirectory CONSTANT VARCHAR2(30) := UPPER('&1');
cDumpFileName CONSTANT VARCHAR2(64) := '&2'||'.dmp';
cLogFileName CONSTANT VARCHAR2(64) := '&2'||'.log';
cDdlFileName CONSTANT VARCHAR2(64) := '&2'||'.txt';
cPathList CONSTANT VARCHAR2(4000) := UPPER('&3');
i NUMBER;
vDataPumpJobHandle NUMBER;
vProgress_ratio NUMBER;
vJobState VARCHAR2(30);
oLogEntry ku$_LogEntry;
oStatus ku$_Status;
BEGIN
DBMS_OUTPUT.ENABLE;

v4Debug := 'OPEN';
vDataPumpJobHandle
:= DBMS_DATAPUMP.OPEN (
operation => 'SQL_FILE'
,job_mode => 'FULL'
,remote_link => NULL
);

v4Debug := 'ADD_FILE - dumpfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cDumpFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE
);

v4Debug := 'ADD_FILE - logfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cLogFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE
);

v4Debug := 'ADD_FILE - Ddlfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cDdlFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_SQL_FILE
);

v4Debug := 'METADATA_FILTER - schema name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'INCLUDE_PATH_LIST'
,value => '''' || cPathList || ''''
);

v4Debug := 'START_JOB';
DBMS_DATAPUMP.START_JOB (
handle => vDataPumpJobHandle
);

v4Debug := 'JOB_STATE';
vProgress_ratio := 0;
vJobState := 'UNDEFINED';
WHILE (vJobState != 'COMPLETED') AND (vJobState != 'STOPPED') LOOP
DBMS_DATAPUMP.GET_STATUS (
vDataPumpJobHandle
,DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR
+ DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS
+ DBMS_DATAPUMP.KU$_STATUS_WIP
,-1
,vJobState
,oStatus
);

IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_WIP) != 0)
THEN
oLogEntry := oStatus.wip;
ELSE
IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR) != 0)
THEN
oLogEntry := oStatus.error;
ELSE
oLogEntry := NULL;
END IF;
END IF;
IF oLogEntry IS NOT NULL
THEN
i := oLogEntry.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(oLogEntry(i).LogText);
i := oLogEntry.NEXT(i);
END LOOP;
END IF;
END LOOP;

DBMS_DATAPUMP.DETACH(vDataPumpJobHandle);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm());
DBMS_OUTPUT.PUT_LINE(v4Debug);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
UNDEFINE 3
SET SERVEROUTPUT OFF
SET VERIFY ON




やっと、いろいろ落ち着いたかと思ったら次から次に似たような事案が落ちてくる今日この頃w。
ということで、しばらくは機嫌悪いかも、ガルーーーーーっw



Data Pumpも癖モノだよね〜w その1 - queryパラメーターの解析タイミング
Data Pumpも癖モノだよね〜w その2 - Materialized ViewをTableとして移行する
Data Pumpも癖モノだよね〜w その3 - dbms_job と dbms_scheduler との複雑な関係
Data Pumpも癖モノだよね〜w その4 - schemaモードでMviewを他のPDBへ複製
Data Pumpも癖モノだよね〜w その4と1/2 - schemaモードでMviewを他のPDBへ複製 (紛らわしいステータスw)
Data Pumpも癖モノだよね〜w その5 - schemaモードでMviewを他のPDBへ複製(オプジェクトパス de 絞り込み)
Data Pumpも癖モノだよね〜w その6 - schemaモードでMviewを他のPDBへ複製(オプジェクトパスが不足すると...


RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ
RDS Oracle 雑多なメモ#13 / FAQ
RDS Oracle 雑多なメモ#14 - おまけ / FAQ
RDS Oracle 雑多なメモ#15 - おまけのおまけ / FAQ
RDS Oracle 雑多なメモ#16 - 再び:) / FAQ
RDS Oracle 雑多なメモ#17/ FAQ
RDS Oracle 雑多なメモ#18 / DBMS_DATAPUMPパッケージ de expdp/impdp
RDS Oracle 雑多なメモ#19 FAQ / DBMS_DATAPUMPパッケージ de ジョブの停止

| | | コメント (0)

2020年7月20日 (月)

RDS Oracle 雑多なメモ#19 FAQ / DBMS_DATAPUMPパッケージ de ジョブの停止

Previously, Mac De Oracle...

RDS Oracle 雑多なメモ#18 / DBMS_DATAPUMPパッケージ de expdp/impdp

DBMS_DATAPUMPパッケージを利用した Full Export スクリプトと、ディレクトリ名は大文字で渡すことというマニュアルに記載されていない、発生するとしばらく道に迷ってしまいそうなエラーが返るというお話でした。

今日は、その続き、Data PumpジョブはコマンドラインであってもCTRL+Cでは停止することはできないことはみなさんご存知の通りだとは思います。

正しい停止手順は以下の通り

DBA_DATAPUMP_JOBSで停止したいDATAPUMPジョブ名とジョブオーナー名を確認する

4.199 DBA_DATAPUMP_JOBS
https://docs.oracle.com/cd/F19136_01/refrn/DBA_DATAPUMP_JOBS.html#GUID-141B1FA2-9DE4-4EAF-8270-630E68431DDA


expdp/impdpのattachコマンド、または、DBMS_DATAPUMP.ATTACH()ファンクションで該当ジョブへattachする

3.4.4 ATTACH (expdp/impdpとも同じ対話コマンド)
https://docs.oracle.com/cd/F19136_01/sutil/datapump-import-utility.html#GUID-ADF15D47-FD19-4794-A5C4-685740AA04F9

47.5.2 ATTACHファンクション
https://docs.oracle.com/cd/F19136_01/arpls/DBMS_DATAPUMP.html#GUID-E073EA12-363D-4A6B-9596-1E1D40EA747C


expdp/impdpのkill_jobコマンド、または、DBMS_DATAPUMP.STOP_JOB()プロシージャで該当ジョブを停止する

2.5.7 KILL_JOB (expdp/impdpとも同じ対話コマンド)
https://docs.oracle.com/cd/F19136_01/sutil/oracle-data-pump-export-utility.html#GUID-9DA12603-B6FA-4631-8DFA-B75466CAF178

47.5.16 STOP_JOBプロシージャ
https://docs.oracle.com/cd/F19136_01/arpls/DBMS_DATAPUMP.html#GUID-9CE265FA-F9C6-4816-8AE0-AD0BEF3DC3CA


コマンドラインでもAPI経由でも手順は全く同じ。利用するツールが異なるだけでなので、まずは手順を覚えることが大切なんですよね。これ。
あとは、コマンドラインでは以下のマニュアルを参照し、具体的なコマンドオプションを知る
DBMS_DATAPUMPプロシージャを利用する場合は以下のマニュアルを参照し、どのようなスクリプトになるかを知る

あとは、実践のみですよー、みなさん! :)


では、DBMS_DATAPUMPパッケージでの例を(すでにエクスポートは実行中という状況からはじめます)

BILL> @show_datapump_jobs

OWNER_NAME JOB_NAME STATE JOB_MODE OPERATION
----------------- ------------------------- ------------- ------------- -------------
BILL SYS_EXPORT_FULL_01 EXECUTING FULL EXPORT



BILL> @cancel_datapump_job SYS_EXPORT_FULL_01 BILL

PL/SQL procedure successfully completed.


BILL> @show_datapump_jobs

no rows selected

BILL> @ls_dir data_pump_dir

FILENAME TYPE FILESIZE MTIME
--------------------------- ---------- ---------- ---------
fullexp.log file 2761 18-JUL-20
datapump/ directory 4096 18-JUL-20


BILL> @cat_file data_pump_dir fullexp.log

TEXT
----------------------------------------------------------------------------------------
FLASHBACK automatically enabled to preserve database integrity.
Starting "BILL"."SYS_EXPORT_FULL_01":
Processing object type DATABASE_EXPORT/EARLY_OPTIONS/VIEWS_AS_TABLES/TABLE_DATA
Processing object type DATABASE_EXPORT/NORMAL_OPTIONS/TABLE_DATA
Processing object type DATABASE_EXPORT/NORMAL_OPTIONS/VIEWS_AS_TABLES/TABLE_DATA
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/TABLE_DATA
Processing object type DATABASE_EXPORT/SCHEMA/PACKAGE_BODIES/PACKAGE/PACKAGE_BODY
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/INDEX/STATISTICS/FUNCTIONAL_INDEX/INDEX_STATISTICS
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type DATABASE_EXPORT/STATISTICS/MARKER
Processing object type DATABASE_EXPORT/PRE_SYSTEM_IMPCALLOUT/MARKER
Processing object type DATABASE_EXPORT/PRE_INSTANCE_IMPCALLOUT/MARKER
Processing object type DATABASE_EXPORT/TABLESPACE
Processing object type DATABASE_EXPORT/PROFILE
Processing object type DATABASE_EXPORT/SCHEMA/USER
Processing object type DATABASE_EXPORT/ROLE
Processing object type DATABASE_EXPORT/RADM_FPTM
Processing object type DATABASE_EXPORT/GRANT/SYSTEM_GRANT/PROC_SYSTEM_GRANT

...中略...

Processing object type DATABASE_EXPORT/NORMAL_OPTIONS/TABLE
Job "BILL"."SYS_EXPORT_FULL_01" stopped due to fatal error at Sat Jul 18 10:02:49 2020 elapsed 0 00:00:50

41 rows selected.


BILL> @rm_file data_pump_dir fullexp.log
DATA_PUMP_DIR/fullexp.log removed.

PL/SQL procedure successfully completed.

BILL> @ls_dir data_pump_dir

FILENAME TYPE FILESIZE MTIME
--------------------------- ---------- ---------- ---------
datapump/ directory 4096 18-JUL-20

停止できましたー


今回利用したDATAPUMPジョブ停止スクリプトの例は以下のとおり。

BILL> !cat show_datapump_jobs.sql

col owner_name for a30
col job_name for a30
col state for a30
col job_mode for a30
col operation for a30
SELECT
owner_name
, job_name
, state
, job_mode
, operation
FROM
DBA_DATAPUMP_JOBS
ORDER BY
owner_name
, job_name
, state
;


BILL> !cat cancel_datapump_job.sql

DECLARE
hdl NUMBER;
BEGIN
hdl := DBMS_DATAPUMP.ATTACH(
job_name => UPPER('&1')
,job_owner => UPPER('&2')
);

DBMS_DATAPUMP.STOP_JOB(
handle => hdl
, immediate => 1
, keep_master => 0
);
END;
/


ls_dir.sql, rm_file.sql そして、cat_file.sqlの例は過去のエントリを参照してみてください。

ls_dir.sql
https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2018/09/rds-oracle-3-fa.html

rm_file.sql
https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2018/09/rds-oracle-7-fa.html

cat_file.sql
https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2018/09/rds-oracle-4-fa.html

では、次回へつづく。




Data Pumpも癖モノだよね〜w その1 - queryパラメーターの解析タイミング
Data Pumpも癖モノだよね〜w その2 - Materialized ViewをTableとして移行する
Data Pumpも癖モノだよね〜w その3 - dbms_job と dbms_scheduler との複雑な関係
Data Pumpも癖モノだよね〜w その4 - schemaモードでMviewを他のPDBへ複製
Data Pumpも癖モノだよね〜w その4と1/2 - schemaモードでMviewを他のPDBへ複製 (紛らわしいステータスw)
Data Pumpも癖モノだよね〜w その5 - schemaモードでMviewを他のPDBへ複製(オプジェクトパス de 絞り込み)
Data Pumpも癖モノだよね〜w その6 - schemaモードでMviewを他のPDBへ複製(オプジェクトパスが不足すると...


RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ
RDS Oracle 雑多なメモ#13 / FAQ
RDS Oracle 雑多なメモ#14 - おまけ / FAQ
RDS Oracle 雑多なメモ#15 - おまけのおまけ / FAQ
RDS Oracle 雑多なメモ#16 - 再び:) / FAQ
RDS Oracle 雑多なメモ#17/ FAQ
RDS Oracle 雑多なメモ#18 / DBMS_DATAPUMPパッケージ de expdp/impdp

| | | コメント (0)

2020年7月19日 (日)

RDS Oracle 雑多なメモ#18 / DBMS_DATAPUMPパッケージ de expdp/impdp

さて、COVID-19の感染者も再び増加傾向にある今日この頃ですが、みなさま、お変わりありませんか?

半年ぶり以上ぶりのエントリです。(2020年のOracle ACEのKPIカウントもスタートしたのでやっと再始動)
再始動第一段目は、癖者の極み、Data Pumpネタで。

何年か前にも癖者だというエントリは書いたことがありますが、今回はコマンドラインではなく、DBMS_DATAPUMPパッケージ。そう、APIが主人公です

Data Pumpも癖モノだよね〜w その6 - schemaモードでMviewを他のPDBへ複製(オプジェクトパスが不足すると...) https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2017/04/data-pumpw6---s.html

そして最近では、

DS Oracle 雑多なメモ#1 / FAQ 〜 https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2018/09/rds-oracle-1-fa.html

から

RDS Oracle 雑多なメモ#17/ FAQ https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2019/02/rds-oracle-17-f.html


何度かData Pumpネタを取り上げてきました、今回は RDS Oracleでは需要が多そうな
DBMS_DATAPUMPパッケージからAPIを利用してexpdp/impdp相当の操作に特化していくつご紹介しておきます。

普段はコマンドラインだけでした操作したことないData Pumpですが、いざDBMS_DATAPUMPパッケージしか使えないという状況になって、涙目状態は辛いですよね。
そんなことにならないように普段から慣れておくとかスクリプト集を用意しておくと良いと思います。

コマンドラインでも癖もとだよねぇ〜というネタのとおりDBMS_DATAPUMPパッケージでも一癖あるのはおなじですw
とはいえ、コマンドラインである程度ポイントを抑えていればある程度は感でいけますよ。

では、さっそく、DBMS_DATAPUMPパッケージの準備運動として、Data Pump Full exportを試してみましょう。
以前も同類のスクリプトは公開済みなので、過去のエントリを読んだことがあるなら簡単なはず:)

環境 Oracle Client 19c (SQL*Plusパッケージが含まれているパッケージ) https://www.oracle.com/jp/database/technologies/instant-client.html

AWS RDS Oracle Database 19c https://aws.amazon.com/jp/console/

 

実行例)


BILL> @expdp_full data_pump_dir fullexp
FLASHBACK automatically enabled to preserve database integrity.
Starting "BILL"."SYS_EXPORT_FULL_01":
Processing object type DATABASE_EXPORT/EARLY_OPTIONS/VIEWS_AS_TABLES/TABLE_DATA
Processing object type DATABASE_EXPORT/NORMAL_OPTIONS/TABLE_DATA
Processing object type DATABASE_EXPORT/NORMAL_OPTIONS/VIEWS_AS_TABLES/TABLE_DATA
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/TABLE_DATA
Processing object type DATABASE_EXPORT/SCHEMA/PACKAGE_BODIES/PACKAGE/PACKAGE_BODY
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/INDEX/STATISTICS/FUNCTIONAL_INDEX/INDEX_STATISTICS
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type DATABASE_EXPORT/STATISTICS/MARKER
Processing object type DATABASE_EXPORT/PRE_SYSTEM_IMPCALLOUT/MARKER
Processing object type DATABASE_EXPORT/PRE_INSTANCE_IMPCALLOUT/MARKER
Processing object type DATABASE_EXPORT/TABLESPACE
Processing object type DATABASE_EXPORT/PROFILE
Processing object type DATABASE_EXPORT/SCHEMA/USER
Processing object type DATABASE_EXPORT/ROLE
Processing object type DATABASE_EXPORT/RADM_FPTM
Processing object type DATABASE_EXPORT/GRANT/SYSTEM_GRANT/PROC_SYSTEM_GRANT

...中略...

Processing object type DATABASE_EXPORT/SCHEMA/VIEW/VIEW
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/INDEX/INDEX
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/INDEX/FUNCTIONAL_INDEX/INDEX
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/CONSTRAINT/CONSTRAINT
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/CONSTRAINT/REF_CONSTRAINT
Processing object type DATABASE_EXPORT/SCHEMA/TABLE/TRIGGER
Processing object type DATABASE_EXPORT/SCHEMA/EVENT/TRIGGER
Processing object type DATABASE_EXPORT/FINAL_POST_INSTANCE_IMPCALLOUT/MARKER
Processing object type DATABASE_EXPORT/SCHEMA/POST_SCHEMA/PROCACT_SCHEMA
Processing object type DATABASE_EXPORT/AUDIT_UNIFIED/AUDIT_POLICY_ENABLE
Processing object type DATABASE_EXPORT/POST_SYSTEM_IMPCALLOUT/MARKER
. . exported "SYS"."KU$_USER_MAPPING_VIEW" 5.921 KB 28 rows

...中略...

. . exported "SCOTT"."TAB31" 541.6 MB 2000000 rows
. . exported "SCOTT"."TAB311" 521.6 MB 2000000 rows
. . exported "SCOTT"."TAB3" 267.9 MB 1000000 rows

...中略...

. . exported "HOGE"."HOGE" 5.093 KB 1 rows

...中略...

Master table "BILL"."SYS_EXPORT_FULL_01" successfully loaded/unloaded
******************************************************************************
Dump file set for BILL.SYS_EXPORT_FULL_01 is:
/rdsdbdata/datapump/fullexp.dmp
Job "BILL"."SYS_EXPORT_FULL_01" successfully completed at Sat Jul 18 10:25:02 2020 elapsed 0 00:04:25

PL/SQL procedure successfully completed.

Elapsed: 00:04:27.32
BILL>
BILL> @ls_dir data_pump_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- ---------
datapump/ directory 4096 18-JUL-20
fullexp.log file 9071 18-JUL-20
fullexp.dmp file 1461907456 18-JUL-20

Elapsed: 00:00:00.15

なお、上記で利用している ls_dir.sql は以下エントリを参照のこと。 
RDS Oracle 雑多なメモ#3 / FAQ https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2018/09/rds-oracle-3-fa.html

RDS Oracle 雑多なメモ#9 / FAQでも紹介しているスクリプトを多少変更してあります。
Full ExportなのでDBMS_DATAPUMP.OPEN()のjob_modeパラメータが'FULL'になっている点と、DBMS_DATAPUMP.METADATA_FILTER()でEXCLUDEやINCLUDEしていない。つまり、Full Exportになっています。
https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2018/09/rds-oracle-9-fa.html

参考)PL/SQLパッケージおよびタイプ・リファレンス
https://docs.oracle.com/cd/F19136_01/arpls/DBMS_DATAPUMP.html#GUID-AEA7ED80-DB4A-4A70-B199-592287206348

なお、マニュアルには書かれてないのですが、ディレクトリ名は、"大文字" にするのがポイントです。Case Sensitiveで大文字しか認識しません。スクリプトを注意深く見ていただけると、UPPER()を使っている部分(赤字部分)があることに気づくとおもいます。もし、小文字で渡してしまうとDBMS_DATAPUMPパッケージの癖の強いエラーメッセージを受け取りことになり、意味わからなーーーーい。としばらくは迷子になってしまうことでしょう。後半で、UPPER()なしで小文字で渡してしまった場合にはどのようなエラーが返されるのかお見せしたいと思います。

BILL> !cat expdp_full.sql

SET VERIFY OFF
SET SERVEROUTPUT ON
DECLARE
v4Debug VARCHAR2(50);
cDirectory CONSTANT VARCHAR2(30) := UPPER('&1');
cDumpFileName CONSTANT VARCHAR2(128) := '&2'||'.dmp';
cLogFileName CONSTANT VARCHAR2(128) := '&2'||'.log';
i NUMBER;
vDataPumpJobHandle NUMBER;
vProgress_ratio NUMBER;
vJobState VARCHAR2(30);
oLogEntry ku$_LogEntry;
oStatus ku$_Status;
BEGIN
DBMS_OUTPUT.ENABLE;

v4Debug := 'OPEN';
vDataPumpJobHandle
:= DBMS_DATAPUMP.OPEN (
operation => 'EXPORT'
,job_mode => 'FULL'
,remote_link => NULL
,job_name => NULL
,version => 'LATEST'
);

v4Debug := 'ADD_FILE - dumpfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cDumpFileName
,directory => cDirectory(
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE
);

v4Debug := 'ADD_FILE - logfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cLogFileName
,directory => cDirectory(
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE
);

v4Debug := 'START_JOB';
DBMS_DATAPUMP.START_JOB (
handle => vDataPumpJobHandle
);

v4Debug := 'JOB_STATE';
vProgress_ratio := 0;
vJobState := 'UNDEFINED';
WHILE (vJobState != 'COMPLETED') AND (vJobState != 'STOPPED') LOOP
DBMS_DATAPUMP.GET_STATUS (
vDataPumpJobHandle
,DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR
+ DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS
+ DBMS_DATAPUMP.KU$_STATUS_WIP
,-1
,vJobState
,oStatus
);

IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_WIP) != 0)
THEN
oLogEntry := oStatus.wip;
ELSE
IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR) != 0)
THEN
oLogEntry := oStatus.error;
ELSE
oLogEntry := NULL;
END IF;
END IF;
IF oLogEntry IS NOT NULL
THEN
i := oLogEntry.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(oLogEntry(i).LogText);
i := oLogEntry.NEXT(i);
END LOOP;
END IF;
END LOOP;

DBMS_DATAPUMP.DETACH(vDataPumpJobHandle);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm());
DBMS_OUTPUT.PUT_LINE(v4Debug);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
SET SERVEROUTPUT OFF
SET VERIFY ON

 

では、少しだけ前振りしていた、もしも、ディレクトリ名を小文字で渡してしまったら....以下のようなエラーが返されます。

なかなかの癖者なエラーメッセージですよね。このエラーですぐに問題点に気づけたら達人に域ですw (癖者な人よりは扱いやすいとは思いますがw ただのAPIなので)

BILL> @expdp_full data_pump_dir fullexp2
old 3: cDirectory CONSTANT VARCHAR2(30) := '&1';
new 3: cDirectory CONSTANT VARCHAR2(30) := 'data_pump_dir';
old 4: cDumpFileName CONSTANT VARCHAR2(64) := '&2'||'.dmp';
new 4: cDumpFileName CONSTANT VARCHAR2(64) := 'fullexp2'||'.dmp';
old 5: cLogFileName CONSTANT VARCHAR2(64) := '&2'||'.log';
new 5: cLogFileName CONSTANT VARCHAR2(64) := 'fullexp2'||'.log';
ORA-39001: invalid argument value
ADD_FILE - dumpfile
DECLARE
*
ERROR at line 1:
ORA-39001: invalid argument value
ORA-06512: at line 86
ORA-06512: at "SYS.DBMS_SYS_ERROR", line 79
ORA-06512: at "SYS.DBMS_DATAPUMP", line 4929
ORA-06512: at "SYS.DBMS_DATAPUMP", line 5180
ORA-06512: at line 26

では、次回へつづく。


Data Pumpも癖モノだよね〜w その1 - queryパラメーターの解析タイミングData Pumpも癖モノだよね〜w その2 - Materialized ViewをTableとして移行するData Pumpも癖モノだよね〜w その3 - dbms_job と dbms_scheduler との複雑な関係Data Pumpも癖モノだよね〜w その4 - schemaモードでMviewを他のPDBへ複製Data Pumpも癖モノだよね〜w その4と1/2 - schemaモードでMviewを他のPDBへ複製 (紛らわしいステータスw)
Data Pumpも癖モノだよね〜w その5 - schemaモードでMviewを他のPDBへ複製(オプジェクトパス de 絞り込み)
Data Pumpも癖モノだよね〜w その6 - schemaモードでMviewを他のPDBへ複製(オプジェクトパスが不足すると...

 

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ
RDS Oracle 雑多なメモ#13 / FAQ
RDS Oracle 雑多なメモ#14 - おまけ / FAQ
RDS Oracle 雑多なメモ#15 - おまけのおまけ / FAQ
RDS Oracle 雑多なメモ#16 - 再び:) / FAQ
RDS Oracle 雑多なメモ#17/ FAQ

 

| | | コメント (0)

2019年8月25日 (日)

FAQ / PL/SQL PACKAGEでパプリックスコープを持つ定数をSQL文中で利用するには...

かなーり、ご無沙汰しておりました。(本業でいっぱいいっぱいで、という言い訳はこれぐらいにしてw) 偶に聞かれることがあるので、FAQネタから。 パッケージでパブリックなスコープを持つ定数は無名PL/SQLブロックやパッケージ、プロシージャ、ファンクションでしか参照できないんですよねー 例えば、DBMS_CRYPTOパッケージでHASHファンクションを利用してSH-256を作成したいなーと思って、

39.4 DBMS_CRYPTOのアルゴリズム
https://docs.oracle.com/cd/F19136_01/arpls/DBMS_CRYPTO.html#GUID-CE3CF17D-E781-47CB-AEE7-19A9B2BCD3EC
DBMS_CRYPTO.HASH()は関数なのでSQL文から呼びたーい、と以下のような使い方をすると...

SQL> SELECT DBMS_CRYPTO.HASH(TO_CLOB('hoge'), DBMS_CRYPTO.HASH_SH2569) AS "SH-256" FROM dual;
SELECT DBMS_CRYPTO.HASH(TO_CLOB('hoge'), DBMS_CRYPTO.HASH_SH2569) AS "SH-256" FROM dual
*
ERROR at line 1:
ORA-00904: "DBMS_CRYPTO"."HASH_SH2569": invalid identifier


SQL>
SQL> select DBMS_CRYPTO.HASH_SH256 from dual;
select DBMS_CRYPTO.HASH_SH256 from dual
*
ERROR at line 1:
ORA-06553: PLS-221: 'HASH_SH256' is not a procedure or is undefined
見事にエラーとなるわけです。 DBMS_CRYPTO.HASH_SH256は、パッケージファンクションではないので...利用可能なのはPL/SQLでのみ。
SQL> set serveroutput on
SQL>
¥SQL>
SQL> begin
2 dbms_output.put_line('DBMS_CRYPTO.HASH_SH256 : ' || DBMS_CRYPTO.HASH_SH256);
3 end;
4 /
DBMS_CRYPTO.HASH_SH256 : 4

PL/SQL procedure successfully completed.
SQL文で活用する為には、ファンクションでラップする必要があります。 以下のように。
SQL> l
1 CREATE OR REPLACE FUNCTION get_hash_sh256_type
2 RETURN NUMBER
3 AS
4 BEGIN
5 RETURN DBMS_CRYPTO.HASH_SH256;
6* END;
SQL> /

Function created.

SQL>
冒頭でエラーとなっていたSQL文をDBMS_CRYPTO.HASH_SH256を返すファンクションを使うように書き換えると、 はい、できました。
SQL> l
1 SELECT
2 DBMS_CRYPTO.HASH(
3 TO_CLOB('hoge')
4 , get_hash_sh256_type()
5 ) AS "SH-256"
6 FROM
7* dual
SQL> /

SH-256
--------------------------------------------------------------------------------
ECB666D778725EC97307044D642BF4D160AABB76F56C0069C71EA25B1E926825

SQL>


露店の焼きそばと焼き鳥を食べつつ、晩夏の夏祭りと、涼しい朝晩の気温で熟睡可能な山形より。 では、では。

| | | コメント (0)

2019年3月22日 (金)

ORA_HASH()を使ってリストパーティションにハッシュパーティションのような均一配分を

ハッシュパーティションってリストパーティションみたいにパーティション狙い撃ちできて、かつ、ハッシュパーティションみたいに、データをパーティション間で均一化できないのかなぁ
というずいぶん昔の話を思い出して、そう言えば書いてないかもしれない。いつの話だよってぐらい昔の話だけどw
どうやったかというと、
ハッシュキーにふさわしい値をもつ列を決める(一意キーとか主キー列が理想、カーディナリティの低い、分布に偏りのあるデータを持つ列は使わない)

で、ハッシュキーが決まったら、話は早くて、ORA_HASH()関数で取得できるハッシュ値を利用したリストパーティションを作成するだけ。
ゴニョゴニュ言わなくても、SQLとPL/SQLのコードを見ていただければ、理解していただけるかと。

ORCL@SCOTT> l
1 CREATE TABLE list_p_tab
2 (
3 id_code VARCHAR2(10) NOT NULL
4 , foo VARCHAR2(30)
5 , id_code_hash_value NUMBER(2) NOT NULL
6 )
7 PARTITION BY LIST (id_code_hash_value)
8 (
9 PARTITION list_p_tab_p1 VALUES(0)
10 ,PARTITION list_p_tab_p2 VALUES(1)
11 ,PARTITION list_p_tab_p3 VALUES(2)
12 ,PARTITION list_p_tab_p4 VALUES(3)
13* )
ORCL@SCOTT> /

Table created.

ORCL@SCOTT> ALTER TABLE list_p_tab ADD CONSTRAINT gpk_list_p_tab PRIMARY KEY(id_code) USING INDEX GLOBAL;

Table altered.

ORCL@SCOTT> l
1 DECLARE
2 TYPE id_code_t IS TABLE OF list_p_tab.id_code%TYPE INDEX BY PLS_INTEGER;
3 TYPE foo_t IS TABLE OF list_p_tab.foo%TYPE INDEX BY PLS_INTEGER;
4 id_codes id_code_t;
5 foos foo_t;
6 k PLS_INTEGER := 1;
7 BEGIN
8 FOR i IN 1..400000 LOOP
9 id_codes(k) := TO_CHAR(i,'fm0000000009');
10 foos(k) := i;
11 k := k + 1;
12 IF k > 1000 THEN
13 FORALL j in 1..k-1
14 INSERT INTO list_p_tab VALUES(id_codes(j), foos(j), ORA_HASH(id_codes(j),3));
15 COMMIT;
16 k := 1;
17 END IF;
18 END LOOP;
19* END;
ORCL@SCOTT> /

PL/SQL procedure successfully completed.

Elapsed: 00:00:12.28

ORCL@SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'LIST_P_TAB',granularity=>'ALL',cascade=>true,no_invalidate=>false,degree=>4);

PL/SQL procedure successfully completed.

Elapsed: 00:00:02.51

ORCL@SCOTT> r
1 select
2 table_name
3 ,partition_name
4 ,num_rows
5 from
6 user_tab_partitions
7 where
8 table_name = 'LIST_P_TAB'
9 order by
10* 1,2

TABLE_NAME PARTITION_NAME NUM_ROWS
------------------------------ ------------------------------ ----------
LIST_P_TAB LIST_P_TAB_P1 99901
LIST_P_TAB LIST_P_TAB_P2 100194
LIST_P_TAB LIST_P_TAB_P3 100056
LIST_P_TAB LIST_P_TAB_P4 99849

ORCL@SCOTT> select id_code_hash_value,count(1) from list_p_tab group by id_code_hash_value order by 1;

ID_CODE_HASH_VALUE COUNT(1)
------------------ ----------
0 99901
1 100194
2 100056
3 99849

Elapsed: 00:00:00.06

ORCL@SCOTT> explain plan for
2 select
3 *
4 from
5 list_p_tab
6 where
7 id_code_hash_value = 1;

Explained.

Elapsed: 00:00:00.10
ORCL@SCOTT> @?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 2143708561

----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100K| 2054K| 275 (1)| 00:00:01 | | |
| 1 | PARTITION LIST SINGLE| | 100K| 2054K| 275 (1)| 00:00:01 | 2 | 2 |
| 2 | TABLE ACCESS FULL | LIST_P_TAB | 100K| 2054K| 275 (1)| 00:00:01 | 2 | 2 |
----------------------------------------------------------------------------------------------------

グローバル索引を作成してあるので、パーティション関係ない検索は主キー索引経由でも可。

ORCL@SCOTT> explain plan for select * from list_p_tab where id_code = '00004000000';

Explained.

Elapsed: 00:00:00.02
ORCL@SCOTT> @?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------
Plan hash value: 4132161764

---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 21 | 3 (0)| 00:00:01 | | |
| 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| LIST_P_TAB | 1 | 21 | 3 (0)| 00:00:01 | ROWID | ROWID |
|* 2 | INDEX UNIQUE SCAN | GPK_LIST_P_TAB | 1 | | 2 (0)| 00:00:01 | | |
---------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ID_CODE"='00004000000')

| | | コメント (0)

2019年2月12日 (火)

RDS Oracle 雑多なメモ#17/ FAQ

Oracle DB インスタンスの一般的な DBA タスクに記載されているAmazon RDS OracleとオンプレのOracleの操作方法の違は意外に多く、長年かけて体に染み付いていて、脊髄反応でタイプしてしまうとエラー、あ”〜っなんてこともしばしばw

仕方ないので、慣れるしかないわけですが、脊髄反応でオンプレのコマンドをタイプして、あ”〜っ! となったことのある個人的な Top5 を備忘録として書いておきます:)
脊髄反応でそんな権限ないよーというショックなエラーうけとる回数を少しでも減らせるようAmazon RDSパッケージのタイプ練習中の日々w (いずれ、うまく切り替えられるようになれるだろうと信じてw

Oracle DB インスタンスの一般的な DBA システムタスク
Oracle DB インスタンスの一般的な DBA データベースタスク
Oracle DB インスタンスの一般的な DBA ログタスク
Oracle DB インスタンスの一般的な DBA のその他のタスク

個人的に、つい、オンプレと同じ操作をして、エラーになってしまった Top5 w

1位. sysオブジェクトへ権限付与で grant文をタイプしてしまう。
2位. つい、alter system kill session をタイプしてしまう。
3位. オンラインログファイルを切り替えたり、追加、削除で、alter database add logfile..をタイプしたり、alter system switch logfileをタイプしてしまう。
4位. ディレクトリオブジェクトを作成しようとして、create directory...をタイプしてしまう。
5位. rmanの検証コマンドを使おうとして、生のrmanは使えなかった、と気づくw

私がつい、脊髄反応でオリジナルのコマンドをタイプして、エラー? なぬ? あ、RDSではAmazon RDS向けのパッケージ使うんだった!!と 気づく典型的な操作の数々(^^;;;;; 長年しみついた手癖で脊髄反応しちゃうのでどうしようもないのですw
みなさんはどのコマンドで、あ”! となることが多いのでしょうか?(おそらく Top.1は、私と同じ、grant関連ではないでしょうか?w 一番使う機会が多いですからね)

話は少々脱線しますが、上記操作を行うAmazon RDSパッケージでデータベースサイズが大きくなると処理時間を要するものもそこそこあります。
Amazon RDSパッケージで提供されていても内部ではOracleの対応する機能を実行しているわけで、処理時間を要するタイプの操作を行った場合、処理の進行状況を確認確認したくなることもあります。そんな時は、v$session_longops を参照するとよいのではないかと思います。全ての機能が詳細な情報をv$session_longopsに載せてくれるわけではないですが。。(オンプレのOracle Databaseを利用していたという方でも、v$session_longops を使ったことはないなんてことも少なくないような気がします)


RDS Oracleでも v$session_longops ビューは効果的に利用できる例として、datapumpやOracle DB インスタンスの一般的な DBA データベースタスクでも解説されているrmanの検証コマンドの実行時など、操作にそれなりの時間を要するタイプのものです。(datapumpについては、詳細なステータスを記録していないようなので、datapumpのlogを覗くほうが状況確認としては便利ではありますが、一応、datapumpもv$session_longopsには記録されます。この点はOracle由来なのでオンプレでも同じです。)

一例として、それなりに時間を要する処理の代表格、Amazon RDS プロシージャ rdsadmin.rdsadmin_rman_util.validate_database(Oracle DB インスタンスの一般的な DBA データベースタスク参照)を利用した関連ファイル検証の進行状況をv$session_longopsを利用してモニターリング:)

今回利用したRDS Oracleのデータファイルはぞれぞれ以下のようなサイズです。

SQL> r
1 select
2 tablespace_name
3 ,file_name
4 ,sum(bytes)/1024/1024 "MB"
5 from
6 dba_data_files
7 group by
8 tablespace_name
9 ,file_name
10 union all
11 select
12 tablespace_name
13 ,file_name
14 ,sum(bytes)/1024/1024 "MB"
15 from
16 dba_temp_files
17 group by
18 tablespace_name
19* ,file_name

TABLESPACE_NAME FILE_NAME MB
------------------------------ ------------------------------------------------------------ ----------
SYSAUX /rdsdbdata/db/ORCL_A/datafile/o1_mf_sysaux_fxpjf1nv_.dbf 498.9375
USERS /rdsdbdata/db/ORCL_A/datafile/o1_mf_users_fxpjf3d2_.dbf 100
UNDO_T1 /rdsdbdata/db/ORCL_A/datafile/o1_mf_undo_t1_fxpjf2lx_.dbf 290
RDSADMIN /rdsdbdata/db/ORCL_A/datafile/o1_mf_rdsadmin_fxpkkz9k_.dbf 7
SYSTEM /rdsdbdata/db/ORCL_A/datafile/o1_mf_system_fxpjdxws_.dbf 500
TEMP /rdsdbdata/db/ORCL_A/datafile/o1_mf_temp_fxpjf34b_.tmp 100
----------
sum 1495.9375


事前に作成しておいたスクリプトでAmazon RDS プロシージャ rdsadmin.rdsadmin_rman_util.validate_databaseの処理状況をモニタリングしています(スクリプトの例は後半参照のこと)

SQL> @show_validate_status

レコードが選択されませんでした。

SQL> /

NOW SID SERIAL# OPNAME CONTEXT SOFAR TOTALWORK % done
-------------------------------- ---------- ---------- ------------------------------ ---------- ---------- ---------- ----------
19-02-11 09:42:18.217701 +00:00 665 51783 RMAN: full datafile backup 1 123322 178680 69.02

SQL> /

NOW SID SERIAL# OPNAME CONTEXT SOFAR TOTALWORK % done
-------------------------------- ---------- ---------- ------------------------------ ---------- ---------- ---------- ----------
19-02-11 09:42:19.298741 +00:00 665 51783 RMAN: full datafile backup 1 139452 178680 78.05

SQL> /

NOW SID SERIAL# OPNAME CONTEXT SOFAR TOTALWORK % done
-------------------------------- ---------- ---------- ------------------------------ ---------- ---------- ---------- ----------
19-02-11 09:42:20.367723 +00:00 665 51783 RMAN: full datafile backup 1 156028 178680 87.32

SQL> /

NOW SID SERIAL# OPNAME CONTEXT SOFAR TOTALWORK % done
-------------------------------- ---------- ---------- ------------------------------ ---------- ---------- ---------- ----------
19-02-11 09:42:21.289310 +00:00 665 51783 RMAN: full datafile backup 1 170428 178680 95.38

SQL> /

レコードが選択されませんでした。

SQL>
SQL> !cat show_validate_status.sql
col "NOW" for a32
col opname for a30
SELECT
systimestamp AS "NOW"
,sid
,serial#
,opname
,context
,sofar
,totalwork
,round(sofar / totalwork * 100, 2) "% done"
FROM
v$session_longops
WHERE
opname LIKE 'RMAN%'
AND opname NOT LIKE '%aggregate%'
AND sofar != totalwork
AND totalwork != 0;


Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ
RDS Oracle 雑多なメモ#13 / FAQ
RDS Oracle 雑多なメモ#14 - おまけ / FAQ
RDS Oracle 雑多なメモ#15 - おまけのおまけ / FAQ
RDS Oracle 雑多なメモ#16 - 再び:) / FAQ

| | | コメント (0) | トラックバック (0)

2018年11月 4日 (日)

RDS Oracle 雑多なメモ#16 - 再び:) / FAQ

再び忘れがちなので、備忘録。

RDS Oracleでマスターユーザー以外で、SQL*PLusの Auto trace そして、DBMS_XPLAN.DISPLAY や DBMS_XPLAN.DISPLAY_CURSOR を使おうとすると以下のようなエラーに遭遇! 
なにも準備してないと。(explain plan for文だけは準備していなくても可能なのでが)


TEST> set autot trace exp stat
SP2-0618: Cannot find the Session Identifier. Check PLUSTRACE role is enabled
SP2-0611: Error enabling STATISTICS report

とか

...略...
TEST> select * from table(dbms_xplan.display_cursor(format=>'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
User has no SELECT privilege on V$SESSION

なんてことに、

RDS Oracle、マスターユーザーでは可能なのですが、PLUSTRACEロールも作成されていない、かつ、 plustrce.sql がない.

AWSUSER> select role from dba_roles where role = 'PLUSTRACE';

no rows selected


ということで、いちいち調べるのも面倒なFAQとなっているので、備忘録として書いておきました。

マスターユーザー以外のユーザーに alter session システム権限を付与しておきます。
該当システム権限が付与されていないと、セッションレベルで statistics_level パラメータを変更できません。
このパラメータは、dbms_xplan.display_cursor でactual planを取得する際に必要になるのですが、最悪付与されていない場合には、SQL文に以下のヒントを追加することで代替可能ではありますが、いちいちSQL文に以下のヒントを追加しなければならないので面倒。
とはいえ、alter sessionは付与したくないということもなくはなく、そんな時は以下のヒントで頑張っください。

ex.

SELECT
/*+ gather_plan_statistics */
*
FROM
hoge
WHERE
id = 1;


次に必要なのは、 v$sessionなどを含むいくつかのパフォーマンスビューへのSELECTオブジェクト権限、グローバルな一時表として定義されているplan_tableへの全オブジェクト権限です。
管理面を考えてロールを作成し関連権限をロールに付与、作成したロールを対象ユーザーに付与するようにすると便利です。


alter sessionsシステム権限を含む最低限必要なオブジェクト権限とそれを付与するロール作成スクリプトの例は以下の通り。

foobar$ cat create_dev_role.sql

-- create developer role
create role dev_role;

-- for show parameter
grant alter session to dev_role;
-- for dbms_xplan.display_cursor and auto trace
exec rdsadmin.rdsadmin_util.grant_sys_object('V_$SESSION', 'DEV_ROLE', 'SELECT');

-- for dbms_xplan.display_cursor
exec rdsadmin.rdsadmin_util.grant_sys_object('V_$SQL_PLAN_STATISTICS_ALL', 'DEV_ROLE', 'SELECT');
exec rdsadmin.rdsadmin_util.grant_sys_object('V_$PARAMETER', 'DEV_ROLE', 'SELECT');
exec rdsadmin.rdsadmin_util.grant_sys_object('V_$SQL', 'DEV_ROLE', 'SELECT');
exec rdsadmin.rdsadmin_util.grant_sys_object('V_$SQL_PLAN', 'DEV_ROLE', 'SELECT');

-- for auto trace
exec rdsadmin.rdsadmin_util.grant_sys_object('V_$STATNAME', 'DEV_ROLE', 'SELECT');
exec rdsadmin.rdsadmin_util.grant_sys_object('V_$MYSTAT', 'DEV_ROLE', 'SELECT');
exec rdsadmin.rdsadmin_util.grant_sys_object('V_$SESSTAT', 'DEV_ROLE', 'SELECT');

-- for auto trace (plan_table - temporary table)
exec rdsadmin.rdsadmin_util.grant_sys_object('PLAN_TABLE$', 'DEV_ROLE', 'ALL');

参考
Oracle DB インスタンスの一般的な DBA タスク


ということで、動作確認を兼ねたサンプルは以下のとおり。

まずは、RDS Oracleのマスターユーザーで.
マスターユーザー以外のユーザーの作成と権限とロールの付与(チューニングが必要とされる開発者等)を想定

foobar$ sqlplus awsuser/xxxxxxx@xxxx.xxxxxxxx.rds.amazonaws.com:1521/HOGE

SQL*Plus: Release 12.1.0.2.0 Production on Fri Nov 2 22:05:14 2018

Copyright (c) 1982, 2016, Oracle. All rights reserved.

Last Successful login time: Fri Nov 02 2018 22:01:24 +09:00

...中略...

AWSUSER> l
1 create user test identified by xxxxxx
2 default tablespace users
3 temporary tablespace temp
4* quota unlimited on users
AWSUSER> /

User created.

AWSUSER> grant connect, resource to test;

Grant succeeded.

AWSUSER> @create_dev_role

Role created.

Grant succeeded.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

AWSUSER> grant dev_role to test;

Grant succeeded.

AWSUSER> exit


作成したユーザーで接続して、各方法で実行計画を取得できるか確認!
クエリーを実行するため適当な表を作成してデータを登録しておく。

foobar$ sqlplus test/xxxxxxx@xxxx.xxxxxxxx.rds.amazonaws.com:1521/HOGE

SQL*Plus: Release 12.1.0.2.0 Production on Fri Nov 2 22:15:23 2018

Copyright (c) 1982, 2016, Oracle. All rights reserved.

...中略...
TEST>
TEST> l
1 create table hoge (
2 id number not null primary key
3 ,foobar varchar2(20)
4* ) nologging
TEST> /

Table created.

TEST>
TEST> l
1 begin
2 for i in 1..10000 loop
3 insert into
4 hoge (
5 id
6 ,foobar
7 )
8 values (
9 i
10 ,to_char(i)
11 );
12 end loop;
13 commit;
14* end;
TEST> /

PL/SQL procedure successfully completed.


SQL*Plusのauto traceが行えるか確認!

TEST> set autot trace exp stat
TEST> select * from hoge where id = 1;

Elapsed: 00:00:00.07

Execution Plan
----------------------------------------------------------
Plan hash value: 2757398040

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 25 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 25 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | SYS_C005687 | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ID"=1)

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
342 bytes sent via SQL*Net to client
488 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

参考
文のトレースについて


explain plan for文は準備なしで問題ないのですが、念のための確認! DBMS_XPLAN.DISPLAYプロシージャを利用して実行計画を取得

TEST> 
TEST> explain plan for
2 select * from hoge where id = 1;

explained.

TEST> @show_explain

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
Plan hash value: 2757398040

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 25 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 25 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | SYS_C005687 | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ID"=1)

14 rows selected.

Elapsed: 00:00:00.05

スクリプト例は以下の通り。$ORACLE_HOME/rdbms/admin/utlxpls.sql や、utlxplp.sqlが利用できれば楽なんでが、それら中身は、DBMS_XPLAN.DISPLAYなので大差ない内容。

$ cat show_explain.sql

set linesize 200
set long 100000
set longchunk 200
set tab off

SELECT
*
FROM
TABLE(
DBMS_XPLAN.DISPLAY(
format => 'ALL -PROJECTION -ALIAS'
)
)
;


Actual planをDBMS_XPLAN.DISPLAY_CURSORプロシージャで取得〜

TEST> 
TEST> @show_realplan

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
statistics_level string TYPICAL

Session altered.

*** SQL that you want to get an actual plan ***
1* select * from hoge where id = 1
***********************************************

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID 6f67zkz43kr76, child number 1
-------------------------------------
select * from hoge where id = 1

Plan hash value: 2757398040

-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 3 |
| 1 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 1 | 1 |00:00:00.01 | 3 |
|* 2 | INDEX UNIQUE SCAN | SYS_C005687 | 1 | 1 | 1 |00:00:00.01 | 2 |
-----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ID"=1)

19 rows selected.

Elapsed: 00:00:00.11

Session altered.


NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
statistics_level string TYPICAL

Actual planを取得するスクリプト例
SQL*Plusのコマンドを駆使してはいますが、ポイントはset statistics_level = allにすることと、DBMS_XPLAN.DISPLAY_CURSORをコールする際のformatパラメータです
set termout off/onでSQL文の結果を表示しないようにしています。この設定はSQLファイルからSQL文を実行した場合にだけ有効です。(ちょっとしたTips :)

$ cat show_realplan.sql

show parameter statistics_level
alter session set statistics_level = all;

set linesize 200
set long 100000
set longchunk 200
set tab off

PROMPT *** SQL that you want to get an actual plan ***

select * from hoge where id = 1
.
l

PROMPT ***********************************************

set termout off
r
set termout on


-- get the actual plan
set timi on
SELECT
*
FROM
TABLE(
DBMS_XPLAN.DISPLAY_CURSOR(
format => 'ALLSTATS LAST'
)
)
;
set timi off
alter session set statistics_level = typical;
show parameter statistics_level

参考
DBMS_XPLANサブプログラムの要約


Apple Store Ginzaで、息子が ”iPadでムービーを作ろう” に参加するので、その合間に、パタパタブログを書き、その足で英会話に向かう日曜日の午後w



Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ
RDS Oracle 雑多なメモ#13 / FAQ
RDS Oracle 雑多なメモ#14 - おまけ / FAQ
RDS Oracle 雑多なメモ#15 - おまけのおまけ / FAQ

| | | コメント (0) | トラックバック (0)

2018年9月28日 (金)

RDS Oracle 雑多なメモ#15 - おまけのおまけ / FAQ

やっぱり、改造してしまった。おまけのおまけ編w

前回単純にMD5を取得だけのスクリプトをやっつけで作ったので、それを少し改造して、締めくくり。?(たぶん。。)

前回作成したMD5取得スクリプトを元に、ファイルが同じかどうか比較するスクリプトに作り変えました :)

21:35:35 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.dmp.bak file 1083412480 18-09-24
hoge.dmp.gz.bak file 2603349 18-09-24
hoge.log.bak file 1202 18-09-24
hoge.log.gz.bak file 512 18-09-24
01/ directory 4096 18-09-24
hoge.dmp file 1083412480 18-09-24
hoge.log file 904 18-09-24

9行が選択されました。

ダンプファイル(一旦圧縮したファイルを解凍したファイル)とオリジナルのダンプファイルのコピーの比較

21:35:43 rdsora121@BILL> @diff_md5 test_dir hoge.dmp hoge.dmp.bak
Src : 1B84C9E09F13A4FDC6EF2F03137C0338
Dest: 1B84C9E09F13A4FDC6EF2F03137C0338
-- No difference found. --

PL/SQLプロシージャが正常に完了しました。


ダンプファイル(オリジナルを圧縮したファイル)とオリジナルダンプファイルを圧縮したファイルのコピーの比較

21:36:28 rdsora121@BILL> @diff_md5 test_dir hoge.dmp.gz hoge.dmp.gz.bak
Src : D2CB26A7E75C9725F00C30A9280C1599
Dest: D2CB26A7E75C9725F00C30A9280C1599
-- No difference found. --

PL/SQLプロシージャが正常に完了しました。

異なるファイルの比較、ログファイルのコピーと、圧縮したログファイルのコピー。

21:37:35 rdsora121@BILL> @diff_md5 test_dir hoge.log.bak hoge.log.gz.bak
Src : DCE17181C95EACE997F45A2537BC1DA8
Dest: 6398E040E157B4A0CEF50FCF45C76FF7
-- Difference found. --

PL/SQLプロシージャが正常に完了しました。

圧縮したログファイルとそのコピーの比較。同じでなにより:)

21:38:15 rdsora121@BILL> @diff_md5 test_dir hoge.log.gz hoge.log.gz.bak
Src : 6398E040E157B4A0CEF50FCF45C76FF7
Dest: 6398E040E157B4A0CEF50FCF45C76FF7
-- No difference found. --

PL/SQLプロシージャが正常に完了しました。


異なるファイルの比較、どちらもログファイルですが、内容がことなります。

21:38:51 rdsora121@BILL> @cat_file test_dir hoge.log

TEXT
------------------------------------------------------------------------------------------
マスター表"BILL"."SYS_IMPORT_TABLE_01"は正常にロード/アンロードされました
"BILL"."SYS_IMPORT_TABLE_01"を起動しています:
オブジェクト型TABLE_EXPORT/TABLE/TABLEの処理中です
オブジェクト型TABLE_EXPORT/TABLE/TABLE_DATAの処理中です
. . "SCOTT"."HOGE" 1.008 GB 270000行がインポートされました
オブジェクト型TABLE_EXPORT/TABLE/INDEX/INDEXの処理中です
オブジェクト型TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINTの処理中です
オブジェクト型TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/MARKERの処理中です
ジョブ"BILL"."SYS_IMPORT_TABLE_01"が月 9月 24 04:05:17 2018 elapsed 0 00:00:52で正常に完了しました

11行が選択されました。

21:39:38 rdsora121@BILL> @cat_file test_dir hoge.log.bak

TEXT
------------------------------------------------------------------------------------------
"BILL"."EXPTABLE_HOGE"を起動しています:
BLOCKSメソッドを使用して見積り中です...
オブジェクト型TABLE_EXPORT/TABLE/TABLE_DATAの処理中です
BLOCKSメソッドを使用した見積り合計: 2.125 GB
オブジェクト型TABLE_EXPORT/TABLE/TABLEの処理中です
オブジェクト型TABLE_EXPORT/TABLE/INDEX/INDEXの処理中です
オブジェクト型TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINTの処理中です
オブジェクト型TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/MARKERの処理中です
. . "SCOTT"."HOGE" 1.008 GB 270000行がエクスポートされました
マスター表"BILL"."EXPTABLE_HOGE"は正常にロード/アンロードされました
******************************************************************************
BILL.EXPTABLE_HOGEに設定されたダンプ・ファイルは次のとおりです:
/rdsdbdata/userdirs/01/hoge.dmp
ジョブ"BILL"."EXPTABLE_HOGE"が日 9月 23 05:36:33 2018 elapsed 0 00:00:35で正常に完了しました

16行が選択されました。

21:39:48 rdsora121@BILL> @diff_md5 test_dir hoge.log hoge.log.bak
Src : 6DA2D7C6150A9C8ACD60D95D6A61C1B5
Dest: DCE17181C95EACE997F45A2537BC1DA8
-- Difference found. --

PL/SQLプロシージャが正常に完了しました。

スクリプトは以下の通り。MD5の比較には、UTL_RAW.COMPARE関数を利用しています。

21:40:05 rdsora121@BILL> !cat diff_md5.sql
SET VERIFY OFF
SET SERVEROUTPUT ON FORMAT WRAPPED
DECLARE
vDirectoryName VARCHAR2(30);
vSrcFileName VARCHAR2(60);
vDestFileName VARCHAR2(60);
vSrcMd5 RAW(16);
vDestMd5 RAW(16);

PROCEDURE close_bfile
(
iBfile IN OUT NOCOPY BFILE
)
IS
BEGIN
IF DBMS_LOB.ISOPEN(iBfile) = 1 THEN
DBMS_LOB.CLOSE(iBfile);
END IF;
END close_bfile;

FUNCTION md5
(
iDirectoryName IN VARCHAR2
, iFileName IN VARCHAR2
) RETURN RAW
IS
vBfile BFILE;
vBlob BLOB;
vMd5 RAW(16);
BEGIN
vBFile := BFILENAME(UPPER(iDirectoryName), iFileName);
DBMS_LOB.OPEN(vBFile);

DBMS_LOB.CREATETEMPORARY(
lob_loc => vBlob
, cache => false
, dur => DBMS_LOB.SESSION
);

DBMS_LOB.LOADFROMFILE(
dest_lob => vBlob
, src_lob => vBFile
, amount => DBMS_LOB.GETLENGTH(vBFile)
, dest_offset => 1
, src_offset => 1
);

vMd5 := DBMS_CRYPTO.HASH(vBlob, DBMS_CRYPTO.HASH_MD5);

DBMS_LOB.FREETEMPORARY(vBlob);
close_bfile(vBFile);
RETURN vMd5;
EXCEPTION
WHEN OTHERS THEN
DBMS_LOB.FREETEMPORARY(vBlob);
close_bfile(vBFile);
RAISE;
END md5;

BEGIN
vDirectoryName := UPPER('&1');
vSrcFileName := '&2';
vDestFileName := '&3';

vSrcMd5 := md5(vDirectoryName, vSrcFileName);
vDestMd5 := md5(vDirectoryName, vDestFileName);

DBMS_OUTPUT.PUT_LINE('Src : '||vSrcMd5);
DBMS_OUTPUT.PUT_LINE('Dest: '||vDestMd5);

IF UTL_RAW.COMPARE(r1 => vSrcMd5, r2 => vDestMd5) = 0
THEN
DBMS_OUTPUT.PUT_LINE('-- No difference found. --');
ELSE
DBMS_OUTPUT.PUT_LINE('-- Difference found. --');
END IF;

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM()||':'||SQLCODE());
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
UNDEFINE 3
SET SERVEROUTPUT OFF
SET VERIFY ON


たぶん、これで終わりなはず。(なにか浮かばなければw)


Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ
RDS Oracle 雑多なメモ#13 / FAQ
RDS Oracle 雑多なメモ#14 - おまけ / FAQ

| | | コメント (0) | トラックバック (0)

2018年9月27日 (木)

RDS Oracle 雑多なメモ#14 - おまけ / FAQ

ということで、done ってしておきながら、おまけ(得意技w)です。

RDS Oracle限定というわけではなくなってきましたがw 勢いでさらに追加。


手作りのcpっぽいスクリプトなので、バグってないか少し不安w ということで、オリジナルファイルと同じなのか確認できるようにメッセージダイジェストを取得するスクリプトも作成しましたw

21:14:43 rdsora121@BILL> @list_dir

DIRECTORY_NAME DIRECTORY_PATH
---------------------------------------- ----------------------------------------------------------------------
ADUMP /rdsdbdata/log/audit
BDUMP /rdsdbdata/log/trace
DATA_PUMP_DIR /rdsdbdata/datapump
HOGE_DIR /rdsdbdata/userdirs/02
OPATCH_INST_DIR /rdsdbbin/oracle/OPatch
OPATCH_LOG_DIR /rdsdbbin/oracle/QOpatch
OPATCH_SCRIPT_DIR /rdsdbbin/oracle/QOpatch
TEST_DIR /rdsdbdata/userdirs/01

8行が選択されました。

21:14:48 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.dmp.bak file 1083412480 18-09-24
hoge.dmp.gz.bak file 2603349 18-09-24
hoge.log.bak file 1202 18-09-24
hoge.log.gz.bak file 512 18-09-24
01/ directory 4096 18-09-24
hoge.dmp file 1083412480 18-09-24
hoge.log file 904 18-09-24

9行が選択されました。

ダンプファイルを圧縮ファイル、ログファイル、ダンプファイルなどなど、MD5が取得できるようになりました。ファイルが壊れてないか確認しやすい:)

21:14:55 rdsora121@BILL> @check_md5 test_dir hoge.dmp.gz
TEST_DIR/hoge.dmp.gz MD5 : D2CB26A7E75C9725F00C30A9280C1599

PL/SQLプロシージャが正常に完了しました。

21:15:32 rdsora121@BILL> @check_md5 test_dir hoge.dmp.gz.bak
TEST_DIR/hoge.dmp.gz.bak MD5 : D2CB26A7E75C9725F00C30A9280C1599

PL/SQLプロシージャが正常に完了しました。

21:15:50 rdsora121@BILL> @check_md5 test_dir hoge.log
TEST_DIR/hoge.log MD5 : 6DA2D7C6150A9C8ACD60D95D6A61C1B5

PL/SQLプロシージャが正常に完了しました。

21:16:26 rdsora121@BILL> @check_md5 test_dir hoge.dmp
TEST_DIR/hoge.dmp MD5 : 1B84C9E09F13A4FDC6EF2F03137C0338

PL/SQLプロシージャが正常に完了しました。

21:18:02 rdsora121@BILL> @check_md5 test_dir hoge.dmp.bak
TEST_DIR/hoge.dmp.bak MD5 : 1B84C9E09F13A4FDC6EF2F03137C0338

PL/SQLプロシージャが正常に完了しました。


ざっくり作ったスクリプトは以下の通り。BFILEからファイルを読み込み、一時BLOBとしてBFILEからロード、MD5取得というながれです。
(オンプレでもそのまま使える内容にはなっちゃいましたが、勢いで作ったので:)


21:18:50 rdsora121@BILL> !cat check_md5.sql
SET VERIFY OFF
SET SERVEROUTPUT ON FORMAT WRAPPED
DECLARE
vSrcfile BFILE;
vSrcBlob BLOB;
vSrcDirectoryName VARCHAR2(30);
vSrcFileName VARCHAR2(60);
vSrcFileSize PLS_INTEGER;
vSrcFileMd5 RAW(16);

PROCEDURE close_bfile
(
iBfile IN OUT NOCOPY BFILE
)
IS
BEGIN
IF DBMS_LOB.ISOPEN(iBfile) = 1 THEN
DBMS_LOB.CLOSE(iBfile);
END IF;
END close_bfile;

BEGIN
vSrcDirectoryName := UPPER('&1');
vSrcFileName := '&2';

vSrcFile
:= BFILENAME(UPPER(vSrcDirectoryName), vSrcFileName);

DBMS_LOB.OPEN(vSrcFile);
vSrcFileSize := DBMS_LOB.GETLENGTH(vSrcFile);


DBMS_LOB.CREATETEMPORARY (
lob_loc => vSrcBlob
, cache => false
, dur => DBMS_LOB.SESSION
);

DBMS_LOB.LOADFROMFILE (
dest_lob => vSrcBlob
, src_lob => vSrcFile
, amount => vSrcFileSize
, dest_offset => 1
, src_offset => 1
);

vSrcFileMd5 := DBMS_CRYPTO.HASH(vSrcBlob, DBMS_CRYPTO.HASH_MD5);

DBMS_LOB.FREETEMPORARY(vSrcBlob);
close_bfile(vSrcFile);

DBMS_OUTPUT.PUT_LINE(
vSrcDirectoryName
||'/'||vSrcFileName
||' MD5 : '||vSrcFileMd5
);
EXCEPTION
WHEN OTHERS THEN
DBMS_LOB.FREETEMPORARY(vSrcBlob);
close_bfile(vSrcFile);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
SET SERVEROUTPUT OFF
SET VERIFY ON

ということで、done... これを元にまたまにか作るかもしれない、作らないかもしれないw


Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ
RDS Oracle 雑多なメモ#13 / FAQ

| | | コメント (0) | トラックバック (0)

2018年9月26日 (水)

RDS Oracle 雑多なメモ#13 / FAQ

メモの続きです。
前回は、圧縮したテキストファイルとダンプファイル(バイナリファイル)を解凍するスクリプトを実行したところまででした。

やっとここまできたw

今回は、解凍したファイルが使えるかなど内容確認をしてみたいと思います。


まず、解凍したログファイル(テキストファイル)の内容を確認
問題なさそうですね。

12:35:01 rdsora121@BILL> @cat_file test_dir hoge.log

TEXT
----------------------------------------------------------------------------------------
"BILL"."EXPTABLE_HOGE"を起動しています:
BLOCKSメソッドを使用して見積り中です...
オブジェクト型TABLE_EXPORT/TABLE/TABLE_DATAの処理中です
BLOCKSメソッドを使用した見積り合計: 2.125 GB
オブジェクト型TABLE_EXPORT/TABLE/TABLEの処理中です
オブジェクト型TABLE_EXPORT/TABLE/INDEX/INDEXの処理中です
オブジェクト型TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINTの処理中です
オブジェクト型TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/MARKERの処理中です
. . "SCOTT"."HOGE" 1.008 GB 270000行がエクスポートされました
マスター表"BILL"."EXPTABLE_HOGE"は正常にロード/アンロードされました
******************************************************************************
BILL.EXPTABLE_HOGEに設定されたダンプ・ファイルは次のとおりです:
/rdsdbdata/userdirs/01/hoge.dmp
ジョブ"BILL"."EXPTABLE_HOGE"が日 9月 23 05:36:33 2018 elapsed 0 00:00:35で正常に完了しました

16行が選択されました。

解凍したダンプファイルをインポートして確認する前に、元のオブジェクトの確認後、削除しておきます。

12:37:17 rdsora121@BILL> set linesize 80
12:37:20 rdsora121@BILL> desc scott.hoge
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
FOO VARCHAR2(4000)

12:37:25 rdsora121@BILL> select count(1) from scott.hoge;

COUNT(1)
----------
270000

12:39:07 rdsora121@BILL> r
1 select
2 segment_name
3 ,sum(bytes)/1024/1024/1024 "GB"
4 from
5 dba_segments
6 where
7 owner='SCOTT'
8 and segment_name in ('HOGE','PK_HOGE')
9 group by
10* segment_name

SEGMENT_NAME GB
------------------------------ ----------
PK_HOGE .004882813
HOGE 2.125

12:39:17 rdsora121@BILL> set linesize 400
12:39:21 rdsora121@BILL>
12:40:02 rdsora121@BILL> drop table scott.hoge purge;

表が削除されました。

dbms_datapumpパッケージを利用したスクリプトを作成してインポート可能か確認 :)

13:03:40 rdsora121@BILL> @import_table test_dir hoge scott hoge
マスター表"BILL"."SYS_IMPORT_TABLE_01"は正常にロード/アンロードされました
"BILL"."SYS_IMPORT_TABLE_01"を起動しています:
オブジェクト型TABLE_EXPORT/TABLE/TABLEの処理中です
オブジェクト型TABLE_EXPORT/TABLE/TABLE_DATAの処理中です
. . "SCOTT"."HOGE" 1.008 GB 270000行がインポートされました
オブジェクト型TABLE_EXPORT/TABLE/INDEX/INDEXの処理中です
オブジェクト型TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINTの処理中です
オブジェクト型TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/MARKERの処理中です
ジョブ"BILL"."SYS_IMPORT_TABLE_01"が月 9月 24 04:05:17 2018 elapsed 0 00:00:52で正常に完了しました

PL/SQLプロシージャが正常に完了しました。

インポート成功!!! 表などの確認!

13:05:54 rdsora121@BILL> set linesize 80
13:06:00 rdsora121@BILL> desc scott.hoge
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
FOO VARCHAR2(4000)

13:06:04 rdsora121@BILL> select count(1) from scott.hoge;

COUNT(1)
----------
270000

13:07:27 rdsora121@BILL> r
1 select
2 segment_name
3 ,sum(bytes)/1024/1024/1024 "GB"
4 from
5 dba_segments
6 where
7 owner='SCOTT'
8 and segment_name in ('HOGE','PK_HOGE')
9 group by
10* segment_name

SEGMENT_NAME GB
------------------------------ ----------
PK_HOGE .004882813
HOGE 2.125


スクリプトは以下の通り。 表モードのインポートをハードコードしていますが、そのあたりパラメータ化すればそこそこ使いやすくなりますかね。。

13:08:03 rdsora121@BILL> !cat import_table.sql
SET VERIFY OFF
SET SERVEROUTPUT ON
DECLARE
v4Debug VARCHAR2(50);
cDirectory CONSTANT VARCHAR2(30) := UPPER('&1');
cDumpFileName CONSTANT VARCHAR2(64) := '&2'||'.dmp';
cLogFileName CONSTANT VARCHAR2(64) := '&2'||'.log';
cSchemaName CONSTANT VARCHAR2(30) := UPPER('&3');
cTableName CONSTANT VARCHAR2(30) := UPPER('&4');
i NUMBER;
vDataPumpJobHandle NUMBER;
vProgress_ratio NUMBER;
vJobState VARCHAR2(30);
oLogEntry ku$_LogEntry;
oStatus ku$_Status;
BEGIN
DBMS_OUTPUT.ENABLE;

v4Debug := 'OPEN';
vDataPumpJobHandle
:= DBMS_DATAPUMP.OPEN (
operation => 'IMPORT'
,job_mode => 'TABLE'
,remote_link => NULL
);

v4Debug := 'ADD_FILE - dumpfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cDumpFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE
);

v4Debug := 'ADD_FILE - logfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cLogFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE
);

v4Debug := 'METADATA_FILTER - schema name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'SCHEMA_LIST'
,value => '''' || cSchemaName || ''''
);

v4Debug := 'METADATA_FILTER - table name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'NAME_LIST'
,value => '''' || cTableName || ''''
);

v4Debug := 'START_JOB';
DBMS_DATAPUMP.START_JOB (
handle => vDataPumpJobHandle
);

v4Debug := 'JOB_STATE';
vProgress_ratio := 0;
vJobState := 'UNDEFINED';
WHILE (vJobState != 'COMPLETED') AND (vJobState != 'STOPPED') LOOP
DBMS_DATAPUMP.GET_STATUS (
vDataPumpJobHandle
,DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR
+ DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS
+ DBMS_DATAPUMP.KU$_STATUS_WIP
,-1
,vJobState
,oStatus
);

IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_WIP) != 0)
THEN
oLogEntry := oStatus.wip;
ELSE
IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR) != 0)
THEN
oLogEntry := oStatus.error;
ELSE
oLogEntry := NULL;
END IF;
END IF;
IF oLogEntry IS NOT NULL
THEN
i := oLogEntry.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(oLogEntry(i).LogText);
i := oLogEntry.NEXT(i);
END LOOP;
END IF;
END LOOP;

DBMS_DATAPUMP.DETACH(vDataPumpJobHandle);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm());
DBMS_OUTPUT.PUT_LINE(v4Debug);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
UNDEFINE 3
UNDEFINE 4
SET SERVEROUTPUT OFF
SET VERIFY ON


ということで、ひとまず、RDS Oracleの雑多なメモ done :)



Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ
RDS Oracle 雑多なメモ#12 / FAQ

| | | コメント (0) | トラックバック (0)

2018年9月25日 (火)

RDS Oracle 雑多なメモ#12 / FAQ

メモの続きです。

さて、前回、cpっぽいスクリプトの問題を改善したので今回は、圧縮したファイルを解凍するスクリプトを作ろうと思います。

前回の操作でファイルが増えて見づらくなったので、解凍に必要な最小限のファイルだけに整理します

12:20:19 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.log file 1202 18-09-23
hoge.dmp file 1083412480 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.dmp.bak file 1083412480 18-09-24
hoge.dmp.gz.bak file 2603349 18-09-24
hoge.log.bak file 1202 18-09-24
hoge.log.gz.bak file 512 18-09-24
01/ directory 4096 18-09-24

9行が選択されました。

12:20:25 rdsora121@BILL> @rm_file test_dir hoge.log
TEST_DIR/hoge.log removed.

PL/SQLプロシージャが正常に完了しました。

12:21:49 rdsora121@BILL> @rm_file test_dir hoge.dmp
TEST_DIR/hoge.dmp removed.

PL/SQLプロシージャが正常に完了しました。

12:21:57 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.dmp.bak file 1083412480 18-09-24
hoge.dmp.gz.bak file 2603349 18-09-24
hoge.log.bak file 1202 18-09-24
hoge.log.gz.bak file 512 18-09-24
01/ directory 4096 18-09-24

7行が選択されました。

では圧縮したテキストファイルとダンプファイル(バイナリファイル)を解凍してみます。

12:22:06 rdsora121@BILL> @gunzip_file test_dir hoge.log.gz
TEST_DIR/hoge.log.gz uncompressed to hoge.log (1202 bytes)

PL/SQLプロシージャが正常に完了しました。

12:22:44 rdsora121@BILL> @gunzip_file test_dir hoge.dmp.gz
TEST_DIR/hoge.dmp.gz uncompressed to hoge.dmp (1083412480 bytes)

PL/SQLプロシージャが正常に完了しました。

12:23:04 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.dmp.bak file 1083412480 18-09-24
hoge.dmp.gz.bak file 2603349 18-09-24
hoge.log.bak file 1202 18-09-24
hoge.log.gz.bak file 512 18-09-24
hoge.log file 1202 18-09-24
01/ directory 4096 18-09-24
hoge.dmp file 1083412480 18-09-24

9行が選択されました。


できた!!!


今回の利用した解凍スクリプトも元は12年前のエントリで利用したコードのほぼ再利用です。
Mac De Oracle (PL/SQL De UNCOMPRESS)

12:24:22 rdsora121@BILL> !cat gunzip_file.sql
SET VERIFY OFF
SET SERVEROUTPUT ON
DECLARE
vSrcGzippedFile BFILE;
vUncompressedBlob BLOB;
vDirectoryName VARCHAR2(30);
vSrcGzippedFileName VARCHAR2(60);
vDestFileName VARCHAR2(60);
VUncompressedSize PLS_INTEGER;

PROCEDURE close_bfile
(
iBfile IN OUT NOCOPY BFILE
)
IS
BEGIN
IF DBMS_LOB.ISOPEN(iBfile) = 1 THEN
DBMS_LOB.CLOSE(iBfile);
END IF;
END close_bfile;

PROCEDURE write_blob_to_file
(
iDirectoryName IN VARCHAR2,
iFileName IN VARCHAR2,
iSrcBlob IN OUT NOCOPY BLOB
)
IS
vFile UTL_FILE.FILE_TYPE;
BEGIN
vFile := UTL_FILE.FOPEN(
UPPER(iDirectoryName),
iFileName,
'wb',
32767
);

DECLARE
cChunkSize CONSTANT PLS_INTEGER := 32767;

vBuffer RAW(32767);
vAmount PLS_INTEGER := cChunkSize;
vNumOfChunk PLS_INTEGER;
BEGIN
vNumOfChunk := CEIL(DBMS_LOB.GETLENGTH(iSrcBlob)/cChunkSize);
FOR chunk# IN 1..vNumOfChunk LOOP
DBMS_LOB.READ(
iSrcBlob,
vAmount,
(cChunkSize * (chunk# - 1)) + 1, /*offet*/
vBuffer
);
UTL_FILE.PUT_RAW(vFile, vBuffer, TRUE);
END LOOP;
END;

UTL_FILE.FCLOSE(vFile);
END write_blob_to_file;

BEGIN
vDirectoryName := UPPER('&1');
vSrcGzippedFileName := '&2';
vDestFileName := SUBSTR('&2', 1, INSTR('&2', '.gz') - 1);

vSrcGzippedFile
:= BFILENAME(UPPER(vDirectoryName), vSrcGzippedFileName);

DBMS_LOB.OPEN(vSrcGzippedFile);
vUncompressedBlob := UTL_COMPRESS.LZ_UNCOMPRESS(vSrcGzippedFile);
vUncompressedSize := DBMS_LOB.GETLENGTH(vUncompressedBlob);

write_blob_to_file(
UPPER(vDirectoryName),
vDestFileName,
vUncompressedBlob
);

DBMS_LOB.FREETEMPORARY(vUncompressedBlob);
close_bfile(vSrcGzippedFile);
DBMS_OUTPUT.PUT_LINE(
vDirectoryName||'/'
||vSrcGzippedFileName
||' uncompressed to '
||vDestFileName
||' ('||vUncompressedSize||' bytes)'
);
EXCEPTION
WHEN OTHERS THEN
DBMS_LOB.FREETEMPORARY(vUncompressedBlob);
close_bfile(vSrcGzippedFile);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
SET SERVEROUTPUT OFF
SET VERIFY ON

ということで、次回、解凍したテキストファイルの内容確認と、解凍したダンプファイルからインポートして内容確認へつづく :)



Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ
RDS Oracle 雑多なメモ#11 / FAQ

| | | コメント (0) | トラックバック (0)

2018年9月24日 (月)

RDS Oracle 雑多なメモ#11 / FAQ

メモの続きです。

はじめに、
ごねんなさい、ごねんなさい

RDS Oracle 雑多なメモ#6 / FAQ
で作成したUTL_FILE.FCOPYを利用したcpっぽいスクリプトはtextファイルにしか対応できません。
理由は、UTL_FILE.FCOPYプロシージャ自体がtextファイル向けでバイナリファイルに対応していないことが理由ではあるのですが、textファイルしかコピーできないのもなんなので、バイナリファイルにも対応したバージョンに変更したいと思います。

RDS Oracle 雑多なメモ#6 / FAQで作成したスクリプトは今回作成したスクリプトで置き換えていただければ、m(_ _)m


前々回作成したダンプファイル(バイナリファイル)やテキストファイルを使ってみようと思います。

utl_file.fcopyプロシージャでコピーできる例(テキストファイル)

10:48:50 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
01/ directory 4096 18-09-24

11:14:42 rdsora121@BILL> @cp_file test_dir hoge.log test_dir hoge.log.bak1

source file : test_dir/hoge.log
dest file : test_dir/hoge.log.bak1 copied.

PL/SQLプロシージャが正常に完了しました。

11:15:10 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
01/ directory 4096 18-09-24
hoge.log.bak1 file 1202 18-09-24

6行が選択されました。

utl_file.fcopyプロシージャでコピーできない例(バイナリファイル)
エラーが発生し、コピー途中のゴミファイルが作成されていまいます。。。。これはこまる。。

11:15:10 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
01/ directory 4096 18-09-24
hoge.log.bak1 file 1202 18-09-24

11:16:51 rdsora121@BILL> @cp_file test_dir hoge.dmp test_dir hoge.dmp.bak1
-29284:ORA-29284: ファイル読取りエラーが発生しました。

PL/SQLプロシージャが正常に完了しました。

11:17:20 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.log.bak1 file 1202 18-09-24
hoge.dmp.bak1 file 45093 18-09-24
01/ directory 4096 18-09-24

7行が選択されました。


RDS Oracle 雑多なメモ#6 / FAQで作成したcp_file.sqlのコードは以下の通り
これでバイナリーファイルもコピーできると嬉しいのにね〜。昔からできないのでしかたない。。

11:25:20 rdsora121@BILL> !cat cp_file.sql
set verify off
set serveroutput on format wrapped
BEGIN
UTL_FILE.FCOPY(
src_location => UPPER('&1')
,src_filename => '&2'
,dest_location => UPPER('&3')
,dest_filename => '&4'
);
DBMS_OUTPUT.PUT_LINE(' ');
DBMS_OUTPUT.PUT_LINE('source file : &1/&2');
DBMS_OUTPUT.PUT_LINE('dest file : &3/&4 copied.');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE()||':'||SQLERRM());
END;
/

undefine 1
undefine 2
undefine 3
undefine 4
set verify on
set serveroutput off


他の手はないかな??? あ、dbms_file_transfer.copy_fileプロシージャを使ったらどうか?

ただし、バイナリファイルをうまくコピーできるようになりましたが、このプロシージャにはいくつか制限があります。
Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1 (12.1) DBMS_FILE_TRANSFER
コピーされるファイルのサイズは512バイトの倍数である必要があります。
コピーされるファイルのサイズは、2TB以下である必要があります。
コピーの進行状況を監視するには、V$SESSION_LONGOPS動的パフォーマンス・ビューを参照します。

コピーできない例
512バイトの倍数でないバイナリファイルをコピーしようとすると。。。

11:17:20 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.log.bak1 file 1202 18-09-24
hoge.dmp.bak1 file 45093 18-09-24
01/ directory 4096 18-09-24

7行が選択されました。

11:18:02 rdsora121@BILL> @cp_transferfile test_dir hoge.dmp.gz test_dir hoge.dmp.gz.bak1
-19505:ORA-19505: ファイル"/rdsdbdata/userdirs/01/hoge.dmp.gz"の識別に失敗しました。
ORA-27046: ファイル・サイズが論理ブロック・サイズの倍数ではありません。
Additional information: 1

PL/SQLプロシージャが正常に完了しました。

うまくコピーできる例
512バイトの倍数サイズのバイナリファイルのコピー

11:23:12 rdsora121@BILL> @cp_transferfile test_dir hoge.dmp test_dir hoge.dmp.bak2

source file : test_dir/hoge.dmp
dest file : test_dir/hoge.dmp.bak2 copied.

PL/SQLプロシージャが正常に完了しました。

11:24:01 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.log.bak1 file 1202 18-09-24
hoge.dmp.bak1 file 45093 18-09-24
01/ directory 4096 18-09-24
hoge.dmp.bak2 file 1083412480 18-09-24

8行が選択されました。

dbms_transfer_fileパッケージを利用したコードは以下の通り
これも制限が多くなければコードも短くてうれしいわけなんですが。。。しかたなし

11:24:10 rdsora121@BILL> !cat cp_transferfile.sql
set verify off
set serveroutput on format wrapped
BEGIN
DBMS_FILE_TRANSFER.COPY_FILE(
source_directory_object => UPPER('&1')
,source_file_name => '&2'
,destination_directory_object => UPPER('&3')
,destination_file_name => '&4'
);
DBMS_OUTPUT.PUT_LINE(' ');
DBMS_OUTPUT.PUT_LINE('source file : &1/&2');
DBMS_OUTPUT.PUT_LINE('dest file : &3/&4 copied.');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE()||':'||SQLERRM());
END;
/

undefine 1
undefine 2
undefine 3
undefine 4
set verify on
set serveroutput off


制限もあるし、使いやすいような使いにくいような...
いろいろ消化不良状態。。。なので

スクリプト長くなってめんどくさいけど、この部分楽はできない模様なので、手作り決定!

最終的に、既存プロシージャにないのなら、作っちゃえ。ということで、cp_file.sqlを全面作り変えw ました。

11:32:13 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
01/ directory 4096 18-09-24

11:32:21 rdsora121@BILL> @cp_file test_dir hoge.dmp test_dir hoge.dmp.bak
TEST_DIR/hoge.dmp (1083412480) to TEST_DIR/hoge.dmp.bak (1083412480)

PL/SQLプロシージャが正常に完了しました。

11:33:07 rdsora121@BILL> @cp_file test_dir hoge.log test_dir hoge.log.bak
TEST_DIR/hoge.log (1202) to TEST_DIR/hoge.log.bak (1202)

PL/SQLプロシージャが正常に完了しました。

11:33:26 rdsora121@BILL> @cp_file test_dir hoge.dmp.gz test_dir hoge.dmp.gz.bak
TEST_DIR/hoge.dmp.gz (2603349) to TEST_DIR/hoge.dmp.gz.bak (2603349)

PL/SQLプロシージャが正常に完了しました。

11:33:42 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.log file 1202 18-09-23
hoge.dmp file 1083412480 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
hoge.dmp.bak file 1083412480 18-09-24
hoge.log.bak file 1202 18-09-24
01/ directory 4096 18-09-24
hoge.dmp.gz.bak file 2603349 18-09-24

8行が選択されました。

新、cp_file.sqlのスクリプトは以下の通り。基本的に、gzip_file.sql内部で利用していたバイナリファイルのハンドリング部分を抜き出した感じです。一部違うのは DBMS_LOB.CREATETEMPORARYで一時BLOBを読んでいるところ。BFILEからBLOBをロードして、出力先ファイルへ書き出しているかしょは圧縮で利用しているコードと同じです。最後に一時BLOBの解放をお忘れなく

11:34:21 rdsora121@BILL> !cat cp_file.sql
SET VERIFY OFF
SET SERVEROUTPUT ON FORMAT WRAPPED
DECLARE
vSrcfile BFILE;
vSrcBlob BLOB;
vSrcDirectoryName VARCHAR2(30);
vDestDirectoryName VARCHAR2(30);
vSrcFileName VARCHAR2(60);
vDestFileName VARCHAR2(60);
vSrcFileSize PLS_INTEGER;
vDestFileSize PLS_INTEGER;

PROCEDURE close_bfile
(
iBfile IN OUT NOCOPY BFILE
)
IS
BEGIN
IF DBMS_LOB.ISOPEN(iBfile) = 1 THEN
DBMS_LOB.CLOSE(iBfile);
END IF;
END close_bfile;

PROCEDURE write_blob_to_file
(
iDirectoryName IN VARCHAR2
, iFileName IN VARCHAR2
, iSrcBlob IN OUT NOCOPY BLOB
)
IS
vFile UTL_FILE.FILE_TYPE;
BEGIN
vFile := UTL_FILE.FOPEN(
UPPER(iDirectoryName)
, iFileName
, 'wb'
, 32767
);

DECLARE
cChunkSize CONSTANT PLS_INTEGER := 32767;

vBuffer RAW(32767);
vAmount PLS_INTEGER := cChunkSize;
vNumOfChunk PLS_INTEGER;
BEGIN
vNumOfChunk := CEIL(DBMS_LOB.GETLENGTH(iSrcBlob)/cChunkSize);
FOR chunk# IN 1..vNumOfChunk LOOP
DBMS_LOB.READ(
iSrcBlob
, vAmount
, (cChunkSize * (chunk# - 1)) + 1 /*offset*/
, vBuffer
);
UTL_FILE.PUT_RAW(vFile, vBuffer, TRUE);
END LOOP;
END;
UTL_FILE.FCLOSE(vFile);
END write_blob_to_file;

BEGIN
vSrcDirectoryName := UPPER('&1');
vSrcFileName := '&2';
vDestDirectoryName := UPPER('&3');
vDestFileName := '&4';

vSrcFile
:= BFILENAME(UPPER(vSrcDirectoryName), vSrcFileName);

DBMS_LOB.OPEN(vSrcFile);
vSrcFileSize := DBMS_LOB.GETLENGTH(vSrcFile);


DBMS_LOB.CREATETEMPORARY (
lob_loc => vSrcBlob
, cache => false
, dur => DBMS_LOB.SESSION
);

DBMS_LOB.LOADFROMFILE (
dest_lob => vSrcBlob
, src_lob => vSrcFile
, amount => vSrcFileSize
, dest_offset => 1
, src_offset => 1
);
vDestFileSize := DBMS_LOB.GETLENGTH(vSrcBlob);

write_blob_to_file(
UPPER(vDestDirectoryName)
, vDestFileName
, vSrcBlob
);

DBMS_LOB.FREETEMPORARY(vSrcBlob);
close_bfile(vSrcFile);

DBMS_OUTPUT.PUT_LINE(
vSrcDirectoryName||'/'||vSrcFileName||' ('||vDestFileSize||') to '
||vDestDirectoryName||'/'||vDestFileName||' ('||vSrcFileSize||')'
);
EXCEPTION
WHEN OTHERS THEN
DBMS_LOB.FREETEMPORARY(vSrcBlob);
close_bfile(vSrcFile);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
UNDEFINE 3
UNDEFINE 4
SET SERVEROUTPUT OFF
SET VERIFY ON

ということで、次回へづつく。:)


Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ
RDS Oracle 雑多なメモ#10 / FAQ

| | | コメント (0) | トラックバック (0)

RDS Oracle 雑多なメモ#10 / FAQ

メモの続きです。
前回は、dbms_datapumpパッケージを利用してエクスポートを行うスクリプトを作成しました。
今回は、作成したダンプファイル(バイナリファイルでサイズは1GB越え)をutl_compressパッケージを利用して圧縮するスクリプトを作成します。

このネタ実は、12年前の古いネタが元になっています。
Mac De Oracle (PL/SQL De COMPRESS)
Mac De Oracle (PL/SQL De UNCOMPRESS)

ということで、RDS Oracleのディレクトリオブジェクトにエクポートしたダンプファイル(バイナリファイル)圧縮してみましょう。。動くのか?。。。

ディレクトリを確認

10:48:42 rdsora121@BILL> @list_dir

DIRECTORY_NAME DIRECTORY_PATH
---------------------------------------- ----------------------------------------------------------------------
ADUMP /rdsdbdata/log/audit
BDUMP /rdsdbdata/log/trace
DATA_PUMP_DIR /rdsdbdata/datapump
HOGE_DIR /rdsdbdata/userdirs/02
OPATCH_INST_DIR /rdsdbbin/oracle/OPatch
OPATCH_LOG_DIR /rdsdbbin/oracle/QOpatch
OPATCH_SCRIPT_DIR /rdsdbbin/oracle/QOpatch
TEST_DIR /rdsdbdata/userdirs/01

8行が選択されました。

エクスポートしたダンプファイル(バイナリファイル)とログファイル(テキストファイル)が対象ディレクトリ以下にあることを確認

10:42:05 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
01/ directory 4096 18-09-24

まず、ダンプファイル(バイナリファイル)を圧縮します。ダンプファイルは圧縮効率がいいんですよね。なので転送する際には圧縮するのがオススメ
圧縮はうまくいった?ようですね。解凍スクリプト編で正常に解凍できるか確認する予定なのでしばしお待ちを。

10:46:00 rdsora121@BILL> @gzip_file test_dir hoge.dmp
TEST_DIR/hoge.dmp compressed to hoge.dmp.gz(99.76%)

PL/SQLプロシージャが正常に完了しました。

圧縮したファイルは、.gzの拡張子をつけて出力するようにしています。

10:46:23 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
01/ directory 4096 18-09-24

次に、テキストファイルも圧縮!

10:46:32 rdsora121@BILL> @gzip_file test_dir hoge.log
TEST_DIR/hoge.log compressed to hoge.log.gz(57.4%)

PL/SQLプロシージャが正常に完了しました。

10:46:51 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
hoge.dmp file 1083412480 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp.gz file 2603349 18-09-24
hoge.log.gz file 512 18-09-24
001/ directory 4096 18-09-24

スクリプトは以下の通り、utl_compressパッケージを利用しています。バイナリファイルはBFILEから読み込み、圧縮、圧縮結果のバイナリは一時BLOBで返されるので、一時BLOBをバイナリファイルとして書き出しています。最後に一時BLOBを解放してあげてゴミ掃除という流れです。

10:46:58 rdsora121@BILL> !cat gzip_file.sql
SET VERIFY OFF
SET SERVEROUTPUT ON FORMAT WRAPPED
DECLARE
vSrcfile BFILE;
vCompressedBlob BLOB;
vDirectoryName VARCHAR2(30);
vSrcFileName VARCHAR2(60);
vDestFileName VARCHAR2(60);
vSrcFileSize PLS_INTEGER;
vDestFileSize PLS_INTEGER;

PROCEDURE close_bfile
(
iBfile IN OUT NOCOPY BFILE
)
IS
BEGIN
IF DBMS_LOB.ISOPEN(iBfile) = 1 THEN
DBMS_LOB.CLOSE(iBfile);
END IF;
END close_bfile;

PROCEDURE write_blob_to_file
(
iDirectoryName IN VARCHAR2,
iFileName IN VARCHAR2,
iSrcBlob IN OUT NOCOPY BLOB
)
IS
vFile UTL_FILE.FILE_TYPE;
BEGIN
vFile := UTL_FILE.FOPEN(
UPPER(iDirectoryName),
iFileName,
'wb',
32767
);

DECLARE
cChunkSize CONSTANT PLS_INTEGER := 32767;

vBuffer RAW(32767);
vAmount PLS_INTEGER := cChunkSize;
vNumOfChunk PLS_INTEGER;
BEGIN
vNumOfChunk := CEIL(DBMS_LOB.GETLENGTH(iSrcBlob)/cChunkSize);
FOR chunk# IN 1..vNumOfChunk LOOP
DBMS_LOB.READ(
iSrcBlob,
vAmount,
(cChunkSize * (chunk# - 1)) + 1, /*offset*/
vBuffer
);
UTL_FILE.PUT_RAW(vFile, vBuffer, TRUE);
END LOOP;
END;

UTL_FILE.FCLOSE(vFile);
END write_blob_to_file;
--
BEGIN
vDirectoryName := UPPER('&1');
vSrcFileName := '&2';
vDestFileName := '&2'||'.gz';

vSrcFile
:= BFILENAME(UPPER(vDirectoryName), vSrcFileName);

DBMS_LOB.OPEN(vSrcFile);
vSrcFileSize := DBMS_LOB.GETLENGTH(vSrcFile);
vCompressedBlob := UTL_COMPRESS.LZ_COMPRESS(vSrcFile);
vDestFileSize := DBMS_LOB.GETLENGTH(vCompressedBlob);

write_blob_to_file(
UPPER(vDirectoryName),
vDestFileName,
vCompressedBlob
);

DBMS_LOB.FREETEMPORARY(vCompressedBlob);
close_bfile(vSrcFile);

DBMS_OUTPUT.PUT_LINE(
vDirectoryName
||'/'||vSrcFileName||' compressed to '
||vDestFileName||'('
||ROUND((1 - (vDestFileSize / vSrcFileSize)) * 100, 2)||'%)'
);
XCEPTION
WHEN OTHERS THEN
DBMS_LOB.FREETEMPORARY(vCompressedBlob);
close_bfile(vSrcFile);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
SET SERVEROUTPUT OFF
SET VERIFY ON

できた〜

ということで、次回へづつく。:)



Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ
RDS Oracle 雑多なメモ#9 / FAQ

| | | コメント (0) | トラックバック (0)

RDS Oracle 雑多なメモ#9 / FAQ

メモの続きです。
ということで、 (どういうことだ〜っ。久々に一人、ツッコミ)

圧縮解凍ネタに進む前に、dbms_datapumpパッケージを使って、1GB越えのファイルを作っておこうかと。ここで作成したダンプファイルを圧縮解凍ネタにしようと思います。
事前にscottユーザーを作成して、hoge表を作成し、1GB程度のセグメントサイズになるようにデータを登録、その後、主キー索引を追加してあります。

14:29:48 rdsora121@BILL> desc scott.hoge
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
FOO VARCHAR2(4000)

14:31:24 rdsora121@BILL>select segment_name,sum(bytes)/1024/1024/1024 "GB" from dba_segments where owner='SCOTT' group by segment_name;

SEGMENT_NAME GB
------------------------------ ----------
PK_HOGE .004882813
HOGE 2.125

経過: 00:00:00.09
14:28:59 rdsora121@BILL> @list_dir

DIRECTORY_NAME DIRECTORY_PATH
---------------------------------------- ----------------------------------------------------------------------
ADUMP /rdsdbdata/log/audit
BDUMP /rdsdbdata/log/trace
DATA_PUMP_DIR /rdsdbdata/datapump
HOGE_DIR /rdsdbdata/userdirs/02
OPATCH_INST_DIR /rdsdbbin/oracle/OPatch
OPATCH_LOG_DIR /rdsdbbin/oracle/QOpatch
OPATCH_SCRIPT_DIR /rdsdbbin/oracle/QOpatch
TEST_DIR /rdsdbdata/userdirs/01

8行が選択されました。

経過: 00:00:00.03
14:29:03 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
01/ directory 4096 18-09-22

scottユーザーのhoge表を表モードでエクスポートしてダンプファイルをtest_dirへ出力させます!
以前やっつけで作ったのスクリプトの再利用なので、エクスポートモードなど他のパラメータも変更できるようにすれば、より良く改良できると思います。いまはやりませんがw
(以前の記事も参考にしてみてくさい。SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ))
出力されたダンプファイルは約1GBってところですね。

14:35:41 rdsora121@BILL> @export_table test_dir hoge scott hoge
"BILL"."EXPTABLE_HOGE"を起動しています:
BLOCKSメソッドを使用して見積り中です...
オブジェクト型TABLE_EXPORT/TABLE/TABLE_DATAの処理中です
BLOCKSメソッドを使用した見積り合計: 2.125 GB
オブジェクト型TABLE_EXPORT/TABLE/TABLEの処理中です
オブジェクト型TABLE_EXPORT/TABLE/INDEX/INDEXの処理中です
オブジェクト型TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINTの処理中です
オブジェクト型TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICSの処理中です
オブジェクト型TABLE_EXPORT/TABLE/STATISTICS/MARKERの処理中です
. . "SCOTT"."HOGE" 1.008 GB 270000行がエクスポートされました
マスター表"BILL"."EXPTABLE_HOGE"は正常にロード/アンロードされました
******************************************************************************

BILL.EXPTABLE_HOGEに設定されたダンプ・ファイルは次のとおりです:
/rdsdbdata/userdirs/01/hoge.dmp
ジョブ"BILL"."EXPTABLE_HOGE"が日 9月 23 05:36:33 2018 elapsed 0 00:00:35で正常に完了しました

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:40.23
14:36:34 rdsora121@BILL>
14:37:04 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
01/ directory 4096 18-09-23
hoge.log file 1202 18-09-23
hoge.dmp file 1083412480 18-09-23

表モードエクスポートのDataPump Exportジョブを実行するスクリプトは以下のとおり。
(表モードに固定してある部分を変更してスキーマモードにしたり、複数の表のエクスポートに対応できるように改造してやれば、より便利なスクリプトにすることもできると思います。)

14:36:34 rdsora121@BILL> !cat export_table.sql
SET VERIFY OFF
SET SERVEROUTPUT ON
DECLARE
v4Debug VARCHAR2(50);
cDirectory CONSTANT VARCHAR2(30) := UPPER('&1');
cDumpFileName CONSTANT VARCHAR2(64) := '&2'||'.dmp';
cLogFileName CONSTANT VARCHAR2(64) := '&2'||'.log';
cSchemaName CONSTANT VARCHAR2(30) := UPPER('&3');
cTableName CONSTANT VARCHAR2(30) := UPPER('&4');
i NUMBER;
vDataPumpJobHandle NUMBER;
vProgress_ratio NUMBER;
vJobState VARCHAR2(30);
oLogEntry ku$_LogEntry;
oStatus ku$_Status;
BEGIN
DBMS_OUTPUT.ENABLE;

v4Debug := 'OPEN';
vDataPumpJobHandle
:= DBMS_DATAPUMP.OPEN (
operation => 'EXPORT'
,job_mode => 'TABLE'
,remote_link => NULL
,job_name => 'EXPTABLE_'||cTableName
,version => 'LATEST'
);

v4Debug := 'ADD_FILE - dumpfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cDumpFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE
);

v4Debug := 'ADD_FILE - logfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => cLogFileName
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE
);

v4Debug := 'METADATA_FILTER - schema name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'SCHEMA_LIST'
,value => '''' || cSchemaName || ''''
);

v4Debug := 'METADATA_FILTER - table name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'NAME_LIST'
,value => '''' || cTableName || ''''
);

v4Debug := 'START_JOB';
DBMS_DATAPUMP.START_JOB (
handle => vDataPumpJobHandle
);

v4Debug := 'JOB_STATE';
vProgress_ratio := 0;
vJobState := 'UNDEFINED';
WHILE (vJobState != 'COMPLETED') AND (vJobState != 'STOPPED') LOOP
DBMS_DATAPUMP.GET_STATUS (
vDataPumpJobHandle
,DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR
+ DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS
+ DBMS_DATAPUMP.KU$_STATUS_WIP
,-1
,vJobState
,oStatus
);

IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_WIP) != 0)
THEN
oLogEntry := oStatus.wip;
ELSE
IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR) != 0)
THEN
oLogEntry := oStatus.error;
ELSE
oLogEntry := NULL;
END IF;
END IF;
IF oLogEntry IS NOT NULL
THEN
i := oLogEntry.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(oLogEntry(i).LogText);
i := oLogEntry.NEXT(i);
END LOOP;
END IF;
END LOOP;

DBMS_DATAPUMP.DETACH(vDataPumpJobHandle);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm());
DBMS_OUTPUT.PUT_LINE(v4Debug);
RAISE;
END;
/

UNDEFINE 1
UNDEFINE 2
UNDEFINE 3
UNDEFINE 4
SET SERVEROUTPUT OFF
SET VERIFY ON


ということで、次回へづつく。:)


Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ
RDS Oracle 雑多なメモ#8 / FAQ

| | | コメント (0) | トラックバック (0)

RDS Oracle 雑多なメモ#8 / FAQ

メモの続きです。
前回は、utl_fileパッケージでrmっぽいことを行うスクリプトを作成しました。
前回まででutl_fileパッケージを利用したディレクトリオブジェクト配下のファイル操作スクリリプトを作ることができました。

さて、今回はなにをしましょう?。。。。としばし考えて浮かんだのが、昔の圧縮解凍ネタとdbms_datapumpネタの組み合わせ。。。

再利用ネタ、その1は以下。
SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ
上記ネタの何を利用するかというと、DBMS_DATAPUMPパッケージ。このパッケージもディレクトリオブジェクトを利用するのでネタとしてはちょうどよさげ。

もう一つのネタはかなり古い(12年前?w)
Mac De Oracle (PL/SQL De COMPRESS)
Mac De Oracle (PL/SQL De UNCOMPRESS)
この辺りを使って、圧縮、解凍スクリプトも作っておいたら便利なんじゃないかとおもいます。

それから、kempe さんの記事も利用すればいろいろできそうですね。 プライベートサブネットのVPCエンドポイント経由でS3とやりとりすればパブリックネットワークは経由しなくて良さそうですし:)
RDSとS3でファイルのやり取りを行う

なお、11.2では、aclパラメータに.xml拡張子が必要ですが、12.1以降では必要ありません(いつ変わったw)が、12c以降では以下プロシージャは非推奨で下位互換向けにのこされています
11.2のマニュアルには aclパラメータには、.xml 拡張子が必要とは明記されていないのですが、意味不明なエラーが返されます。例のaclには.xml拡張子が付加されているのですが、パラメータの解説では一切触れられていないのでハマることがあるので注意しましょうね。

15:54:54 orcl@SYS> select version from v$instance;

VERSION
-----------------
11.2.0.4.0

15:54:00 orcl@SYS> !cat test.sql
begin
dbms_network_acl_admin.create_acl (
acl => 'test'
, description => 'acl create test'
, principal => 'SCOTT'
, is_grant => true
, privilege => 'connect'
);
end;
/

15:54:07 orcl@SYS> @test
begin
*
ERROR at line 1:
ORA-46059: Invalid ACL identifier specified
ORA-06512: at "SYS.DBMS_NETWORK_ACL_ADMIN", line 70
ORA-06512: at "SYS.DBMS_NETWORK_ACL_ADMIN", line 218
ORA-06512: at line 2

15:54:19 orcl@SYS> !cat test.sql
begin
dbms_network_acl_admin.create_acl (
acl => 'test.xml'
, description => 'acl create test'
, principal => 'SCOTT'
, is_grant => true
, privilege => 'connect'
);
end;
/

15:54:25 orcl@SYS> @test

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.02
15:54:27 orcl@SYS> exec dbms_network_acl_admin.drop_acl('test.xml');

PL/SQL procedure successfully completed.

dbms_network_acl_adminについては11.2と12.1で考慮はいるかもしれません。(非推奨プロシージャはいずれ廃止されるでしょうから)

というあたりも考慮してネタを作るかどうか。。。
その前に、ファイル、圧縮解凍だけのネタを作っておくか。。

ということで、次回へづつく。:)



Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ
RDS Oracle 雑多なメモ#7 / FAQ

| | | コメント (0) | トラックバック (0)

2018年9月23日 (日)

RDS Oracle 雑多なメモ#7 / FAQ

メモの続きです。
前回は、utl_fileパッケージでcpっぽいことを行うスクリプトを作成しました。
今回は、ディレクトリオブジェクト以下のファイルにrmっぽい操作が行えるようなスクリプトを作ります :)

作成済みのtest_dirディレクトのファイルを再利用しています。

ディレクトリとファイルを指定してファイル削除!


17:11:54 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables_v1.txt file 2277 18-09-19
top100_tables_v2.txt file 2277 18-09-21
01/ directory 4096 18-09-21


17:12:37 rdsora121@BILL> @rm_file test_dir top100_tables_v2.txt
TEST_DIR/top100_tables_v2.txt removed.

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.03

17:13:25 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables_v1.txt file 2277 18-09-19
01/ directory 4096 18-09-22

スクリプトは以下の通り。UTL_FILE.FREMOVEを呼び出しているだけの単純なスクリプトにしてあります
第一回目にも書きましたが、スクリプトは、EC2など、SQL*Plusを起動しているクライアント側に作成します。

17:14:51 rdsora121@BILL> !cat rm_file.sql
set verify off
set serveroutput on
BEGIN
UTL_FILE.FREMOVE (
location => UPPER('&1')
, filename => '&2'
);
DBMS_OUTPUT.PUT_LINE(UPPER('&1')||'/&2 removed.');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE()||':'||SQLERRM());
END;
/

set serveroutput off
undefine 1
undefine 2
set verify on


次回へ続く


Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ
RDS Oracle 雑多なメモ#6 / FAQ




ところで、今年も秋葉原UDXで開催されたdb tech showcase 2018へ参加してきました。
今回、自分のセッションも司会もなしだったので、久々にゆっくりといろいろな方のセッションを聞くことがきたし、毎年このイベント合う多数のデータベースエンジニアとの交流もたのしくてたのしくてw それ以上表現のしようがないw
(ボキャ貧なのでごめんなさい、ごめんなさい)

そして、インサイトテクノロジーの社長を退かれた小幡さん、 ”おもしろった!” ありがとうございました。
おいしいみかんできたら、送ってね :)

| | | コメント (0) | トラックバック (0)

2018年9月22日 (土)

RDS Oracle 雑多なメモ#6 / FAQ

メモの続きです。
前回は、utl_fileパッケージでmvっぽいことを行うスクリプトを作成しました。
今回は、ディレクトリオブジェクト以下のファイルにcpっぽい操作が行えるようなスクリプトを作ります :)



ただ、 utl_file.fcopyってテキストファイルしかコピーできないんだよね。という制限付きです。いまのところ。
(バイナリ版は手作りで作ればできたような、。。。昔のネタ引っ張り出すかw)

ということで、utl_file.fcopyもdbms_transfer_fileパッケージも使わずにLOB系操作でバイナリファイルとテキストファイルをコピーするよう、cp_file.sqlスクリプトを作り変えました。

RDS Oracle 雑多なメモ#11 / FAQも合わせて参照ください。m(_ _)m




作成済みのtest_dirとhoge_dirディレクトリとファイルを再利用しています。
ざっくりめで書いたので複数ファイルの一括コピーや引数の指定方法などなど、改善したいところはあるわけですがw 適宜対応ということで。

同一ディレクトリでファイルコピー

00:44:59 SQL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables_v1.txt file 2277 18-09-19
01/ directory 4096 18-09-21

経過: 00:00:02.35
00:55:33 SQL> @cp_file test_dir top100_tables_v1.txt test_dir top100_tables_v2.txt

source file : test_dir/top100_tables_v1.txt
dest file : test_dir/top100_tables_v2.txt copied.

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.04
00:57:42 SQL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables_v1.txt file 2277 18-09-19
top100_tables_v2.txt file 2277 18-09-21
01/ directory 4096 18-09-21

異なるディレクトリへ同一名でファイルコピー

01:01:55 SQL> @cp_file test_dir top100_tables_v2.txt hoge_dir top100_tables_v2.txt

source file : test_dir/top100_tables_v2.txt
dest file : hoge_dir/top100_tables_v2.txt copied.

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.05
01:05:13 SQL> @ls_dir hoge_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables_v2.txt file 2277 18-09-21
02/ directory 4096 18-09-21

異なるディレクトリへ異なるファイル名でコピー

01:06:30 SQL> @cp_file test_dir top100_tables_v2.txt hoge_dir top100_tables_v3.txt

source file : test_dir/top100_tables_v2.txt
dest file : hoge_dir/top100_tables_v3.txt copied.

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.04
01:06:47 SQL> @ls_dir hoge_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables_v2.txt file 2277 18-09-21
02/ directory 4096 18-09-21
top100_tables_v3.txt file 2277 18-09-21

スクリプトは以下の通り。


01:09:56 SQL> !cat cp_file.sql
set verify off
set serveroutput on format wrapped
BEGIN
UTL_FILE.FCOPY(
src_location => UPPER('&1')
,src_filename => '&2'
,dest_location => UPPER('&3')
,dest_filename => '&4'
);
DBMS_OUTPUT.PUT_LINE(' ');
DBMS_OUTPUT.PUT_LINE('source file : &1/&2');
DBMS_OUTPUT.PUT_LINE('dest file : &3/&4 copied.');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE()||':'||SQLERRM());
END;
/

undefine 1
undefine 2
undefine 3
undefine 4
set verify on
set serveroutput off


次回へ続く



Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ
RDS Oracle 雑多なメモ#5 / FAQ

| | | コメント (0) | トラックバック (0)

2018年9月21日 (金)

RDS Oracle 雑多なメモ#5 / FAQ

メモの続きです。
前回は、AWS提供のrdsadmin.rds_file_util.read_text_file関数でcatっぽいスクリプトを作っておきました。(改善したほうがよさげなところもありますがw)
今回は、ディレクトリオブジェクト以下のファイルにmvっぽい操作が行えるようなスクリプトを作ります。

これからの操作はファイルそのものに対しておこなうので、 わかりやすくする意味もあり新たにディレクトオブジェクトを作成して、ファイルを一作っておきます。

実験用ディレクトリオブジェクトの作成

00:49:07 rdsora121@BILL> @create_dir test_dir

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:02.04

DIRECTORY_NAME DIRECTORY_PATH
---------------------------------------- ----------------------------------------------------------------------
TEST_DIR /rdsdbdata/userdirs/01

経過: 00:00:00.03


作成したディレクトリオブジェクト以下に適当なファイル(今回はテキストファイル)を作成

01:05:30 rdsora121@BILL> @query2file.sql

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.12

ここでは、”utl_file I/O” - この症状はあれの可能性が高いですね。でも紹介したUTL_FILEパッケージでディレクトオブジェクト以下にファイルを出力するサンプルを再利用してファイルを書き出しました。

01:06:02 rdsora121@BILL> !cat query2file.sql
DECLARE
cDIR_NAME CONSTANT VARCHAR2(30) := 'TEST_DIR';
cFILE_NAME CONSTANT VARCHAR2(128) := 'top100_tablenames__'||TO_CHAR(systimestamp, 'rrmmddhh24miss.ff')||'.txt';
cBufferSize CONSTANT BINARY_INTEGER := 32767;
cOpenMode CONSTANT VARCHAR2(2) := 'w';
fileHandle UTL_FILE.FILE_TYPE;
buffer VARCHAR2(32767);

cBulkReadLimit CONSTANT PLS_INTEGER := 324;
TYPE tBulkReadArray IS TABLE OF VARCHAR2(8192) INDEX BY BINARY_INTEGER;
bulkReadArray tBulkReadArray;
CURSOR cur_top100_tablenames IS
SELECT
table_name
FROM
dba_tables
ORDER BY
table_name
FETCH FIRST 100 ROWS ONLY
;
BEGIN
OPEN cur_top100_tablenames;
fileHandle := UTL_FILE.FOPEN(cDIR_NAME, cFILE_NAME, cOpenMode, cBufferSize);
LOOP
FETCH cur_top100_tablenames
BULK COLLECT INTO bulkReadArray
LIMIT cBulkReadLimit;

EXIT WHEN bulkReadArray.COUNT = 0;

buffer := NULL;
FOR i IN bulkReadArray.FIRST..bulkReadArray.LAST LOOP
buffer := buffer || bulkReadArray(i) || UTL_TCP.CRLF;
END LOOP;
UTL_FILE.PUT(fileHandle, buffer);
UTL_FILE.FFLUSH(fileHandle);
END LOOP;
UTL_FILE.FFLUSH(fileHandle);
UTL_FILE.FCLOSE(fileHandle);
CLOSE cur_top100_tablenames;
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(fileHandle) THEN
UTL_FILE.FCLOSE(fileHandle);
END IF;

IF cur_top100_tablenames%ISOPEN THEN
CLOSE cur_top100_tablenames;
END IF;
RAISE;
END;
/

テキストファイルがディレクトリオブジェクト以下に作成されましった!

01:06:29 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tablenames__180919160551.113374000.txt file 2277 18-09-19
01/ directory 4096 18-09-19

念のため中身を確認

01:06:38 rdsora121@BILL> @cat_file test_dir top100_tablenames__180919160551.113374000.txt

TEXT
------------------------------------------------------------------------------------------
ACCESS$
ACLMV$
ACLMV$_REFLOG
ACLMVREFSTAT$


・・・中略・・・

AQ$_SUBSCRIBER_TABLE
AQ$_SYS$SERVICE_METRICS_TAB_G
AQ$_SYS$SERVICE_METRICS_TAB_H
AQ$_SYS$SERVICE_METRICS_TAB_I

100行が選択されました。

同一ディレクトリ内でファイル名を変更

01:29:47 rdsora121@BILL> @mv_file test_dir top100_tablenames__180919160551.113374000.txt test_dir top100_tables.txt

source file : test_dir/top100_tablenames__180919160551.113374000.txt
dest file : test_dir/top100_tables.txt moved.

PL/SQLプロシージャが正常に完了しました。

長いファイル名称を短く変更!

01:29:57 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables.txt file 2277 18-09-19
01/ directory 4096 18-09-19

ファイル名は同じままで、別ディレクトリへファイル移動

08:05:17 rdsora121@BILL> @create_dir hoge_dir

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:02.26

DIRECTORY_NAME DIRECTORY_PATH
---------------------------------------- ----------------------------------------------------------------------
HOGE_DIR /rdsdbdata/userdirs/02

経過: 00:00:00.04
08:05:29 rdsora121@BILL> @mv_file test_dir top100_tables.txt hoge_dir top100_tables.txt

source file : test_dir/top100_tables.txt
dest file : hoge_dir/top100_tables.txt moved.

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.04
08:06:27 rdsora121@BILL> @ls_dir hoge_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables.txt file 2277 18-09-19
02/ directory 4096 18-09-19

経過: 00:00:02.25
08:06:39 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
01/ directory 4096 18-09-19

ファイル名を変更して、ディレクトリ移動

08:08:22 rdsora121@BILL> @mv_file hoge_dir top100_tables.txt test_dir top100_tables_v1.txt

source file : hoge_dir/top100_tables.txt
dest file : test_dir/top100_tables_v1.txt moved.

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.02
08:09:00 rdsora121@BILL> @ls_dir hoge_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
02/ directory 4096 18-09-19

経過: 00:00:02.05
08:09:11 rdsora121@BILL> @ls_dir test_dir

FILENAME TYPE FILESIZE MTIME
---------------------------------------------------------------------- ---------- ---------- --------
top100_tables_v1.txt file 2277 18-09-19
01/ directory 4096 18-09-19

経過: 00:00:02.05


スクリプトは以下の通り

01:30:20 rdsora121@BILL> !cat mv_file.sql
set verify off
set serveroutput on format wrapped
BEGIN
UTL_FILE.FRENAME(
src_location => UPPER('&1')
,src_filename => '&2'
,dest_location => UPPER('&3')
,dest_filename => '&4'
,overwrite => false
);
DBMS_OUTPUT.PUT_LINE(' ');
DBMS_OUTPUT.PUT_LINE('source file : &1/&2');
DBMS_OUTPUT.PUT_LINE('dest file : &3/&4 moved.');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE()||':'||SQLERRM());
END;
/

undefine 1
undefine 2
undefine 3
undefine 4
set verify on
set serveroutput off

次回へ続く



Previously on Mac De Oracle

RDS Oracle 雑多なメモ#1 / FAQ
RDS Oracle 雑多なメモ#2 / FAQ
RDS Oracle 雑多なメモ#3 / FAQ
RDS Oracle 雑多なメモ#4 / FAQ

| | | コメント (0) | トラックバック (0)

2018年5月 2日 (水)

Oracle Database Connect 2018 エキスパートはどう考えるか? 体感!パフォーマンスチューニング Ⅱ (番外編=没ネタ)

Oracle Database Connect 2018 エキスパートはどう考えるか?体感!パフォーマンスチューニング Ⅱ
~Autonomous Databaseの到来において必要となるチューニングとは~

これ、パフォーマンスチューニングネタをまとめ上げるまで、みなさんスケジュール調整し、
オンライン/オフラインミーティングを繰り返してネタを詰めていくという、かなーり面倒なことをやっています。
私なんて、ネタの候補検討だけで16時間ぐらい使ってますからねw(自分の余暇を使って、半分楽しみながら、締め切りがあるので半分苦しみながらw)



余談はこれくらいにして、今日の本題です。:)

Oracle Database Connect 2018 エキスパートはどう考えるか? 体感!パフォーマンスチューニング Ⅱ で没にしたネタがあるのですが、
そのまま捨てるのも勿体ないので、多少取得情報を追加した状態で公開しちゃおうと思います。


お時間のありますときに、頭の体操、AWRレポートの解析方法(最近はADDM、ASHレポートまで含まれています)のトレーニングにどうぞ。

遅延原因の想定と、想定原因の特定情報は、このエントリでは公開しません。おそらくGW開けの12日あたりか、その一週間後の19日ごろには原因を公開しようかと:)の後半に追記してあります:)

なお、本ページに公開した情報以外に、追加で見たい情報がある場合は、コメント欄やtwのmentionで具体的にリクエストいただければ、取得されている情報の範囲内で追加公開します。取得されてない情報はその旨を追記していきます。:)
まずは、エスパー力全開で、原因を想定してみてください。>
(かなり難易度高めだとは思いますが、過去この状況に遭遇した経験をお持ちのかたは、ここに公開した程度の情報から勘で言い当てたりしちゃうんですよね。実は、昨年末に、何年か振りでこの症状を目にしたのでネタにしよう思いました。)

以下取得された情報は以下のとおり。以下の情報を元に遅延原因を想定してみてください。



なーんでだ?!


状況説明
試験時には問題のなかった処理(30分以内に終了する状態が正常な状態です。)をリリースしました。
ところが、30分以上経過しても終了しません。遅延原因を想定、特定してください。
なお、本番環境のメンテナンス時間中に試験を行ったため、データベースインスタンス内で試験を行いリリースしています。
ちなみに、試験時の処理時間は、約21分でしたが、リリース時は約56分と大幅に遅延。


他のトランザクションが走行している状況をミックスするとより
本物っぽくなり、特定に苦労するので面白いのですが、
遅延している処理だけが走行している状況にしてあります。


資料1 試験時(正常時)AWRレポート
資料2 リリース時(遅延時)AWRレポート
資料3 試験時(正常時)とリリース時(遅延時)のAWR DIFFレポート

資料4 試験時(正常時)CPU利用率グラフ(調査による多少のノイズあり)

1

資料5 リリース時(遅延時)CPU利用率グラグ(スパイク部分調査による多少のノイズあり)

2

GWも後半スタート、天気は少々荒れそうですが、みなさん、よいGWを!





Twitterでのやり取り....

20180506_82405
20180506_82353




ここまでくるとなにかが見えてきたかな。。。原因を特定するまであと一歩な感じ。

追加資料1(遅延時)
top

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
13058 oracle 20 0 10.434g 365704 359548 R 100.0 1.4 1:05.70 oracle_13058_or
5973 oracle 20 0 1955632 253648 73812 S 4.7 1.0 1:42.62 gnome-shell
4657 oracle -2 0 10.417g 58792 55660 S 2.0 0.2 0:12.15 ora_vktm_orcl12
1285 root 20 0 483544 76000 21372 S 1.7 0.3 0:55.11 Xorg
4862 oracle 20 0 10.417g 68960 65868 S 1.3 0.3 0:01.23 ora_lg00_orcl12
223 root 20 0 0 0 0 S 0.7 0.0 0:00.49 kworker/u24:3
13059 oracle 20 0 157976 4564 3544 R 0.7 0.0 0:00.29 top
...略...

追加資料2(遅延時)
sar -P ALL

   ...略...
02:47:29 AM CPU %user %nice %system %iowait %steal %idle
02:47:34 AM all 4.78 0.00 2.36 0.07 0.00 92.80
02:47:34 AM 0 1.06 0.00 1.06 0.85 0.00 97.03
02:47:34 AM 1 0.20 0.00 0.40 0.00 0.00 99.40
02:47:34 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 3 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 4 0.00 0.00 0.20 0.00 0.00 99.80
02:47:34 AM 5 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 6 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 7 68.08 0.00 31.92 0.00 0.00 0.00
02:47:34 AM 8 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 9 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 10 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 11 0.00 0.00 0.20 0.20 0.00 99.60
...略...

追加資料3(遅延時)
perf top -C 7

Samples: 107K of event 'cpu-clock', Event count (approx.): 29550214790                                                              
Overhead Shared Object Symbol
4.36% libc-2.17.so [.] vfprintf
2.77% oracle [.] lxoCpStr
2.00% [kernel] [k] __radix_tree_lookup
1.94% oracle [.] dbgfcsIlcsGetNextDef
1.58% oracle [.] skgovprint
1.48% [kernel] [k] __do_softirq
1.44% [kernel] [k] selinux_file_permission
1.43% oracle [.] dbgaFmtAttrCb_int
1.36% [kernel] [k] system_call_after_swapgs
1.33% oracle [.] dbgaAttrFmtProcArg
1.26% oracle [.] dbgtfmWriteMetadata
1.13% oracle [.] dbgtfdFileWrite
1.02% libc-2.17.so [.] _IO_default_xsputn
...略...





その後のやりとり (Twitter)

20180513_80620

そして核心に迫る追加資料が...あったんです。(しばちょうさん風にはしづらかったw)





正常時(試験時)の追加情報はないようです。リリース後は常に遅いらしい。

ふむふむ。

以下、遅延時のstrace -c -pの情報を取得してもらいました。
straceで眺めるとsystem call write()がダントツで上位にきています。その次が lseek()

追加資料4
strace -c -p

[oracle@localhost ˜]$ sudo strace -c -p 13058
Process 13058 attached
^CProcess 13058 detached
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
77.24 47.883538 1 35085592 write
19.22 11.913638 1 17542850 lseek
3.53 2.191099 1 3414889 getrusage
0.01 0.006377 12 524 semop
0.00 0.000403 7 62 close
0.00 0.000210 4 54 chown
0.00 0.000126 2 63 open
0.00 0.000099 2 54 lstat
0.00 0.000050 1 54 chmod
0.00 0.000029 1 54 stat
0.00 0.000028 3 10 semtimedop
0.00 0.000015 0 54 fcntl
0.00 0.000010 1 8 read
0.00 0.000007 1 8 select
0.00 0.000000 0 3 mmap
0.00 0.000000 0 6 rt_sigprocmask
0.00 0.000000 0 1 rt_sigreturn
0.00 0.000000 0 1 readlink
------ ----------- ----------- --------- --------- ----------------
100.00 61.995629 56044287 total

そろそろ突き止めた感が。

なにかを、どこかに、書き出してますよね。

lsofで書き込みに絞って取得してもった結果は以下のとおり。

[root@localhost ˜]# lsof -p 13058 | grep -E '[0-9]+w'
oracle_13 13058 oracle 1w CHR 1,3 0t0 1038 /dev/null
oracle_13 13058 oracle 2w CHR 1,3 0t0 1038 /dev/null
oracle_13 13058 oracle 7w REG 8,17 2351882843 71176413 /u01/app/oracle/diag/rdbms/orcl12c/orcl12c/trace/orcl12c_ora_13058.trc
oracle_13 13058 oracle 8w REG 8,17 358606101 71176414 /u01/app/oracle/diag/rdbms/orcl12c/orcl12c/trace/orcl12c_ora_13058.trm

ほう。 .trcファイルへ大量の書き込みがあります。

このようになるときは、 なにが起っていると思いますか?

.trcファイルに大量の書き込みのあるケースとして、SQLトレースが有効になっている場合です。(意図したSQLトレースなら問題はないですが...意図していない場合はちょいと問題ですよね。)

ということで、確定診断に移りましょう。

SQL*Plus: Release 12.2.0.1.0 Production on Wed Apr 25 01:23:13 2018

Copyright (c) 1982, 2016, Oracle. All rights reserved.

Connected.
ORCL@SYSTEM>
ORCL@SYSTEM> select username,status,event,sql_trace from v$session where paddr = (select addr from v$process where spid = 13058)

USERNAME STATUS EVENT SQL_TRAC
------------------------------ -------- ---------------------------------------------------------------- --------
SCOTT ACTIVE log file switch completion ENABLED

見ての通り、
SQL_TRACE列がdisabled(デフォルト)からenabledに変わっていることから、.trcファイルへの大量の書き出しは、セッションレベルでSQLトレースが有効化されたため、と言えますよね。


今回の新規リリースバッチ処理遅延の原因は、

SQLトレースを有効化したままリリースしたしまったことにより、オーバーヘッドが発生し、グルグル系バッチ処理のスループットが低下したため! 

ということでした。SQLトレースを利用したあとの無効化をお忘れなく! :)


SQLトレースは便利な機能の一つですが、インスタンスレベルやグルグル系バッチ処理での利用時は大量のトレース情報の書き出し等によるオーバーヘッドが発生します。
便利なツールではありますが、スループットの低下等による影響を考慮して利用するタイプのツールでもあります。
また、利用した後は、必ず、無効化することもお忘れなく...

ミイラ取りがミイラになってしまってはいみないですから.....ね :)

| | | コメント (0) | トラックバック (0)

2018年2月22日 (木)

Temp落ち #4 - 手動PGA管理で作業領域として指定可能な最大サイズ de Temp落ちの確認


Previously on Mac De Oracle
手動PGA管理で作業領域として指定可能な最大サイズは、2GB - 1までということの確認でした。
本当に、その程度のサイズまでPGAの作業領域が利用されるのでしょうか?。 念のために確認しておきましょう。 実は、それより少ないサイズで頭打ちなんてことは、ないかな〜 と わざとらしく言ってみたりして(意味深w

その前に、指定した作業領域を使い切れるぐらい(つまり、Temp落ちさせられる程度)のデータ量の表を準備しておきます。
今回は、Nested Loop Join(NLJ)やソート回避などのための索引は作成しません。Temp落ちのネタなので。

M1とM2の2表を作成し、それぞれ、2.5GB程度のセグメントサイズにしておきます。 なお、以下の無名PL/SQLブロックでは、FORALLを利用して1000行単位でバルク処理しています。配列を利用するのでメモリー使用量にはそれなりに配慮が必要ですが。:)
単純なぐるぐる系INSERTにしてしまうとデータ量が多い場合、性能的に辛くなってしまうので、ここ重要!

ORCL@SCOTT> l
1 CREATE TABLE m1
2 (
3 id CHAR(7) NOT NULL
4 ,rev# NUMBER NOT NULL
5 ,value NUMBER NOT NULL
6 ,description VARCHAR2(4000)
7 ,additional_info CHAR(200)
8* ) NOLOGGING
ORCL@SCOTT> /

Table created.

ORCL@SCOTT> l
1 DECLARE
2 TYPE IDS_t IS TABLE OF m1.id%TYPE INDEX BY PLS_INTEGER;
3 TYPE REV#S_t IS TABLE OF m1.rev#%TYPE INDEX BY PLS_INTEGER;
4 TYPE VALS_t IS TABLE OF m1.value%TYPE INDEX BY PLS_INTEGER;
5 IDs IDS_t;
6 REV#s REV#S_t;
7 VALs VALS_t;
8 k PLS_INTEGER := 1;
9 BEGIN
10 FOR i IN 1..100000 LOOP
11 FOR j IN 1..10 LOOP
12 IDs(k) := 'C' || TO_CHAR(i, 'FM000000');
13 REV#s(k) := j;
14 VALs(k) := i + j;
15 k := k + 1;
16 END LOOP;
17 IF MOD(i, 100) = 0 THEN
18 FORALL l in 1..1000 EXECUTE IMMEDIATE
19 'INSERT /*+ APPEND_VALUE NO_GATHER_OPTIMIZER_STATISTICS */ INTO m1 '
20 || 'VALUES(:1, :2, :3, LPAD(''X'',2000, ''X''), LPAD(''9'',200,''9''))'
21 USING IDs(l), REV#s(l), VALs(l);
22 COMMIT;
23 k := 1;
24 END IF;
25 END LOOP;
26* END;
ORCL@SCOTT> /

PL/SQL procedure successfully completed.

Elapsed: 00:01:22.45
ORCL@SCOTT> select count(1) from m1;

COUNT(1)
----------
1000000

ORCL@SCOTT> select segment_name,sum(bytes)/1024/1024/1024 "GB" from user_segments where segment_name='M1' group by segment_name;

SEGMENT_NAME GB
------------------------------ ----------
M1 2.5625

ORCL@SCOTT> l
1 CREATE TABLE m2
2 (
3 id CHAR(7) NOT NULL
4 ,rev# NUMBER NOT NULL
5 ,value NUMBER NOT NULL
6 ,description VARCHAR2(4000)
7* ) NOLOGGING
ORCL@SCOTT> /

Table created.

ORCL@SCOTT> l
1 INSERT /*+ APPEND NO_GATHER_OPTIMIZER_STATISTICS */ INTO m2
2* SELECT id,rev#,value,description FROM m1
ORCL@SCOTT> /

1000000 rows created.

Elapsed: 00:00:40.22
ORCL@SCOTT> commit;

Commit complete.

ORCL@SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'M1',no_invalidate=>false,method_opt=>'FOR ALL COLUMNS SIZE SKEWONLY');

PL/SQL procedure successfully completed.

ORCL@SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'M2',no_invalidate=>false,method_opt=>'FOR ALL COLUMNS SIZE SKEWONLY');

PL/SQL procedure successfully completed.

これで、準備完了。
ちなみに、NO_GATHER_OPTIMIZER_STATISTICSヒントを利用していますが、データ登録後に、ヒストグラムも含めて取得したかったため、バルクロード時のオンラインオプティマイザ統計収集を行わないようにするためのヒントです。
データ登録後オプティマイザ統計を取得するため、データ登録時のオンラインオプティマイザ統計のオーバーヘッドは無駄となるためです。利用できるなら利用したほうがよいとは思いますが、制限もあるのでご一読を(バルク・ロードのためのオンライン統計収集


 

手動PGA管理で2GB - 1(手動PGA管理での最大 Sort作業領域サイズ)

メモリーソートで2GBぐらいまで利用していることが確認できます。(ソートされるデータ量が2GB以下程度に制限しています。)
ORCL@SCOTT> @manual_sortwk2gb_optimal    
1* alter session set workarea_size_policy=manual

Session altered.

1* alter session set sort_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 WHERE
9 id <= 'C075000'
10 ORDER BY
11 id
12* ,rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 5.42 | 4.13 | 1.29 | 50001 | 334K |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=3534657201)
===================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
===================================================================================================================================
| 0 | SELECT STATEMENT | | | | 15 | +2 | 1 | 750K | . | 20.00 | Cpu (1) |
| 1 | SORT ORDER BY | | 752K | 439K | 16 | +1 | 1 | 750K | 2GB | 60.00 | Cpu (3) |
| 2 | TABLE ACCESS FULL | M1 | 752K | 90828 | 3 | +2 | 1 | 750K | . | 20.00 | Cpu (1) |
===================================================================================================================================

 

Temp落ちする程度のデータ量でソートさせた場合も、PGAの作業領域は一旦、2GBまで利用されていることが確認できます。
ORCL@SCOTT> @manual_sortwk2gb
1* alter session set workarea_size_policy=manual

Session altered.

1* alter session set sort_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 ORDER BY
9 id
10* ,rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
-------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read | Write | Write |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes | Reqs | Bytes |
===========================================================================================
| 46 | 11 | 15 | 20 | 66668 | 334K | 2148 | 2GB | 2146 | 2GB |
===========================================================================================

SQL Plan Monitoring Details (Plan Hash Value=3534657201)
=====================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=====================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 60 | +2 | 1 | 1M | | | | | . | . | 6.38 | Cpu (2) |
| | | | | | | | | | | | | | | | | PGA memory operation (1) |
| 1 | SORT ORDER BY | | 1M | 553K | 61 | +1 | 1 | 1M | 2148 | 2GB | 2146 | 2GB | 2GB | 2GB | 91.49 | Cpu (32) |
| | | | | | | | | | | | | | | | | direct path read temp (10) |
| | | | | | | | | | | | | | | | | direct path write temp (1) |
| 2 | TABLE ACCESS FULL | M1 | 1M | 90822 | 27 | +2 | 1 | 1M | | | | | . | . | 2.13 | Cpu (1) |
=====================================================================================================================================================================================

 

手動PGA管理で2GB - 1(手動PGA管理での最大 Hash作業領域サイズ) Hash Joinの場合も、手動PGA管理で設定可能な最大サイズ程度まで利用されていることが確認できます。(Temp落ちしない程度のデータ量にしています。)

ORCL@SCOTT> @manual_hashwk2gb_optimal
1* alter session set workarea_size_policy = manual

Session altered.

1* alter session set hash_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 LEADING(m1 m2)
5 USE_HASH(m2)
6 */
7 *
8 FROM
9 m1
10 INNER JOIN m2
11 ON
12 m1.id = m2.id
13 AND m1.rev# = m2.rev#
14 WHERE
15* m1.id <= 'C075000'
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes |
===========================================================================
| 8.05 | 4.66 | 0.16 | 3.23 | 50001 | 717K | 2618 | 3GB |
===========================================================================

SQL Plan Monitoring Details (Plan Hash Value=1822065247)
==================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
==================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 21 | +2 | 1 | 750K | | | . | 12.50 | Cpu (1) |
| 1 | HASH JOIN | | 752K | 181K | 22 | +1 | 1 | 750K | | | 2GB | 25.00 | Cpu (2) |
| 2 | TABLE ACCESS FULL | M1 | 752K | 90828 | 2 | +2 | 1 | 750K | | | . | 25.00 | Cpu (2) |
| 3 | TABLE ACCESS FULL | M2 | 759K | 90581 | 20 | +3 | 1 | 750K | 2618 | 3GB | . | 37.50 | Cpu (3) |
==================================================================================================================================================

 

Hash Joinの場合もSort時と同じように、一時表領域も利用させる程度のデータ量になると、一旦、手動PGA管理で設定可能な最大サイズ程度まで利用したうえでTemp落ちしていることが確認できます。

ORCL@SCOTT> @manual_hashwk2gb
1* alter session set workarea_size_policy = manual

Session altered.

1* alter session set hash_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 LEADING(m1 m2)
5 USE_HASH(m2)
6 */
7 *
8 FROM
9 m1
10 INNER JOIN m2
11 ON
12 m1.id = m2.id
13* AND m1.rev# = m2.rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read | Write | Write |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes | Reqs | Bytes |
===========================================================================================
| 12 | 5.68 | 2.17 | 4.59 | 66668 | 706K | 3133 | 3GB | 515 | 511MB |
===========================================================================================

SQL Plan Monitoring Details (Plan Hash Value=1822065247)
=====================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=====================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 30 | +2 | 1 | 1M | | | | | . | . | 25.00 | Cpu (4) |
| 1 | HASH JOIN | | 1M | 189K | 31 | +1 | 1 | 1M | 515 | 511MB | 515 | 511MB | 2GB | 515MB | 43.75 | Cpu (5) |
| | | | | | | | | | | | | | | | | direct path read temp (1) |
| | | | | | | | | | | | | | | | | direct path write temp (1) |
| 2 | TABLE ACCESS FULL | M1 | 1M | 90822 | 6 | +0 | 1 | 1M | | | | | . | . | 12.50 | Cpu (2) |
| 3 | TABLE ACCESS FULL | M2 | 1M | 90574 | 24 | +5 | 1 | 1M | 2618 | 3GB | | | . | . | 18.75 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (1) |
=====================================================================================================================================================================================

おまけ 1つのSQLの実行で利用されるPGAの作業領域は1つだけではないということも確認しておきましょうかね。(知ってる方はスルーしてよいですよ:)
以下の例では、MERGE JOINさせていますが、表M1と表M2それぞれのSort作業領域は、Merge Join終了時まで保持されることになるため、最大3GBのSort作業領域が利用されています。(SQLモニターの結果ではわかりにくいのですが。。。)
なお、手動PGA管理、自動PGA管理に関係なく、実行される操作により複数の作業領域が同時に確保されることがあります。
(ちなみに、自動PGA管理で利用されるPGA_AGGREGATE_LIMITがPGA_AGGREGATE_TARGETの2倍とされるのも、このような動きが考慮された結果だと知っていると、納得感はあるかもしれません。)

 

前回使った図で朱色で示したSQL Work Areaが複数ありますが、Hash/Sort/Bitmap系など複数のタイプおよび同一タイプの作業領域が同時に確保されることも意図した図になっているのは、これが理由なんです。

Structure_of_pga
ORCL@SCOTT> @manual_sortwk2gb2_optimal
1* alter session set workarea_size_policy = manual

Session altered.

1* alter session set sort_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 USE_MERGE(m1 m2)
5 */
6 *
7 FROM
8 m1
9 INNER JOIN m2
10 ON
11 m1.id = m2.id
12 AND m1.rev# = m2.rev#
13 WHERE
14* m1.id <= 'C075000'
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
-------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes |
===========================================================================
| 17 | 10 | 4.75 | 2.19 | 50001 | 667K | 5251 | 5GB |
===========================================================================

SQL Plan Monitoring Details (Plan Hash Value=1391069689)
========================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
========================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 23 | +10 | 1 | 750K | | | . | 14.29 | Cpu (2) |
| 1 | MERGE JOIN | | 752K | 849K | 23 | +10 | 1 | 750K | | | . | | |
| 2 | SORT JOIN | | 752K | 439K | 32 | +1 | 1 | 750K | | | 2GB | 21.43 | Cpu (3) |
| 3 | TABLE ACCESS FULL | M1 | 752K | 90828 | 8 | +2 | 1 | 750K | 2633 | 3GB | . | 35.71 | Cpu (3) |
| | | | | | | | | | | | | | direct path read (2) |
| 4 | SORT JOIN | | 759K | 410K | 23 | +10 | 750K | 750K | | | 1GB | 14.29 | Cpu (2) |
| 5 | TABLE ACCESS FULL | M2 | 759K | 90581 | 5 | +10 | 1 | 750K | 2618 | 3GB | . | 14.29 | Cpu (2) |
========================================================================================================================================================

SQLモニターの結果ではわかりにくいわけですが、v$sesstatをから眺めれば状況がよくわかります!
2つのソート作業領域が同時に確保されたことで、2GB + 1GB = 3GB程度のサイズにまで達していることが確認できます。

ORCL@SYSTEM> r
1 select
2 vss.value/1024/1024/1024 "GB"
3 ,vsn.name
4 ,vss.sid
5 from
6 v$sesstat vss
7 inner join v$statname vsn
8 on
9 vss.statistic# = vsn.statistic#
10 and vss.con_id = vsn.con_id
11 and vsn.name like '%pga%'
12 where
13 sid IN (select sid from v$session where username='SCOTT')
14 order by
15* sid,name

GB NAME SID
---------- ---------------------------------------------------------------- ----------
3.04611414 session pga memory 203
3.04611414 session pga memory max 203

では、次回はやっとw、真打、自動PGA管理下での確認。(引っ張り過ぎかもしれないw)
つづく。


Temp落ち #1 - "Temp落ち" って?
Temp落ち #2 - PGA (Program Global Area)
Temp落ち #3 - 手動PGA管理で作業領域として指定可能な最大サイズ

 

| | | コメント (0) | トラックバック (0)

2017年12月 4日 (月)

”utl_file I/O” - この症状はあれの可能性が高いですね。

JPOUG Advent Calendar 2017の4日目のエントリーです。

さて、最近あまりお目にかかってなかったUTL_FILEパッケージで表データをcsvに書きだすネタにしました。

先日、UTL_FILEパッケージを利用した処理が想定以上に遅いという相談をうけました。
AWRレポートをみると、上位の待機イベントは、”utl_file I/O"。

!!!!おーーーこれは、珍しいというか、久々にみた病気w

UTL_FILEパッケージを利用したI/Oをグルグルしているとか、でかいファイル読み書きしているかの、どちらかですよねw
ということで、この症状の治療法を考えてみます。


<参考 - 環境>

MacBook:˜ system_profiler SPHardwareDataType | grep -E 'Model|Processor|Cores|Memory'
Model Name: MacBook
Model Identifier: MacBook9,1
Processor Name: Intel Core m5
Processor Speed: 1.2 GHz
Number of Processors: 1
Total Number of Cores: 2
Memory: 8 GB

ホストOS
MacBook:˜ discus$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.13.1
BuildVersion: 17B1003

MacBook:˜ discus$ VBoxManage -version
5.1.30r118389


ゲストOSとOracle Database
orcl@SYS> !uname -a
Linux localhost.localdomain 4.1.12-94.3.6.el7uek.x86_64 #2 SMP Tue May 30 19:25:15 PDT 2017 x86_64 x86_64 x86_64 GNU/Linux

orcl@SYS> !cat /etc/oracle-release
Oracle Linux Server release 7.3

orcl@SYS>
orcl@SYS> select * from v$version;

BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
PL/SQL Release 12.2.0.1.0 - Production 0
CORE 12.2.0.1.0 Production 0
TNS for Linux: Version 12.2.0.1.0 - Production 0
NLSRTL Version 12.2.0.1.0 - Production 0

UTL_FILEでファイル出力する際、ちょっとした手順の漏れが性能差として現れてしまうことがあります。
表の行長は数百バイト程度ですが、行数は数千万〜数億行なんていう状況だと、性能差が顕著に現れてしまうので注意が必要です。

以下の表、セグメントサイズは1GB程度ですが、行数は1千万行以上あります。
この表データをUTL_FILEパッケージを利用してcsvに出力してみます。

ORCL@SCOTT> select segment_name,sum(bytes)/1024/1024/1024 "GB" from user_segments group by segment_name;

SEGMENT_NAME GB
------------------------------ ----------
ABOUT_100BYTES_ROWS .9140625
PK_ABOUT_100BYTES_ROWS .171875


ORCL@SCOTT> select count(1) from about_100bytes_rows;

COUNT(1)
----------
10737420

表の1行は100bytes(改行コード含)です。

ORCL@SCOTT%gt; r
1 SELECT
2 LENGTH(
3 TO_CHAR(id,'FM000000000000000000000000000009')
4 ||','||FOO
5 ) row_length
6 FROM
7 about_100bytes_rows
8 WHERE
9* rownum <= 1

ROW_LENGTH
----------
99

次の2つのコードの赤太文字部分に着目してください。
その部分が異なるだけで処理時間に大きな差が出ます。

DECLARE
cDIR_NAME CONSTANT VARCHAR2(30) := 'FILES_DIR';
cFILE_NAME CONSTANT VARCHAR2(128) := 'no_writebuffering_'||TO_CHAR(systimestamp, 'rrmmddhh24miss.ff')||'.txt';
cBufferSize CONSTANT BINARY_INTEGER := 32767;
cOpenMode CONSTANT VARCHAR2(2) := 'w';
fileHandle UTL_FILE.FILE_TYPE;

cBulkReadLimit CONSTANT PLS_INTEGER := 324;
TYPE tBulkReadArray IS TABLE OF VARCHAR2(8192) INDEX BY BINARY_INTEGER;
bulkReadArray tBulkReadArray;
CURSOR cur_about100bytesRow IS
SELECT
TO_CHAR(id,'FM000000000000000000000000000009')
||','||FOO
AS csvrow
FROM
about_100bytes_rows
;
BEGIN
OPEN cur_about100bytesRow;
fileHandle := UTL_FILE.FOPEN(cDIR_NAME, cFILE_NAME, cOpenMode, cBufferSize);
LOOP
FETCH cur_about100bytesRow
BULK COLLECT INTO bulkReadArray
LIMIT cBulkReadLimit;

EXIT WHEN bulkReadArray.COUNT = 0;

FOR i IN bulkReadArray.FIRST..bulkReadArray.LAST LOOP
UTL_FILE.PUT_LINE(
file => fileHandle
, buffer => bulkReadArray(i)
, autoflush => true
);
END LOOP;
END LOOP;
UTL_FILE.FFLUSH(fileHandle);
UTL_FILE.FCLOSE(fileHandle);
CLOSE cur_about100bytesRow;
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(fileHandle) THEN
UTL_FILE.FCLOSE(fileHandle);
END IF;

IF cur_about100bytesRow%ISOPEN THEN
CLOSE cur_about100bytesRow;
END IF;
RAISE;
END;
/


PL/SQLプロシージャが正常に完了しました。

経過: 00:02:27.05


DECLARE
cDIR_NAME CONSTANT VARCHAR2(30) := 'FILES_DIR';
cFILE_NAME CONSTANT VARCHAR2(128) := 'writebuffering_'||TO_CHAR(systimestamp, 'rrmmddhh24miss.ff')||'.txt';
cBufferSize CONSTANT BINARY_INTEGER := 32767;
cOpenMode CONSTANT VARCHAR2(2) := 'w';
fileHandle UTL_FILE.FILE_TYPE;
buffer VARCHAR2(32767);

cBulkReadLimit CONSTANT PLS_INTEGER := 324;
TYPE tBulkReadArray IS TABLE OF VARCHAR2(8192) INDEX BY BINARY_INTEGER;
bulkReadArray tBulkReadArray;
CURSOR cur_about100bytesRow IS
SELECT
TO_CHAR(id,'FM000000000000000000000000000009')
||','||FOO
AS csvrow
FROM
about_100bytes_rows
;
BEGIN
OPEN cur_about100bytesRow;
fileHandle := UTL_FILE.FOPEN(cDIR_NAME, cFILE_NAME, cOpenMode, cBufferSize);
LOOP
FETCH cur_about100bytesRow
BULK COLLECT INTO bulkReadArray
LIMIT cBulkReadLimit;

EXIT WHEN bulkReadArray.COUNT = 0;

buffer := NULL;
FOR i IN bulkReadArray.FIRST..bulkReadArray.LAST LOOP
buffer := buffer || bulkReadArray(i) || UTL_TCP.CRLF;
END LOOP;
UTL_FILE.PUT(fileHandle, buffer);
UTL_FILE.FFLUSH(fileHandle);
END LOOP;
UTL_FILE.FFLUSH(fileHandle);
UTL_FILE.FCLOSE(fileHandle);
CLOSE cur_about100bytesRow;
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(fileHandle) THEN
UTL_FILE.FCLOSE(fileHandle);
END IF;

IF cur_about100bytesRow%ISOPEN THEN
CLOSE cur_about100bytesRow;
END IF;
RAISE;
END;
/


PL/SQLプロシージャが正常に完了しました。

経過: 00:00:43.96


何が違うかお分ですよね!
UTL_FILE.PUT_LINE/UTL_FILE.PUTが違う!!w
その部分は重要ではなく、100Bytes単位に書き込んでいるか、約32KB単位で書き込んでいるかが重要なんです。

UTL_FILEは最大32767バイトのバッファを利用できますが、前者はバッファを有効利用せず、100Bytes毎に書き出しています。後者は約32KB単位で書き出しています。

その差はAWRレポートからも見えてきます。


AWRレポート(一部抜粋)
UTL_FILEパッケージによるI/Oの待機は、”utl_file I/O”という待機イベントで現れます。

Avg Waitは短いですが、理由は100バイト単位の小さいなサイズの書き込みを繰り返しているわけなので、そんなとこでしょう。

Top 10 Foreground Events by Total Wait Time

Total Wait Avg % DB Wait
Event Waits Time (sec) Wait time Class
------------------------------ ----------- ---------- --------- ------ --------
DB CPU 147.4 99.3
utl_file I/O 32,212,266 49.5 1.54us 33.3 User I/O
direct path read 1,881 1.2 620.61us .8 User I/O
resmgr:cpu quantum 1 .1 85.85ms .1 Schedule
db file sequential read 17 0 .98ms .0 User I/O
PGA memory operation 90 0 35.13us .0 Other
latch: shared pool 1 0 1.05ms .0 Concurre
control file sequential read 80 0 12.83us .0 System I
Disk file operations I/O 6 0 73.67us .0 User I/O
SQL*Net message to client 2 0 9.00us .0 Network


一方、約32KB単位でバッファリングして書き出している場合、Waits回数が激減しています。
Avg Waitsは大きくなっていますが、書き出しサイズにほぼ比例しているので想定通りというところ。

Top 10 Foreground Events by Total Wait Time

Total Wait Avg % DB Wait
Event Waits Time (sec) Wait time Class
------------------------------ ----------- ---------- --------- ------ --------
DB CPU 45 98.5
utl_file I/O 66,288 27.4 413.54us 60.1 User I/O
direct path read 1,881 1 550.71us 2.3 User I/O
db file sequential read 88 0 443.38us .1 User I/O
direct path write 2 0 4.69ms .0 User I/O
control file sequential read 80 0 33.25us .0 System I
latch: shared pool 1 0 2.38ms .0 Concurre
Disk file operations I/O 4 0 323.50us .0 User I/O
PGA memory operation 79 0 15.73us .0 Other
SQL*Net message to client 4 0 20.25us .0 Network

utl_file.put/put_lineでcsvを出力しているdeviceのiostat(util%)
まだまだ余裕があるのでI/Oで詰まっているのではなく、UTL_FILE.PUT/PUT_LINEの使い方の影響が大きいということですね。
20171203_202342


最後に、
UTL_FILEパッケージの入出力時にはバッファの有効利用をお忘れなく。(つい忘れちゃうこともあるので、急いでるときとかw)
扱うデータが多い場合は得に。

そして、みなさま、
メリークリスマス(まだエントリーを書くかもしれませんがw)
#JPOUG

| | | コメント (0) | トラックバック (0)

2017年11月 7日 (火)

DBMS_ADVISOR.CREATE_FILE()プロシージャは、Diagnostic/Tuning Pack不要らしいということの確認

Previously on Mac De Oracle



余談

DBMS_ADVISOR.CREATE_FILEとなっているがライセンス上、diagnostic/tuning packライセンスはなくてもアクセスしてできるように読み取れるのだけと、もしかすると、この影響で、DBMS_ADVISOR.CREATE_FILEの存在は知っていても使ってないのかあるのかな??

以下のマニュアルを読む限り、Diagnostic/Tuning Packでは、DBMS_ADVISORパッケージ全体ではなく、Diagnostic/Tuning Packに関連するパラメータを与える必要のある機能についての制限であることしか記載されていない。
つまり、DBMS_ADVISOR.CREATE_FILE()に関して言えば、Diagnostic/Tuning Pack特有の機能ではないから利用可能なはず。 ←間違ってたらツッコミ希望w

Oracle® Databaseライセンス情報 12cリリース1 (12.1) 2 オプションおよびパック
https://docs.oracle.com/cd/E49329_01/license.121/b71334/options.htm



なんてことを書いたところ、

control_management_pack_access=none

でも使えるとなら間違いなくないんじゃないの?
というコメントがあったので試してみました。 マニュアルの通りだと思われます。ということですね。FAQ!

orcl@SYS> show parameter control_management_pack_access

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
control_management_pack_access string NONE
orcl@SYS>
orcl@SYS> l
1 DECLARE
2 reportClob CLOB := EMPTY_CLOB();
3 BEGIN
4 reportClob := 'test test test';
5 DBMS_ADVISOR.CREATE_FILE(
6 buffer => reportClob
7 ,location => 'REP_DIR'
8 ,filename => 'test.txt'
9 );
10* END;
orcl@SYS> /

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.23
orcl@SYS> !ls report
test.txt

orcl@SYS> !cat report/test.txt
test test test



DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよw
SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ)
SQL Tuning Set (STS)のフィルタリング
DBMS_SQLTUNE.REPORT_ANALYSIS_TASK()ファンクションで生成されるCLOB型のレポートをファイルに保存する簡単な方法

| | | コメント (0) | トラックバック (0)

2017年11月 3日 (金)

DBMS_SQLTUNE.REPORT_ANALYSIS_TASK()ファンクションで生成されるCLOB型のレポートをファイルに保存する簡単な方法

Previously on Mac De Oracle
STSとの格闘というかSPAとの格闘に疲れてきたところw ですが、
SQL Tuning Set (STS)のフィルタリング
などで、SQLSETを程よい大きさに分割するところまでたどり着きましたw

今回は、いくつかある細かい分析方法については、一旦、置いといて、
分析レポートを保存する簡単な方法についての備忘録

分析などにこの記事も参考になると思いますが、紹介している記事でも分析レポートをファイルに保存する方法はSQL*Plusのコマンドを駆使して行われています。
個人的には少々やぼったい方法かなと感じている方法なんですが、以前はこんなやりかたが多かったようにも思います。

var rep CLOB
...略...
EXEC :rep := DBMS_SQLSPA.REPORT_ANALYSIS_TASK('STSNAM','TEXT',...
spo hoge_report.txt
print :rep
spo off

SQL*PlusとPL/SQLが行ったり来たりするところや、ループ制御しにくいので、この方法は避けたい。。。。

かといって、DBMS_OUTPUT.PUT_LINEで行おうとするとset linesize等の制御が面倒くさいし
結局、SQL*Plusの機能にも依存してしまうので、いまひとつ。

PL/SQLだけで行う方法として、UTL_FILEパッケージを利用してファイル出力する方法もありますが、
UTL_FILEパッケージで提供されているAPIはlow levelなものばかりなので、急ぎの時にはめんどくさくて、やだw

もっと簡単なやつないの?????

あります!w

DBMS_ADVISOR.CREATE_FILE(buffer, location, filename)

なぜ、UTL_FILEパッケージに入れてくれないんだw と思いたくなる方もいるかと思いますが、 理由はよくわかりませんw
(UTL_FILEパッケージはlow levelなAPIだけだから入れたくないのかも、かも、かも。と思ってますが、DBMS_ADVISORパッケージに入れるとは。どういうつもりだ!w)

UTL_FILE.CREATE_FILE()ってあったほうが直感的に探しやすくないかw UTL_FILEみて無い!!
となると、他を探すこともあまりなくなって、結局、コード書いてる場面を多く見てる>< 



余談

DBMS_ADVISOR.CREATE_FILEとなっているがライセンス上、diagnostic/tuning packライセンスはなくてもアクセスしてできるように読み取れるのだけと、もしかすると、この影響で、DBMS_ADVISOR.CREATE_FILEの存在は知っていても使ってないのかあるのかな??

以下のマニュアルを読む限り、Diagnostic/Tuning Packでは、DBMS_ADVISORパッケージ全体ではなく、Diagnostic/Tuning Packに関連するパラメータを与える必要のある機能についての制限であることしか記載されていない。
つまり、DBMS_ADVISOR.CREATE_FILE()に関して言えば、Diagnostic/Tuning Pack特有の機能ではないから利用可能なはず。 ←間違ってたらツッコミ希望w

Oracle® Databaseライセンス情報 12cリリース1 (12.1) 2 オプションおよびパック
https://docs.oracle.com/cd/E49329_01/license.121/b71334/options.htm


とりあえず、SQLSETの分析レポートをDBMS_ADVISOR.CREATE_FILE()を利用して保存するやっつけスクリプトを作ってみました
これを元に育てていけるかなw


実行例)

注)STS20171102というSQLSETを事前に作成してあります。


orcl@SYS> select * from v$version;

BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
PL/SQL Release 12.2.0.1.0 - Production 0
CORE 12.2.0.1.0 Production 0
TNS for Linux: Version 12.2.0.1.0 - Production 0
NLSRTL Version 12.2.0.1.0 - Production 0


orcl@SYS> select owner,name,statement_count,created from dba_sqlset order by name;

OWNER NAME STATEMENT_COUNT CREATED
---------- ------------------------------ --------------- ---------
SPAUSER STS20171102 1003 02-NOV-17


spa.sqlのパラメータは、SQLSETと分析レポートの出力ディレクトリオブジェクトとSQLを実行するスキーマ向けのDBリンクです。

スクリプトの概要
・初回の分析(SQLSETで元のSQLを分析)
・2回目の分析(新環境でSQLを実行して分析)
・初回と2回目の比較レポート作成/保存
・レポートを出力後、分析に利用したタスクを削除



orcl@SYS> @spa STS20171102 rep_dir link_4_target

task name:STS20171102 has been droped.
***END***

PL/SQL procedure successfully completed.

分析レポートは、ディレクトリオブジェクトに対応づけられたパスに保存されます

orcl@SYS> !ls -l report
-rw-r--r-- 1 oracle oinstall 13688 11月 3 02:44 DIFF_STS20171102171103024433.txt

orcl@SYS> !cat report/DIFF_STS20171102171103024433.txt

General Information
---------------------------------------------------------------------------------------------

Task Information: Workload Information:
--------------------------------------------- ---------------------------------------------
Task Name : STS20171102 SQL Tuning Set Name : STS20171102
Task Owner : SYS SQL Tuning Set Owner : SPAUSER
Description : Total SQL Statement Count : 1003

Execution Information:
---------------------------------------------------------------------------------------------
Execution Name : DIFF_STS20171102 Started : 11/03/2017 02:44:26
Execution Type : COMPARE PERFORMANCE Last Updated : 11/03/2017 02:44:29
Description : Global Time Limit : UNLIMITED
Scope : COMPREHENSIVE Per-SQL Time Limit : UNUSED
Status : COMPLETED Number of Errors : 0
Number of Unsupported SQL : 4

Analysis Information:
---------------------------------------------------------------------------------------------
Before Change Execution: After Change Execution:
--------------------------------------------- ---------------------------------------------
Execution Name : SOURCE_STS20171102 Execution Name : TARGET_STS20171102
Execution Type : CONVERT SQLSET Execution Type : TEST EXECUTE REMOTE
Scope : COMPREHENSIVE Database Link : LINK_4_TARGET
Status : COMPLETED Scope : COMPREHENSIVE
Started : 11/03/2017 02:44:04 Status : COMPLETED
Last Updated : 11/03/2017 02:44:04 Started : 11/03/2017 02:44:04
Global Time Limit : UNLIMITED Last Updated : 11/03/2017 02:44:18
Per-SQL Time Limit : UNUSED Global Time Limit : UNLIMITED
Per-SQL Time Limit : UNUSED
Number of Errors : 0

---------------------------------------------
Comparison Metric: ELAPSED_TIME
------------------
Workload Impact Threshold: 1%
--------------------------
SQL Impact Threshold: 1%
----------------------

Report Summary
---------------------------------------------------------------------------------------------

Projected Workload Change Impact:
-------------------------------------------
Overall Impact : 0%
Improvement Impact : 0%
Regression Impact : 0%

SQL Statement Count
-------------------------------------------
SQL Category SQL Count Plan Change Count
Overall 999 490
Unchanged 995 490
Unsupported 4 0

Top 100 SQL Sorted by Absolute Value of Change Impact on the Workload
---------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
| | | Impact on | Execution | Metric | Metric | Impact | Plan |
| object_id | sql_id | Workload | Frequency | Before | After | on SQL | Change |
-----------------------------------------------------------------------------------------
| 1477 | 7hys3h7ysgf9m | .1% | 1 | 14630 | 33 | 99.77% | n |
| 1811 | cw6vxf0kbz3v1 | .03% | 1 | 3602 | 24 | 99.33% | n |
| 1175 | 2s9mmb6g8kbqb | .01% | 1 | 1537 | 20 | 98.7% | n |

...中略...

| 1691 | az25yp5qunj77 | .01% | 1 | 882 | 20 | 97.73% | n |
| 1896 | faun6bjrjqr17 | .01% | 1 | 881 | 19 | 97.84% | n |
| 1956 | g7a2tmw1nrxbh | .01% | 1 | 881 | 19 | 97.84% | n |
-----------------------------------------------------------------------------------------
Note: time statistics are displayed in microseconds
---------------------------------------------------------------------------------------------


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

スクリプトは以下のとおり、無名PL/SQLブロックにしてあります。
レポートのタイプなどは固定にしてますが、それらも含めてパラメータにしてもいいかも。

ちなみに、このエントリの本題である、DBMS_ADVISOR.CREATE_FILE()は赤字部分です!

orcl@SYS> !cat spa.sql

set serveroutput on
DECLARE
report_clob CLOB;
cSQLSET_OWNER VARCHAR2(30) := 'SPAUSER';
cSQLSET_NAME VARCHAR2(20) := UPPER('&1');
cEXECUTION_NAME_PREFIX_SOURCE VARCHAR2(7) := 'SOURCE_';
cEXECUTION_NAME_PREFIX_TARGET VARCHAR2(7) := 'TARGET_';
cEXECUTION_NAME_PREFIX_DIFF VARCHAR2(5) := 'DIFF_';
cREPORT_TYPE CONSTANT VARCHAR2(4) := 'text';
cFILE_EXTENTION CONSTANT VARCHAR2(5) := '.txt';
cDIRECTORY_NAME CONSTANT VARCHAR2(30) := UPPER('&2');
cDB_LINK_4_PARSING_SCHEMA CONSTANT VARCHAR2(128) := UPPER('&3');
stmt_task VARCHAR2(64);

PROCEDURE drop_task (iTASK_NAME VARCHAR2)
IS
BEGIN
FOR task_rec IN (
SELECT
task_name
FROM
dba_advisor_tasks
WHERE
task_name = iTASK_NAME
AND advisor_name = 'SQL Performance Analyzer'
) LOOP
DBMS_OUTPUT.PUT_LINE(
'task name:'||task_rec.task_name||' has been droped.'
);
DBMS_SQLPA.DROP_ANALYSIS_TASK(
task_name => task_rec.task_name
);
END LOOP;
END drop_task;
BEGIN
--Create analysis task
stmt_task := DBMS_SQLPA.CREATE_ANALYSIS_TASK(
sqlset_name => cSQLSET_NAME
,basic_filter => 'parsing_schema_name in (''SCOTT'')'
,sqlset_owner => cSQLSET_OWNER
,task_name => cSQLSET_NAME
);

--SQL Analysis on Source DB
DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(
task_name => cSQLSET_NAME
,execution_type => 'CONVERT SQLSET'
,execution_name =>
cEXECUTION_NAME_PREFIX_SOURCE
|| cSQLSET_NAME
,execution_params =>
DBMS_ADVISOR.ARGLIST(
'sqlset_name', cSQLSET_NAME
,'sqlset_owner', cSQLSET_OWNER
)
);

--SQL Analysis on Target DB
DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(
task_name => cSQLSET_NAME
,execution_type => 'EXECUTE'
,execution_name =>
cEXECUTION_NAME_PREFIX_TARGET
|| cSQLSET_NAME
,execution_params =>
DBMS_ADVISOR.ARGLIST(
'DATABASE_LINK', cDB_LINK_4_PARSING_SCHEMA
,'EXECUTE_FULLDML', 'TRUE'
)
);

--Comparison Source and Target Analysys
DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(
task_name => cSQLSET_NAME
,execution_type => 'COMPARE'
,execution_name =>
cEXECUTION_NAME_PREFIX_DIFF
|| cSQLSET_NAME
,execution_params =>
DBMS_ADVISOR.ARGLIST(
'PLAN_LINES_COMPARISON','ALWAYS'
)
);

--Make comparison report
report_clob := DBMS_SQLPA.REPORT_ANALYSIS_TASK(
task_name => cSQLSET_NAME
,execution_name =>
cEXECUTION_NAME_PREFIX_DIFF
|| cSQLSET_NAME
,type => cREPORT_TYPE
,level => 'typical'
,section => 'summary'
);

--Save comparison report
DBMS_ADVISOR.CREATE_FILE(
buffer => report_clob
,location => cDIRECTORY_NAME
,filename =>
cEXECUTION_NAME_PREFIX_DIFF
||cSQLSET_NAME
||TO_CHAR(systimestamp, 'RRMMDDHH24MISS')
||cFILE_EXTENTION
);


--Drop Task
drop_task(cSQLSET_NAME);
DBMS_OUTPUT.PUT_LINE('***END***');
EXCEPTION
WHEN OTHERS THEN
drop_task(cSQLSET_NAME);
RAISE;
END;
/



DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよw
SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ)
SQL Tuning Set (STS)のフィルタリング

| | | コメント (0) | トラックバック (0)

2017年10月30日 (月)

SQL Tuning Set (STS)のフィルタリング

Previously on Mac De Oracle
前回は、やっつけすくりぷとを書いたところまででした。

ということで、今回のやっつけスクリプトの準備というかアイデア

SQLSET大量のSQL文キャプチャされていて、SQLSETにキャプチャされている大量のSQL文を一括でSPAでまわしたりすることが辛い場合があります。
そんなときはどうすればよいか。。なやみますよね。。。ほんと。。
SQL Performance AnalyzerによるSQLパフォーマンス比較実行例(API)

SQLSETから、より細かなSQLSETにほぼ当分に分割し、新たなSQLSETを作成したいような場合もあるかもしれません。
そんな時はどうするか?

手取りばやくやるなら、ORA_HASH()が便利ですよね。(SQLSETの全表走査はさけられないのですが、Exaならw)

たとえば、以下のSQLSETがあったとして、300件程度に均等に分割したいという場合。
ora_hash()のバケット数はだいたい12ぐらいでできそうですね。

orcl@SYS> select name,statement_count,created from dba_sqlset order by name;

NAME STATEMENT_COUNT CREATED
------------------------------ --------------- ---------
STS20171029 3819 29-OCT-17

orcl@SYS> select statement_count/300 from dba_sqlset where name='STS20171029';

STATEMENT_COUNT/300
-------------------
12.73

いい感じに分割できそうです。

orcl@SYS> r
1 select
2 ora_hash(sql_id,12) as hash#
3 ,count(sql_id)
4 from
5 table(dbms_sqltune.select_sqlset(
6 sqlset_name=>'STS20171029'
7 ,basic_filter=>null
8 ))
9 group by
10 ora_hash(sql_id,12)
11* order by 1

HASH# COUNT(SQL_ID)
---------- -------------
0 289
1 293
2 278
3 296
4 297
5 281
6 282
7 298
8 288
9 304
10 305
11 298
12 310

13 rows selected.


実際に利用するには、basic_filterでwhere句の条件文そのものを書いてあげます。サブクエリも書けますが、データ量が多い場合は性能面に注意しながらいろいろ試してみるといいと思います。
basic_filterでは、SQLSET_ROWオブジェクト・タイプの属性を利用したフィルタリングができるのですが、インジェクションですよね。構文を見ている限り;)
Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1 (12.1) SQLSET_ROWオブジェクト・タイプ

orcl@SYS> set serveroutput on
orcl@SYS> r
1 begin
2 for i in 0..12 loop
3 for sqlset_rec in
4 (
5 select count(sql_id) as num_sql
6 from
7 table(dbms_sqltune.select_sqlset(
8 sqlset_name=>'STS20171029'
9 ,basic_filter=>'ora_hash(sql_id,12)='||i
10 ))
11 )
12 loop
13 dbms_output.put_line('hash#='||to_char(i,'fm99')||':'||sqlset_rec.num_sql);
14 end loop;
15 end loop;
16* end;
hash#=0:289
hash#=1:293
hash#=2:278
hash#=3:296
hash#=4:297
hash#=5:281
hash#=6:282
hash#=7:298
hash#=8:288
hash#=9:304
hash#=10:305
hash#=11:298
hash#=12:310

PL/SQL procedure successfully completed.

ハッシュ値10でsql_id数をカウント!
うまくできそうですよね。

orcl@SYS> r
1 select
2 count(sql_id)
3 from
4 table(dbms_sqltune.select_sqlset(
5 sqlset_name=>'STS20171029'
6 ,basic_filter=>'ora_hash(sql_id,12) = 10'
7* ))

COUNT(SQL_ID)
-------------
305

参考
Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1 (12.1) DBMS_SQLTUNE
Oracle® Database SQL言語リファレンス 12cリリース1 (12.1) ORA_HASH



DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよw
SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ)

| | | コメント (0) | トラックバック (0)

2017年10月 6日 (金)

DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよw

ということで、タイトル通り
マニュアル上、例外を投げるとは記載されてないのですが、テストしてたら”例外投げる”PL/SQLプロシージャ、意外と多いんですw
意図的に例外投げるよーというのは大抵マニュアルに記載されているんですが。人が書いてますからね、記載漏れも仕方ないっすねぇw 

マニュアルバグ、忘れちゃうので、自分向けFAQ and 備忘録 


Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1 (12.1) B71281-05
DBMS_SQLTUNE.PACK_STGTAB_SQLSETプロシージャ

Oracle Database PL/SQL Packages and Types Reference (12.2)のマニュアルには DBMS_SQLTUNE.PACK_STGTAB_SQLSET Procedure は例外を投げるよ!という記載はないが...

DBMS_SQLTUNE.PACK_STGTAB_SQLSET Procedure
DBMS_SQLTUNE.PACK_STGTAB_SQLSET Procedure

orcl@SYS> r
1 SELECT
2 ID
3 ,NAME
4 ,OWNER
5 ,CREATED
6 ,LAST_MODIFIED
7 ,STATEMENT_COUNT
8 FROM
9 dba_sqlset
10 ORDER BY
11 OWNER
12 ,CREATED
13*


ID NAME OWNER CREATED LAST_MODI STATEMENT_COUNT
---------- ------------------------------ ------------------------------ --------- --------- ---------------
1 STS201710052340431 SYS 05-OCT-17 05-OCT-17 0
2 STS201710061431151 SYS 06-OCT-17 06-OCT-17 2

statement_count=0と2の2つのSTS(SQL Tuning Set)があります。
STS(SQL Tuning Set)については、ここでは記載しませんが、何それ? 気になる、気になる〜という方は、
Oracle Database の STS(SQL Tuning Set) を活用して、SQL の 性能統計 や 実行計画 を キャプチャする。を覗いて見てください。

(次回のネタは思いっきりそこなので、予習にもなりますよ :)

例外を投げるとはマニュアルに記載されてないプロシージャが実は例外投げるじゃん! 
というケースは、dbms_sqltune.pack_stgtab_sqlset以外にもあるんですが、
急ぎのやっつけ仕事で遭遇すると、ここで見つかってよかった!!
という安堵感とともに、
ムカ〜〜〜〜っという憎悪も湧いてくるわけですw 
(何れにしてもテストはしっかりやりましょうw

以下、例外投げるとは書かれてないけど、投げるじゃん、dbms_sqltune.pack_stgtab_sqlset の簡易テストの記録

準備として、dbms_sqltune.pack_stgtab_sqlsetは、STSを退避する為に必要なステージング表を作成します。

orcl@SYS> exec dbms_sqltune.create_stgtab_sqlset(table_name=>'TEST',schema_name=>'STSUSR');

PL/SQL procedure successfully completed.

statement_count=0つまり、STSに記録されたSQLの情報がない場合です。
ワーニングとしての意味が強いと思われますが、ORA-15701が投げつけられて、STSが空だということを教えてくれます! :)
マニュアルには記載されてなかったので焦りますw 実際のところざっくり書いたコードではこの例外をハンドリングなんてしてませんでした。

orcl@SYS> l
1 begin
2 dbms_sqltune.pack_stgtab_sqlset(
3 sqlset_name=>'STS201710052340431'
4 ,sqlset_owner=>null
5 ,staging_table_name=>'TEST'
6 ,staging_schema_owner=>'STSUSR'
7 );
8* end;
orcl@SYS> /
begin
*
ERROR at line 1:
ORA-15701: All "SQL Tuning Set(s)" with name like "STS201710052340431" and owner like "SYS" are empty
ORA-06512: at "SYS.DBMS_SQLTUNE", line 5398
ORA-06512: at "SYS.DBMS_SQLTUNE", line 7928
ORA-06512: at line 2


一方、statement_count=2でSTSに記録されたSQLがある場合、つまり、STSは空じゃない場合は正常終了します。

orcl@SYS> l
1 begin
2 dbms_sqltune.pack_stgtab_sqlset(
3 sqlset_name=>'STS201710061431151'
4 ,sqlset_owner=>null
5 ,staging_table_name=>'TEST'
6 ,staging_schema_owner=>'STSUSR'
7 );
8* end;
orcl@SYS> /

PL/SQL procedure successfully completed.

STSが空だという例外を拾って、後続処理にあるであろう、ステージング表のエクスポートとかをバイパスしたりすることはできますね:)

dbms_sqltune.pack_stgtab_sqlsetは、STSが空だと例外投げるよ というお話でした。


To be continued...

| | | コメント (0) | トラックバック (0)

2017年7月29日 (土)

SQLとPL/SQLで「モルダー、あなた疲れてるのよ」を出力する方法

もうかれこれ1年以上経過しているので、何を今更という感じがしないでもないですがw

ここ2週間くらい、SQLチューニングじゃない方向で、非常に忙しかったこともあり

気分的に疲れてしまったので、気分転換のために作ってみました。

Ruby でモルダー、あなた疲れてるのよを出力する方法

Pythonでモルダー案件

Groovyで「モルダー、あなた疲れてるのよ」に対処する

と、どう見てもPL/SQLやSQL案件ではないネタですが、PL/SQL de Python Challenge精神(どんな精神じゃw)で無理やり案件化して見ました。

疲れてる時に疲れるネタやるの?、バカなの? 
とお感じの方もお多いかと存じますが、可能ならもっと変態的なネタに発展することを願いつつ、ご挨拶と代えさせていただきます

データベースエンジニアでも、「モルダー、あなた疲れてるよの」という文字列を出力させる無茶振り案件に突っ込まれるかもしれません。
そんなときの参考になれば幸いです。


スペースで区切ったワードをシャッフルし、「モルダー、あなた疲れてるのよ」が出力されたら終了です。


色々な「モルダー、あなた疲れてるのよ」がありますので、よろしくお願いします!

Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
に接続されました。
orcl@SYSTEM> @mulder_you_tired

スカリーより
--------------------------------------------------------------------------------
1: 疲れてる、あなたのよモルダー
2: のよ、あなたモルダー疲れてる
3: モルダー、疲れてるあなたのよ
4: 疲れてる、あなたモルダーのよ
5: モルダー、あなたのよ疲れてる
6: のよ、あなたモルダー疲れてる
7: 疲れてる、モルダーあなたのよ
8: モルダー、のよ疲れてるあなた
9: のよ、疲れてるモルダーあなた
10: 疲れてる、のよモルダーあなた
11: モルダー、あなたのよ疲れてる
12: のよ、疲れてるあなたモルダー
13: モルダー、のよあなた疲れてる
14: 疲れてる、あなたのよモルダー
15: 疲れてる、あなたモルダーのよ
16: モルダー、あなたのよ疲れてる
17: のよ、あなたモルダー疲れてる
18: モルダー、あなた疲れてるのよ


コードは以下のとおり、WITH FUNCTIONで文字列を生成しCLOBで1行で返しています。
改行コード(UTL_TCP.CRLFを利用、UTL_TCPパッケージなんて使っている人は少ないと思いますがw)を付加し出力時に改行させ複数行に見せています。
WITH FUNCTIONをSELECT文のセレクトリスト部分で利用しています。(TABLE FunctionならFROM句で使えますが、TABLE Functionではないので)

09:54:34 orcl@SYSTEM> !cat mulder_you_tired.sql
set long 100000
set pagesize 10000
WITH function mulderYouAreTired (dummy char)
RETURN CLOB
IS
cMulderYouTired CONSTANT VARCHAR2(42) := 'モルダー、あなた疲れてるのよ';
vMessage VARCHAR2(42);
vMessageFromScully CLOB := EMPTY_CLOB();
line# BINARY_INTEGER := 1;
--
FUNCTION generateMessage
RETURN VARCHAR2
IS
TYPE word_arr IS TABLE OF VARCHAR2(12) INDEX BY BINARY_INTEGER;
cPunctuation CONSTANT CHAR(3) := '、';
vWords VARCHAR2(45) := 'モルダー あなた 疲れてる のよ';
vMessageFromScully VARCHAR2(42);
vMessage word_arr;
vWord VARCHAR2(13);
isSetWord BOOLEAN := FALSE;
idx BINARY_INTEGER := 1;
BEGIN
--
FOR i IN 1..REGEXP_COUNT(vWords, ' ') + 1 LOOP
vMessage(i) := NULL;
END LOOP;
--
WHILE INSTR(vWords, ' ') != 0 LOOP
vWord := SUBSTR(vWords, 1, INSTR(vWords, ' '));
vWords := REPLACE(vWords, vWord);
isSetWord := FALSE;
WHILE NOT isSetWord LOOP
idx := TRUNC(DBMS_RANDOM.VALUE(vMessage.FIRST, vMessage.LAST + 1));
IF vMessage(idx) IS NULL THEN
vMessage(idx) := TRIM(vWord);
isSetWord := TRUE;
END IF;
END LOOP;
END LOOP;
--
FOR j IN vMessage.FIRST..vMessage.LAST LOOP
IF vMessage(j) IS NULL THEN
vMessage(j) := vWords;
END IF;
END LOOP;
--
FOR x IN vMessage.FIRST..vMessage.LAST LOOP
IF x = 2 THEN
vMessageFromScully := vMessageFromScully || cPunctuation;
END IF;
vMessageFromScully := vMessageFromScully || vMessage(x);
END LOOP;
RETURN vMessageFromScully;
END generateMessage;
BEGIN
LOOP
vMessage := generateMessage();
vMessageFromScully :=
vMessageFromScully || TO_CHAR(line#)
|| ': ' || vMessage || UTL_TCP.CRLF;
line# := line# + 1;
EXIT WHEN vMessage = cMulderYouTired;
END LOOP;
RETURN vMessageFromScully;
END;
SELECT
mulderYouAreTired(null) AS "スカリーより"
FROM
dual;
/

| | | コメント (0) | トラックバック (0)

2015年11月21日 (土)

WITH clause in-line PL/SQL functionって....

12cの新機能でオイタをしてみたw

with句に関数仕込んで内部でSQL文を実行してオイタしてみました。

実際にこのような使い方するんでしょうか?
これをやるなら素直にスカラー副問合せの方がいいと思うんですね.....
(スカラー副問合せのように使われたら、同じく12cの新機能、Scalar subquery unnestingもできなし。)

なんでこんな機能必要なんだろう?
どのような場面で、有効なんだろう?

今の所、使いどころが思い浮かばないので、誰か教えて〜〜〜〜〜! 
とモヤモヤしてたんですが、今日はお留守番で時間だけはあったので、ごにょごにょとw


SCOTT> set serveroutput on
SCOTT> set feed off
SCOTT> r
1 with
2 function hoge return varchar2
3 deterministic
4 as
5 begin
6 for rec in (select empno,ename from emp order by empno) loop
7 dbms_output.put_line('**** '||rec.empno||':'||rec.ename||' ****');
8 end loop;
9 return null;
10 end;
11 select
12 hoge
13 from
14 dual
15 where
16* hoge is not null

**** 7369:SMITH ****
**** 7499:ALLEN ****
**** 7521:WARD ****
**** 7566:JONES ****
**** 7654:MARTIN ****
**** 7698:BLAKE ****
**** 7782:CLARK ****
**** 7839:KING ****
**** 7844:TURNER ****
**** 7900:JAMES ****
**** 7902:FORD ****
**** 7934:MILLER ****

経過: 00:00:00.01

こんなことをやられると....
実行計画には現れないしw (ちなみにスカラー副問合せは実行計画を見ただけでわかりますです。はい)

ただのチューナーいじめじゃ >< w
SQLトレースなら補足できるけど、なんぎじゃのう。げほげほ

(WITH句のインラインPL/SQLファンクション(名称あってるのかな?)内部でSQL文は実行しない方がいいと思うけど。。。。???)


SCOTT> 
SCOTT> set autot trace exp stat
SCOTT> r
1 with
2 function hoge return varchar2
3 deterministic
4 as
5 begin
6 for rec in (select empno,ename from emp order by empno) loop
7 dbms_output.put_line('**** '||rec.empno||':'||rec.ename||' ****');
8 end loop;
9 return null;
10 end;
11 select
12 hoge
13 from
14 dual
15 where
16* hoge is not null

**** 7369:SMITH ****
**** 7499:ALLEN ****
**** 7521:WARD ****
**** 7566:JONES ****
**** 7654:MARTIN ****
**** 7698:BLAKE ****
**** 7782:CLARK ****
**** 7839:KING ****
**** 7844:TURNER ****
**** 7900:JAMES ****
**** 7902:FORD ****
**** 7934:MILLER ****

経過: 00:00:00.03

実行計画
----------------------------------------------------------
Plan hash value: 4034615273

-----------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
|* 1 | FILTER | | | | |
| 2 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("HOGE"() IS NOT NULL)


統計
----------------------------------------------------------
1 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
363 bytes sent via SQL*Net to client
541 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed




WITH句のインラインPL/SQLファンクション内でのSQL文の実行はダークサイドな香りしかしないので、使うならデータの加工等、計算中心目的かねぇ〜。とマニュアル読んでたら見つけたw
 
(マニュアルのサンプルも加工のみ! )

SCOTT> r
1 with
2 function hoge(eno number) return varchar2
3 deterministic
4 as
5 begin
6 dbms_output.put_line('Called');
7 return '**** '||eno||' ****';
8 end;
9 select
10 hoge(empno)
11 from
12* emp

HOGE(EMPNO)
---------------------------------------------------------------------
**** 7369 ****
**** 7499 ****
**** 7521 ****
**** 7566 ****
**** 7654 ****
**** 7698 ****
**** 7782 ****
**** 7839 ****
**** 7844 ****
**** 7900 ****
**** 7902 ****
**** 7934 ****
Called
Called
Called
Called
Called
Called
Called
Called
Called
Called
Called
Called
経過: 00:00:00.02

Database SQL Language Reference (12.1)
Database SQL Language Reference (12.1) - SELECT


みなさん、ダークサイドに落ちないように注意しましょうね! :)

| | | コメント (0) | トラックバック (0)

2012年1月30日 (月)

AWRレポート、AWR SQLレポート一括取得スクリプトを作ったよ。

Statspackレポートもそうなのですが、AWRレポート/AWR SQLレポートも個別に取得していると凄ーく辛いんですよね。一日分出力するとか、AWRレポートで処理時間の長いSQL文のAWR SQLレポートを個別に取得しようとなると...

ただでさえ忙しいのに、AWRレポート取得するのに時間掛けたくないですよね。

ということで、やっつけで作ったのですが、そのまま載せるのもあれなんで、やっつけで作った感じを多少減らしてgithubに公開しました。;)

https://github.com/discus/Oracle-AWR-batch-generation-script/blob/master/awrreport_batch.sql


もっといい感じに改造してくれるといいな〜とかとか... :)

Oracle11g R1/R2 Enterprise Edition、HTML形式で出力します。(RAC環境では試してないので多分だめかと。)。
AWRを利用するには追加ライセンスが必要なのでご注意を


使い方は・・

SQL*plusを起動し、select any dictionaryシステム権限、dbms_repositoryパッケージの実行権限が付与されたユーザで接続して実行するだけ。
SYSTEMユーザでやる事が多いけど、所変わればなんとやらなので・・・そこんとこよろしく。(w


一括取得なので実行当日を含めてn日分のAWRレポートを取得し、同時に処理時間の長いTop20のAWR SQLレポートも取得します。
レポートは各スナップショット間(今のところ固定)で取得します。

指定するパラメータは、以下の3つ。

Enter snap_id for starting AWR report generation. [NULL] : 
AWRレポートを取得する最初のSNAP_IDを指定します。 NULLがデフォルトでほとんどの場合デフォルトのままで事足りると思います。


Enter number of days for reporting period. [1] : 

一括取得する日数を指定します。当日を含みます。
当日分のAWRレポートを出力するのであれば、デフォルト値の1のままでOKです。


Enter suffix for AWR reports filename. [NULL] : test

保存するAWRレポートのファイル名に付加するsuffixを指定します。
試験名とか設定するといいですよね。

"test"と指定した場合

awrrpt_nnnn_nnnn_test.htmlや
awrsqrpt_nnnn_nnnn_test_sqlid.html

の形式で保存します。(nnnnはSNAP_ID)


実行例1)当日分の全レポートを取得する例

SYSTEM> 
SYSTEM> !ls -l awr*.sql
-rw-r--r-- 1 oracle oinstall 6065 1月 29 18:23 awrreport_batch.sql

SYSTEM> @awrreport_batch
--
-- Oracle AWR and AWR SQL report batch generation script
--
-- ***** This script always generate html format AWR reports. *****
--
Enter snap_id for starting AWR report generation. [NULL] :
Enter number of days for reporting period. [1] :
Enter suffix for AWR reports filename. [NULL] : test
--
--
--
clear break compute;
repfooter off;
ttitle off;
btitle off;

・・・中略・・・

<p />
<br /><a class="awr" href="#top">Back to Top</a><p />
</body></html>

SYSTEM>


実行例2)当日分かつsnap_id=291以降で一括取得。(事前にsnap_idを調べておいてね)

SYSTEM> 
SYSTEM> @awrreport_batch
--
-- Oracle AWR and AWR SQL report batch generation script
--
-- ***** This script always generate html format AWR reports. *****
--
Enter snap_id for starting AWR report generation. [NULL] : 291
Enter number of days for reporting period. [1] :
Enter suffix for AWR reports filename. [NULL] : test
--
--
--
clear break compute;
repfooter off;
ttitle off;
btitle off;

・・・中略・・・

<p />
<br /><a class="awr" href="#top">Back to Top</a><p />
</body></html>

SYSTEM>
SYSTEM>
SYSTEM> !ls -l *.html
-rw-r--r-- 1 oracle oinstall 379083 1月 29 22:23 awrrpt_291_292_test.html
-rw-r--r-- 1 oracle oinstall 11899 1月 29 22:23 awrsqrpt_291_292_test_0c83z9rqx45hu.html
-rw-r--r-- 1 oracle oinstall 11899 1月 29 22:23 awrsqrpt_291_292_test_0h3mfbzk6uyw0.html
-rw-r--r-- 1 oracle oinstall 11897 1月 29 22:23 awrsqrpt_291_292_test_2p7t0mw7zvu5z.html

・・・中略・・・

-rw-r--r-- 1 oracle oinstall 11899 1月 29 22:23 awrsqrpt_291_292_test_bhtycgwkxhfj9.html
-rw-r--r-- 1 oracle oinstall 11900 1月 29 22:23 awrsqrpt_291_292_test_bpaggvtfkar9k.html
-rw-r--r-- 1 oracle oinstall 11899 1月 29 22:23 awrsqrpt_291_292_test_c50hdbyuwhfn6.html
-rw-r--r-- 1 oracle oinstall 11892 1月 29 22:23 awrsqrpt_291_292_test_g3f3cw3zy5aat.html

SYSTEM>

なお、Oracleインスタンスが再起動された期間でawrrpt.sqlなどを実行すると、レポートが作成できずエラーでSQL*Plusも終了してしまいますが、本スクリプトでは該当部分のレポートはスキップするようにしてあります。:)

Instance     DB Name      Snap Id   Snap Started       Level
------------ ------------ --------- ------------------ -----
lampeye LAMPEYE 274 29 1月 2012 09:33 1
275 29 1月 2012 10:00 1
276 29 1月 2012 10:30 1
277 29 1月 2012 11:00 1
278 29 1月 2012 11:30 1
279 29 1月 2012 12:00 1
280 29 1月 2012 12:30 1
281 29 1月 2012 13:00 1
282 29 1月 2012 13:30 1
283 29 1月 2012 14:00 1
284 29 1月 2012 14:30 1
285 29 1月 2012 15:00 1
286 29 1月 2012 15:30 1
287 29 1月 2012 16:00 1
288 29 1月 2012 16:30 1
289 29 1月 2012 17:00 1
290 29 1月 2012 17:30 1

291 29 1月 2012 18:31 1 ←再起動されてる
292 29 1月 2012 18:34 1

293 29 1月 2012 22:26 1 ←再起動されてる


Enjoy!

| | | コメント (2) | トラックバック (0)

2011年12月14日 (水)

PL/SQL de Conditional Compile #6

随分前にPL/SQL de Conditional Compile #5ってエントリ書いてたのを思い出して、11g R1/R2用のを見てみたらソースに丁寧なコメント(マニュアルよりいいんじゃね?w )が書かれていてうれしくなった。

オラクルさんとして見せたくない部分は見えないようになっているけど、見せても問題ないところはコードが見える(all_sourceビュー)わけで、それはそれでうれしいわけです。はい。マニュアルが意外に不親切だったりするので。

(マニュアル、良くなりましたよ。昔より。 > 褒めておかないとね、だれとなくw。)...文字サイズも多くしておきましたw

● Oracle11g R1 11.1 の DBMS_DB_VERSION

package dbms_db_version is
version constant pls_integer := 11; -- RDBMS version number
release constant pls_integer := 1; -- RDBMS release number

/* The following boolean constants follow a naming convention. Each
constant gives a name for a boolean expression. For example,
ver_le_9_1 represents version <= 9 and release <= 1
ver_le_10_2 represents version <= 10 and release <= 2
ver_le_10 represents version <= 10

A typical usage of these boolean constants is

$if dbms_db_version.ver_le_10 $then
version 10 and ealier code
$elsif dbms_db_version.ver_le_11 $then
version 11 code
$else
version 12 and later code
$end

This code structure will protect any reference to the code
for version 12. It also prevents the controlling package
constant dbms_db_version.ver_le_11 from being referenced
when the program is compiled under version 10. A similar
observation applies to version 11. This scheme works even
though the static constant ver_le_11 is not defined in
version 10 database because conditional compilation protects
the $elsif from evaluation if the dbms_db_version.ver_le_10 is
TRUE.
*/

ver_le_9_1 constant boolean := FALSE;
ver_le_9_2 constant boolean := FALSE;
ver_le_9 constant boolean := FALSE;
ver_le_10_1 constant boolean := FALSE;
ver_le_10_2 constant boolean := FALSE;
ver_le_10 constant boolean := FALSE;
ver_le_11_1 constant boolean := TRUE;
ver_le_11 constant boolean := TRUE;

end dbms_db_version;


● Oracle11g R2 11.2 の DBMS_DB_VERSION

package dbms_db_version is
version constant pls_integer := 11; -- RDBMS version number
release constant pls_integer := 2; -- RDBMS release number

/* The following boolean constants follow a naming convention. Each
constant gives a name for a boolean expression. For example,
ver_le_9_1 represents version <= 9 and release <= 1
ver_le_10_2 represents version <= 10 and release <= 2
ver_le_10 represents version <= 10

A typical usage of these boolean constants is

$if dbms_db_version.ver_le_10 $then
version 10 and ealier code
$elsif dbms_db_version.ver_le_11 $then
version 11 code
$else
version 12 and later code
$end

This code structure will protect any reference to the code
for version 12. It also prevents the controlling package
constant dbms_db_version.ver_le_11 from being referenced
when the program is compiled under version 10. A similar
observation applies to version 11. This scheme works even
though the static constant ver_le_11 is not defined in
version 10 database because conditional compilation protects
the $elsif from evaluation if the dbms_db_version.ver_le_10 is
TRUE.
*/

ver_le_9_1 constant boolean := FALSE;
ver_le_9_2 constant boolean := FALSE;
ver_le_9 constant boolean := FALSE;
ver_le_10_1 constant boolean := FALSE;
ver_le_10_2 constant boolean := FALSE;
ver_le_10 constant boolean := FALSE;
ver_le_11_1 constant boolean := FALSE;
ver_le_11_2 constant boolean := TRUE;
ver_le_11 constant boolean := TRUE;

end dbms_db_version;

version 12 and later codeなんて箇所、いいですね〜w



PL/SQL de Conditional Compile #1
PL/SQL de Conditional Compile #2
PL/SQL de Conditional Compile #3
PL/SQL de Conditional Compile #4
PL/SQL de Conditional Compile #5

| | | コメント (0) | トラックバック (0)

2010年3月24日 (水)

PL/SQLでCASE式を使わずに条件分岐 #3

PL/SQLでCASE式を使わずに条件分岐の3回目です。

前回作成したプロシージャをパッケージ化し、コマンドを外部から与えられるようにしてみます。

前回作成したsimpleCommandプロシージャを削除してパッケージ化します。store()プロシージャでコマンドを登録、execute()プロシージャで実行です。

SCOTT> drop procedure simpleCommand;

プロシージャが削除されました。

SCOTT>
SCOTT> l
1 CREATE OR REPLACE PACKAGE simpleCommand
2 AS
3 PROCEDURE store(
4 commandName IN VARCHAR2
5 ,commandSource IN command
6 );
7 --
8 PROCEDURE execute(
9 commandName IN VARCHAR2
10 );
11* END;
SCOTT> /

パッケージが作成されました。

SCOTT>
SCOTT> l
1 CREATE OR REPLACE PACKAGE BODY simpleCommand
2 AS
3 TYPE command_type IS TABLE OF command INDEX BY VARCHAR2(4000);
4 commands command_type;
5 --
6 PROCEDURE store(
7 commandName IN VARCHAR2
8 ,commandSource IN command
9 )
10 IS
11 BEGIN
12 commands(commandName) := commandSource;
13 END;
14 --
15 PROCEDURE execute(
16 commandName IN VARCHAR2
17 )
18 IS
19 BEGIN
20 commands(commandName).execute();
21 EXCEPTION
22 WHEN NO_DATA_FOUND THEN
23 DBMS_OUTPUT.PUT_LINE('そんなコマンド知りません! >< ');
24 END;
25* END;
SCOTT> /

パッケージ本体が作成されました。

SCOTT>

では早速試してみます。まず、コマンドを登録!

SCOTT> l
1 BEGIN
2 simpleCommand.store(
3 'いま何時?',
4 Command(
5 'BEGIN '||
6 'DBMS_OUTPUT.PUT_LINE(''現在時刻:''||TO_CHAR(systimestamp, ''yyyy-mm-dd hh24:mi:ss''));'||
7 'END;'
8 )
9 );
10 simpleCommand.store(
11 '従業員数は?',
12 Command(
13 'DECLARE '||
14 'numberOfEmployees NUMBER;'||
15 'BEGIN '||
16 'SELECT COUNT(*) INTO numberOFEmployees FROM emp;'||
17 'DBMS_OUTPUT.PUT_LINE(''従業員数:''||TO_CHAR(numberOfEmployees));'||
18 'END;'
19 )
20 );
21 simpleCommand.store(
22 '従業員リスト',
23 Command(
24 'DECLARE '||
25 'CURSOR empList IS '||
26 'SELECT '||
27 'empno '||
28 ',ename '||
29 ',dname '||
30 'FROM '||
31 'emp JOIN dept '||
32 'ON emp.deptno = dept.deptno '||
33 'ORDER BY '||
34 'dname, empno, ename;'||
35 'BEGIN '||
36 'FOR empRec IN empList LOOP '||
37 'DBMS_OUTPUT.PUT_LINE(empRec.empno || '' '' || empRec.ename || '' '' || empRec.dname);'||
38 'END LOOP; '||
39 'END;'
40 )
41 );
42* END;
SCOTT> /

PL/SQLプロシージャが正常に完了しました。

SCOTT>


実行!.これで適用範囲を広げらるかも・・・w

SCOTT> set serveroutput on size 100000
SCOTT>
SCOTT> exec simpleCommand.execute('従業員リスト');
7782 CLARK ACCOUNTING
7839 KING ACCOUNTING
7934 MILLER ACCOUNTING
7369 SMITH RESEARCH
7566 JONES RESEARCH
7788 SCOTT RESEARCH
7876 ADAMS RESEARCH
7902 FORD RESEARCH
7499 ALLEN SALES
7521 WARD SALES
7654 MARTIN SALES
7698 BLAKE SALES
7844 TURNER SALES
7900 JAMES SALES

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand.execute('いま何時?');
現在時刻:2010-03-20 23:53:22

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand.execute('従業員数は?');
従業員数:14

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand.execute('ほげほげ');
そんなコマンド知りません! ><

PL/SQLプロシージャが正常に完了しました。

SCOTT>




・バックナンバー
PL/SQLでCASE式を使わずに条件分岐 #1
PL/SQLでCASE式を使わずに条件分岐 #2

| | | コメント (0) | トラックバック (0)

2010年3月23日 (火)

PL/SQLでCASE式を使わずに条件分岐 #2

PL/SQLでCASE式を使わずに条件分岐の2回目です。

前回作成したプロシージャから、とにかくCASE式を無くてみましょ。

まず、以下の様なオブジェクト型を定義して・・・・・

SCOTT> l
1 CREATE OR REPLACE TYPE command AS OBJECT
2 (
3 commandStr CLOB,
4 MEMBER PROCEDURE execute
5* );
SCOTT> /

型が作成されました。

SCOTT>
SCOTT> l
1 CREATE OR REPLACE TYPE BODY command
2 AS
3 MEMBER PROCEDURE execute
4 IS
5 BEGIN
6 EXECUTE IMMEDIATE commandStr;
7 END;
8* END;
SCOTT> /

型本体が作成されました。

SCOTT>

PL/SQLの結合配列のINDEXにVARCHAR2型を利用すればハッシュテーブルと同じように使えることを利用して実行するコマンドはCLOBに格納してEXECUTE IMMEDIATE文で実行しちゃうという方法です。実行するコマンドは外部から与えるようにすることもできますが、とりあえず内部に直書き。

SCOTT> l
1 CREATE OR REPLACE PROCEDURE simpleCommand
2 (
3 commandName IN VARCHAR2
4 )
5 IS
6 TYPE command_type IS TABLE OF command INDEX BY VARCHAR2(4000);
7 commands command_type;
8 --
9 PROCEDURE init IS
10 BEGIN
11 commands('いま何時?')
12 := Command(
13 'BEGIN '||
14 'DBMS_OUTPUT.PUT_LINE(''現在時刻:''||TO_CHAR(systimestamp, ''yyyy-mm-dd hh24:mi:ss''));'||
15 'END;'
16 );
17 commands('従業員数は?')
18 := Command(
19 'DECLARE '||
20 'numberOfEmployees NUMBER;'||
21 'BEGIN '||
22 'SELECT COUNT(*) INTO numberOFEmployees FROM emp;'||
23 'DBMS_OUTPUT.PUT_LINE(''従業員数:''||TO_CHAR(numberOfEmployees));'||
24 'END;'
25 );
26 commands('従業員リスト')
27 := Command(
28 'DECLARE '||
29 'CURSOR empList IS '||
30 'SELECT '||
31 'empno '||
32 ',ename '||
33 ',dname '||
34 'FROM '||
35 'emp JOIN dept '||
36 'ON emp.deptno = dept.deptno '||
37 'ORDER BY '||
38 'dname, empno, ename;'||
39 'BEGIN '||
40 'FOR empRec IN empList LOOP '||
41 'DBMS_OUTPUT.PUT_LINE(empRec.empno || '' '' || empRec.ename || '' '' || empRec.dname);'||
42 'END LOOP; '||
43 'END;'
44 );
45 END;
46 --
47 BEGIN
48 init();
49 --
50 commands(commandName).execute();
51 EXCEPTION
52 WHEN NO_DATA_FOUND THEN
53 DBMS_OUTPUT.PUT_LINE('そんなコマンド知りません! >< ');
54* END;
SCOTT>
SCOTT> /

プロシージャが作成されました。

SCOTT>


できた!!! :)

SCOTT> set serveroutput on size 100000
SCOTT> exec simpleCommand('いま何時?');
現在時刻:2010-03-20 15:50:27

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand('従業員リスト');
7782 CLARK ACCOUNTING
7839 KING ACCOUNTING
7934 MILLER ACCOUNTING
7369 SMITH RESEARCH
7566 JONES RESEARCH
7788 SCOTT RESEARCH
7876 ADAMS RESEARCH
7902 FORD RESEARCH
7499 ALLEN SALES
7521 WARD SALES
7654 MARTIN SALES
7698 BLAKE SALES
7844 TURNER SALES
7900 JAMES SALES

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand('従業員数は?');
従業員数:14

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand('ほげほげ');
そんなコマンド知りません! ><

PL/SQLプロシージャが正常に完了しました。

SCOTT>

次回はコマンドを外部から与えられるように改造してみます。 次回へつづく。



・バックナンバー

PL/SQLでCASE式を使わずに条件分岐 #1

| | | コメント (0) | トラックバック (0)

2010年3月22日 (月)

PL/SQLでCASE式を使わずに条件分岐 #1

随分前ですが、html5-developers-jpのディスカッションでJavaScriptでswitch文を使わずに条件分岐という話題があって、あ、そうそう、PL/SQLでもできるよね、って瞬間的に思っていたのですが、忙しくて、ず〜っと放置してたネタでございます。:)

今回は使うのは、Oracle11g R2 for Linux x86。

SCOTT> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production

まずは、CASE式を使って書けば以下のような感じになるよね! ってところから。

SCOTT> l
1 CREATE OR REPLACE PROCEDURE simpleCommand
2 (
3 commandName IN VARCHAR2
4 )
5 IS
6 CURSOR empList IS
7 SELECT
8 empno
9 ,ename
10 ,dname
11 FROM
12 emp JOIN dept
13 ON emp.deptno = dept.deptno
14 ORDER BY
15 dname, empno, ename
16 ;
17 --
18 numberOfEmployees NUMBER;
19 BEGIN
20 CASE commandName
21 WHEN 'いま何時?' THEN
22 DBMS_OUTPUT.PUT_LINE('現在時刻:'||TO_CHAR(systimestamp, 'yyyy-mm-dd hh24:mi:ss'));
23 WHEN '従業員数は?' THEN
24 SELECT COUNT(*) INTO numberOFEmployees FROM emp;
25 DBMS_OUTPUT.PUT_LINE('従業員数:'||TO_CHAR(numberOfEmployees));
26 WHEN '従業員リスト' THEN
27 FOR empRec IN empList LOOP
28 DBMS_OUTPUT.PUT_LINE(empRec.empno || ' ' || empRec.ename || ' ' || empRec.dname);
29 END LOOP;
30 ELSE
31 DBMS_OUTPUT.PUT_LINE('そんなコマンド知りません! >< ');
32 END CASE;
33 --
34* END;
SCOTT> /

プロシージャが作成されました。

SCOTT>
SCOTT> set serveroutput on size 100000
SCOTT> exec simpleCommand('いま何時?');
現在時刻:2010-03-21 00:10:17

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand('従業員リスト');
7782 CLARK ACCOUNTING
7839 KING ACCOUNTING
7934 MILLER ACCOUNTING
7369 SMITH RESEARCH
7566 JONES RESEARCH
7788 SCOTT RESEARCH
7876 ADAMS RESEARCH
7902 FORD RESEARCH
7499 ALLEN SALES
7521 WARD SALES
7654 MARTIN SALES
7698 BLAKE SALES
7844 TURNER SALES
7900 JAMES SALES

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand('従業員数は?');
従業員数:14

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec simpleCommand('ほげほげ');
そんなコマンド知りません! ><

PL/SQLプロシージャが正常に完了しました。

SCOTT>


上記のようなPL/SQLプロシージャ中のCASE式を排除してコマンドパターンっぽくできるのか。。。方法はいろいろあるな〜と思いつつ、PL/SQLの結合配列を使う方法で試してみた。
ということで、次回へつづく :) …

| | | コメント (0) | トラックバック (0)

2010年2月20日 (土)

UTL_FILE.FCOPY()とかFRENAME()とか

UTL_FILE.FRENAME()プロシージャとか、UTL_FILE.FCOPY()プロシージャって何かと便利っていうお話。

テストでOracleデータベース側に格納されたデータと紐づけながら画像ファイルを複製してみたり、名称を紐づけたりするときにも重宝するプロシージャなんですよね。
特に、shellなどがないWindows環境で画像を大量に複製したりする場合にはかなり便利だなと。

まずは、OS上のディレクトリとの紐付け。ディレクトリオブジェクトを作成しておきます。(今回はSCOTTユーザにディレクトリオブジェクトを作成できるように権限を付与しておきました)

SCOTT> 
SCOTT> create directory ora_dir as '/home/oracle/ora_dir';

ディレクトリが作成されました。

SCOTT>

UTL_FILE.FRENAME()プロシージャは、Linux/Unixのmvコマンドライク。
以下は、リネーム後のファイル名(hogehoge.png)と同じファイルが既に存在する場合には置き換えない場合の例。overwriteパラメータをFALSE(デフォルト)にしてあげる。

SCOTT> !ls -l ora_dir
-rw-r--r-- 1 oracle oinstall 28800 2月 20 09:26 hogehoge.png
-rw-r--r-- 1 oracle oinstall 28800 2月 19 23:57 test.png

SCOTT> l
1 BEGIN
2 UTL_FILE.FRENAME('ORA_DIR','test.png','ORA_DIR','hogehoge.png',FALSE);
3* END;
SCOTT> /
BEGIN
*
行1でエラーが発生しました。:
ORA-29292: ファイル名の変更操作に失敗しました。 ORA-06512:
"SYS.UTL_FILE", 行348
ORA-06512: "SYS.UTL_FILE", 行1290
ORA-06512: 行2

SCOTT> !ls -l ora_dir
-rw-r--r-- 1 oracle oinstall 28800 2月 20 09:26 hogehoge.png
-rw-r--r-- 1 oracle oinstall 28800 2月 19 23:57 test.png


以下は、リネーム後のファイル名(hogehoge.png)と同じファイルが既に存在する場合に置き換えてしまう例。overwriteパラメータをTRUEにしてあげる。

SCOTT> !ls -l ora_dir
-rw-r--r-- 1 oracle oinstall 28800 2月 20 09:26 hogehoge.png
-rw-r--r-- 1 oracle oinstall 28800 2月 19 23:57 test.png

SCOTT> l
1 BEGIN
2 UTL_FILE.FRENAME('ORA_DIR','test.png','ORA_DIR','hogehoge.png',TRUE);
3* END;
SCOTT> /

PL/SQLプロシージャが正常に完了しました。

SCOTT> !ls -l ora_dir
-rw-r--r-- 1 oracle oinstall 28800 2月 19 23:57 hogehoge.png

SCOTT>


せっかくUTL_FILE.FRENAME()プロシージャを試したので、次いでにUTL_FILE.FCOPY()でファイルコピーも!
といきたいところですが、UTL_FILE.FCOPY()プロシージャはテキストファイルのコピーしか行えない!(パラメータからも想像できると思うけど)
では、画像ファイルなどのバイナリファイルはどうすんのさ!オラクルさん!。ってことで、そのようなプロシージャは無いので自前で作りましょ。
以下は、そのサンプル、バイナリモードで画像ファイルを読み込んでバイナリモードで別ファイルへ書き出す。だたそれだけ。

SCOTT> !ls -l ora_dir 
-rw-r--r-- 1 oracle oinstall 28800 2月 19 23:13 test2.png

SCOTT>
SCOTT> l
1 DECLARE
2 src_file UTL_FILE.FILE_TYPE;
3 dst_file UTL_FILE.FILE_TYPE;
4 buffer RAW(32767);
5 BEGIN
6 src_file := UTL_FILE.FOPEN('ORA_DIR','test2.png', 'rb', 32767);
7 dst_file := UTL_FILE.FOPEN('ORA_DIR','hogehogehoge.png', 'wb', 32767);
8 LOOP
9 BEGIN
10 UTL_FILE.GET_RAW(src_file, buffer, 32767);
11 UTL_FILE.PUT_RAW(dst_file, buffer, TRUE);
12 EXCEPTION
13 WHEN NO_DATA_FOUND THEN
14 EXIT;
15 END;
16 END LOOP;
17 UTL_FILE.FCLOSE(src_file);
18 UTL_FILE.FCLOSE(dst_file);
19* END;
SCOTT> /

PL/SQLプロシージャが正常に完了しました。

SCOTT> !ls -l ora_dir
-rw-r--r-- 1 oracle oinstall 28800 2月 19 23:57 hogehogehoge.png
-rw-r--r-- 1 oracle oinstall 28800 2月 19 23:13 test2.png

SCOTT>

| | | コメント (2) | トラックバック (0)

2009年9月 6日 (日)

PL/SQL で Python Challenge Level 18 - 完

ということで、Python Challenge Level18の最終回。
前回報告した通り level 18 はPL/SQLだけで解けます。今回は問題の元ネタの抽出と回答を抽出した後の確認はOSに付属している機能を使ったが大切なのは問題の元ネタから解答を抽出する所なので。

SQL*Plusから実行したストアドプロシージャで問題を解いたところ....例によってヒントになりそうな部分はもやもやさせてます。 :)


Level18_exec

ヒントになりまくりの答えのスナップショット。(これ以外にも私のブログにヒントになる記事もあるのだが・・・・・それは書きませんよ。。。。)

ちなみに絵を見れば分かると思いますが、MacOS XからX11のX forwordingを使って今回利用したLinuxのDesktopを表示/操作しています。X11のxtermからssh -Y user@hostで接続してgnome-sessionを実行しているだけ。


Level18_eureka

Python Challenge Level19へつづく!(?)

| | | コメント (0) | トラックバック (0)

2009年9月 2日 (水)

PL/SQL で Python Challenge Level 18 解けた

一年以上間が空いてしまったが・・・Python Challenge Level18はPL/SQLだけで解けますよん。ヒントになるようなならないようなモヤモヤした記事は明日にでも。。

| | | コメント (2) | トラックバック (0)

2009年8月31日 (月)

PL/SQL de O(ND) Difference Algorithm

かなり久々ですが、まあ、シリーズもののネタを毎日アップするのもなかなか難しい状況なので単発ネタでも。

ということで今回は、PL/SQLでAn O(ND) Difference Algorithmを実装して頭の体操。

文書比較のアルゴリズムとしてはAn O(NP) Sequence Comparison Algorithmが効率は良いのだが、先日javascriptで書かれたO(ND)のコードを見つけたことをキッカケにPL/SQLで写経したくなったというわけ。

文書比較アルゴリズムは2、3年くらい前、文書比較アブゴリズムdiff(1)/diff(2)/diff(3)等をみたことがあったが、最近pyhthonやらjavascriptのコードを目にするようになり (^^) な顔して眺めていて時間があったらPL/SQLで遊んでみようと思っていた。。他の言語でやってもMac De Oracle的にはおもしろくないので。

ちなみに、pythonだどdifflib使えば文書比較はできるので実際新たに書く必要はそんなにないんじゃなかろうかとも思うわけですが、私のようにわざわざPL/SQLで書いてみようと思う人間もいるわけで、頭の体操にはいいと思います!。理解するのは大変だったけど。wwww

最近見つけたO(ND)やO(NP)に関するブログ等のリンクは以下。

レコメンデーションとエディットグラフ
diff O(np) javascript implementation
"An O(NP) Sequence Comparison Algorithm" with Python
"An O(NP) Sequence Comparison Algorithm" with Python の添削
Javascriptでdiffる ( with 形態素解析 )
google-diff-match-patch


今回はレコメンデーションとエディットグラフにあるO(ND)コードを写経してPL/SQLでやってみた。O(NP)も集中できる時間があったらやってみたい。。。脳トレにも丁度いいかもよ。。。
※Oracle11g 11.1.0.7.0を使ったが、Oracle10g 10.1.0.3.0以上なら動作するはず。。。。(^^;;;

まずは、結果からどうぞ。O(ND)をPL/SQLストアドファンクション化し、SQLから実行できるようにしてあります。また、pythonのdifflibにあるdifferクラスで利用されている差異コード('?'を除く)を出力するようにしてあります。)

SCOTT> set timi on
SCOTT> l
1 SELECT
2 '"' || diffs.code || diffs.string || '"' AS "diff O(ND) results"
3 FROM
4 TABLE(
5 SELECT diffOND('BFEABD', 'ABCDA') from dual
6 ) diffs
7 ORDER BY
8* diffs.seq DESC
SCOTT> /

diff O(ND) results
--------------------------------------------------------------------------------
"- B"
"- F"
"- E"
" A"
" B"
"+ C"
" D"
"+ A"

8行が選択されました。

経過: 00:00:00.00

SCOTT>
SCOTT> l
1 SELECT
2 '"' || diffs.code || diffs.string || '"' AS "diff O(ND) results"
3 FROM
4 TABLE(
5 SELECT diffOND('aaebdd ', 'aedajkd') from dual
6 ) diffs
7 ORDER BY
8* diffs.seq DESC
SCOTT> /

diff O(ND) results
--------------------------------------------------------------------------------
" a"
"- a"
" e"
"- b"
" d"
"+ a"
"+ j"
"+ k"
" d"
"- "

10行が選択されました。

経過: 00:00:00.00

SCOTT>
SCOTT> l
1 SELECT
2 '"' || diffs.code || diffs.string || '"' AS "diff O(ND) results"
3 FROM
4 TABLE(
5 SELECT diffOND('ABCDE', 'ABCDE') from dual
6 ) diffs
7 ORDER BY
8* diffs.seq DESC
SCOTT> /

diff O(ND) results
--------------------------------------------------------------------------------
" A"
" B"
" C"
" D"
" E"

経過: 00:00:00.00

SCOTT>
SCOTT> l
1 SELECT
2 '"' || diffs.code || diffs.string || '"' AS "diff O(ND) results"
3 FROM
4 TABLE(
5 SELECT diffOND('あいうえお','かきくけこ') from dual
6 ) diffs
7 ORDER BY
8* diffs.seq DESC
SCOTT> /

diff O(ND) results
--------------------------------------------------------------------------------
"- あ"
"- い"
"- う"
"- え"
"- お"
"+ か"
"+ き"
"+ く"
"+ け"
"+ こ"

10行が選択されました。

経過: 00:00:00.00

SCOTT>

PL/SQLのコードは以下の通り。

SCOTT> !cat ond.sql

CREATE OR REPLACE TYPE vRecType AS OBJECT
(
x NUMBER
,y NUMBER
,parent ANYDATA
);
/
show errors

BEGIN
FOR functionNames
IN (SELECT object_name FROM USER_OBJECTS WHERE object_name='DIFFOND' AND OBJECT_TYPE='FUNCTION')
LOOP
EXECUTE IMMEDIATE 'DROP FUNCTION ' || functionNames.object_name;
END LOOP;

FOR typeNames
IN (SELECT type_name FROM USER_TYPES WHERE type_name='DIFFLISTTYPE')
LOOP
EXECUTE IMMEDIATE 'DROP TYPE ' || typeNames.type_name;
END LOOP;
END;
/

CREATE OR REPLACE TYPE diffType AS OBJECT
(
seq NUMBER
,code CHAR(2)
,string VARCHAR2(32767)
);
/
show errors

CREATE OR REPLACE TYPE diffListType AS TABLE OF diffType;
/
show errors

CREATE FUNCTION diffOND
(
str1 IN VARCHAR2
,str2 IN VARCHAR2
)
RETURN diffListType
IS
STAT_INIT CONSTANT PLS_INTEGER := 0;
STAT_X CONSTANT PLS_INTEGER := 1;
STAT_Y CONSTANT PLS_INTEGER := 2;

TYPE vType IS TABLE OF vRecType;
v vType := vType();

FUNCTION isVRecNotEmpty(
vRec IN vRecType
)
RETURN BOOLEAN
IS
BEGIN
RETURN (CASE WHEN vRec.x IS NULL AND vRec.y IS NULL AND vRec.parent IS NULL THEN FALSE ELSE TRUE END);
END isVRecNotEmpty;

FUNCTION getDirection
(
vMinus IN vRecType
,vPlus IN vRecType
)
RETURN PLS_INTEGER
IS
BEGIN
IF NOT isVRecNotEmpty(vMinus) AND NOT isVRecNotEmpty(vPlus) THEN
RETURN STAT_INIT;
END IF;

IF NOT isVRecNotEmpty(vMinus) THEN
RETURN STAT_X;
END IF;

IF NOT isVRecNotEmpty(vPlus) THEN
RETURN STAT_Y;
END IF;

RETURN (CASE WHEN vMinus.x < vPlus.x THEN STAT_X ELSE STAT_Y END);
END getDirection;

FUNCTION OND
(
str1 IN VARCHAR2
,str2 IN VARCHAR2
)
RETURN vRecType
IS
offset PLS_INTEGER;
kMax PLS_INTEGER;
kMin PLS_INTEGER;
k PLS_INTEGER;
vIndex PLS_INTEGER;
x PLS_INTEGER;
y PLS_INTEGER;
str1Len PLS_INTEGER;
str2Len PLS_INTEGER;
parent vRecType;
BEGIN
str1Len := LENGTH(str1);
str2Len := LENGTH(str2);
v.EXTEND(str1Len + str2Len + 3);
offset:= str2Len + 2;

FOR d IN 0..str1Len + str2Len LOOP
kMax := (CASE WHEN d <= str1Len THEN d ELSE str1Len - (d - str1Len) END);
kMin := (CASE WHEN d <= str2Len THEN d ELSE str2Len - (d - str2Len) END);

k := kMin * -1;
WHILE k <= kMax LOOP
vIndex := offset + k;
CASE getDirection(v(vIndex-1), v(vIndex+1))
WHEN STAT_INIT THEN
x := 0;
y := 0;
parent := vRecType(0, 0, NULL);
WHEN STAT_X THEN
x := v(vIndex+1).x;
y := v(vIndex+1).y + 1;
parent := v(vIndex+1);
WHEN STAT_Y THEN
x := v(vIndex-1).x + 1;
y := v(vIndex-1).y;
parent := v(vIndex-1);
END CASE;

-- snake
WHILE (x < str1Len AND y < str2Len)
AND (SUBSTR(str1, x+1, 1) = SUBSTR(str2, y+1, 1))
LOOP
x := x + 1;
y := y + 1;
END LOOP;
v(vIndex) := vRecType(x, y, ANYDATA.ConvertObject(parent));

IF str1Len <= x AND str2Len <= y THEN
RETURN v(vIndex);
END IF;

k := k + 2;
END LOOP;
END LOOP;
END OND;

FUNCTION diff
(
str1 IN VARCHAR2
,str2 IN VARCHAR2
)
RETURN diffListType
IS
endPoint vRecType;
parent vRecType;
diff_x PLS_INTEGER;
diff_y PLS_INTEGER;
same_len PLS_INTEGER;
isSuccessGetObject PLS_INTEGER;
diffs diffListType := diffListType();
seq PLS_INTEGER := 0;
BEGIN
endPoint := OND(str1, str2);
WHILE endPoint.parent IS NOT NULL LOOP
IF ANYDATA.getObject(endPoint.parent, parent) != DBMS_TYPES.SUCCESS THEN
RAISE_APPLICATION_ERROR(-20000,'DBMS_TYPES.NO_DATA');
END IF;

diff_x := endPoint.x - parent.x;
diff_y := endPoint.y - parent.y;
same_len := CASE WHEN diff_x <= diff_y THEN diff_x ELSE diff_y END;

FOR i IN 0..same_len-1 LOOP
-- common
diffs.EXTEND();
seq := seq + 1;
diffs(diffs.COUNT()) := diffType(seq, ' ', SUBSTR(str1, endPoint.x-i, 1));
END LOOP;

IF diff_y != diff_x THEN
diffs.EXTEND();
seq := seq + 1;
IF diff_y < diff_x THEN
-- del
diffs(diffs.COUNT()) := diffType(seq, '- ', SUBSTR(str1, parent.x+1, 1));
ELSE
-- add
diffs(diffs.COUNT()) := diffType(seq, '+ ', SUBSTR(str2, parent.y+1, 1));
END IF;
END IF;

endPoint := parent;
END LOOP;

RETURN diffs;
END diff;
BEGIN
RETURN diff(str1, str2);
END diffOND;
/
show errors

Enjoy PL/SQL! というより Enjoy Programming! のほうがいいか。。

| | | コメント (0) | トラックバック (0)

2009年6月 8日 (月)

OraTweet 1.0 あそび始めた。

OraTweet v1.0が公開されたということなので早速試してみた。といってもちょっとだけ。細かいところはみてないけどとりあえずは日本語も使えるみたい。ちょうどPL/SQL gatewayからTwitter APIを使って遊んでみようと思っていたところだから少し遊ぶか。
Oratweet01_2

Oratweet02

| | | コメント (0) | トラックバック (0)

2009年6月 5日 (金)

Leopard de Oracle10g R2 (Intel x86-64) #18 (APEX3.2と遊ぶ #11)

Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第十話。

Oracle HTTP ServerとPL/SQL gatewayをORACLE_HOMEごとPowerMacからIntel Mac移動して、Intel Macへインストール、構築したOracle 10g R2 10.2.0.4.0 for MacOSX(Intel x86-64) + APEX3.2へアクセスしちゃいます。RosettaがPowerPC版のOracle HTTP Server mod_plsqlをIntel Mac上で動かしてくれます。


まず、MacBook Proのオラクル所有者の環境変数の設定などはLeopard de Oracle10g R2 (Intel x86-64) #2の設定に加え、Oracle HTTP Server向けaliasと環境変数を追加。
ちなみに、/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apexをIntel Mac上でのAPExのHOMEディレクトリとしてOracle HTTP Serverを起動、停止スクリプトをaliasとして定義。(Oracle 10g R1 10.1.0.3.0 for MacOSXのCompanionCDでインストールされるOracle HTTP Serverは ORACLE_HOME(Oracle HTTP Serverの)/opmn/bin/opmnctl stopall 又は startallではなくORACLE_HOME(Oracle HTTP Serverの)/Apache/Apache/bin/apachectl start又はstopで起動/停止できます。)

guppy:˜ oracle$ cat .bashrc
alias ll='ls -lv'
alias startohs='$ORACLE_HTMLDB_HOME/bin/apachectl start'
alias stopohs='$ORACLE_HTMLDB_HOME/bin/apachectl stop'

export LANG=ja_JP.UTF-8
export ORACLE_BASE=/Users/shared/u01/app/oracle
export ORACLE_SID=leopard1
export ORACLE_HOME=$ORACLE_BASE/oracle/product/10.2.0/db_1
export NLS_LANG=Japanese_Japan.AL32UTF8
export DYLD_LIBRARY_PATH=$ORACLE_HOME/lib:$ORACLE_HOME/rdbms/lib:$ORACLE_HOME/network/lib:$DYLD_LIBRARY_PATH
export PATH=$ORACLE_HOME/bin:$PATH

#for Oracle11g and Jdeveloper Oracle Middleware HOME
export ORACLE_MIDDLEWARE_HOME=/Users/Shared/Oracle/Middleware

#for Oracle10g R1 Oracle HTTP ServerHome (include mod_plsql)
export ORACLE_HTMLDB_HOME=/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache
guppy:˜ oracle$

次に、PowerPC上でzipしたORACLE HTTP ServerのORACLE_HOMEディレクトリ以下を丸ごとIntel Mac上のOracle HTTP ServerのあたらしいHOMEディレクトリへ移動します。移動方法はrcpでもいいし、環境によってはscpでもいいしUSBメモリ経由でもmobileme経由でもお好きな方法でIntel Macへコピー/解凍します。PowerPC上のORACLE HTTP Serverホームディレクトリ以下は不要なのでHOMEディレクトリごと削除するもよし、ちゃんとOUIで削除してもいいですよん。
以下、MacBook Pro上で解凍した状態。MacBook Pro上のあたらしいHOMEディレクトリは、/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex



1

APEX3.2のimagesディレクトリのコピー。
APEX3.2のimagesディレクトリでOracle HTTP Serverのホームディレクトリ/marvel以下にあるimagesディレクトリを置き換えます。
この作業はAPEXのアップデート作業でもよく作業なのでお忘れなく。HTML DB1.5をAPEX3.2にアップデートしたようなものなので。

guppy:apex oracle$ pwd
/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/marvel
guppy:marvel oracle$ ls
10ins.sql core doc ins.sql jlib readme.html upgrade.sql
builder coreins.sql images install.lst logs sbs utilities
guppy:marvel oracle$ mv images images_htmldb_1_5
guppy:marvel oracle$ cp -rf /Users/oracle/Downloads/apex/images .
guppy:marvel oracle$


.confファイルの変更
ここからがメイン。PowerPC上でインストール構築したので各種.confファイルなどのパスはホスト名がPowerPC上の値に設定されています。それら全てを移動してきたMacBook Pro(Intel x86)上の値に変更していきます。
最初はOracle HTTP Server向けコンフィグファイルから。(全てのconfファイルを確認し要変更と確定できた変更前の*.confファイルは、*.conf.15として退避しておくことにします。)

/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apexは、MacBook Pro上でのOracle HTTP Serverのホームディレクトリ。


guppy:conf oracle$ pwd

/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf
guppy:conf oracle$
guppy:conf oracle$ ll
total 680
-rwxr-xr-x 1 oracle oinstall 348 11 16 2004 access.conf
-rwxr-xr-x 1 oracle oinstall 348 11 16 2004 access.conf.default
-rw------- 1 oracle oinstall 1623 5 18 14:16 dms.conf
-rw------- 1 oracle oinstall 1633 5 18 14:14 dms.conf.15
-rw------- 1 oracle oinstall 1626 7 2 2004 dms.conf.sbs
-rwxr-xr-x 1 oracle oinstall 41721 5 18 14:41 httpd.conf
-rwxr-xr-x 1 oracle oinstall 42011 5 18 14:16 httpd.conf.15
-rwxr-xr-x 1 oracle oinstall 42011 5 17 12:23 httpd.conf.default
-rwxr-xr-x 1 oracle oinstall 39710 12 17 2004 httpd.conf.tmp
-rwxr-xr-x 1 oracle oinstall 12965 11 16 2004 magic
-rwxr-xr-x 1 oracle oinstall 12965 11 16 2004 magic.default
-rwxr-xr-x 1 oracle oinstall 15161 11 16 2004 mime.types
-rwxr-xr-x 1 oracle oinstall 15161 11 16 2004 mime.types.default
-rw-r--r-- 1 oracle oinstall 484 5 18 14:42 mod_oc4j.conf
-rw-r--r-- 1 oracle oinstall 490 5 18 14:41 mod_oc4j.conf.15
-rw-r--r-- 1 oracle oinstall 490 5 17 12:25 mod_oc4j.conf.default
-rw-r--r-- 1 oracle oinstall 503 7 2 2004 mod_oc4j.conf.tmp
-rwxr-xr-x 1 oracle oinstall 779 5 17 12:23 mod_osso.conf
-rwxr-xr-x 1 oracle oinstall 779 12 17 2004 mod_osso.conf.tmp
-rwxr-xr-x 1 oracle oinstall 323 5 17 12:23 ohs_opmn.xml
-rwxr-xr-x 1 oracle oinstall 323 12 17 2004 ohs_opmn.xml.sbs
-rwxr-xr-x 1 oracle oinstall 566 5 18 14:44 ohstarget.xml
-rwxr-xr-x 1 oracle oinstall 575 5 18 14:44 ohstarget.xml.15
-rwxr-xr-x 1 oracle oinstall 585 12 17 2004 ohstarget.xml.tmp
-rwxr-xr-x 1 oracle oinstall 197 5 18 14:45 oracle_apache.conf
-rwxr-xr-x 1 oracle oinstall 217 5 18 14:45 oracle_apache.conf.15
drwxr-xr-x 2 oracle oinstall 68 5 17 12:23 osso
-rwxr-xr-x 1 oracle oinstall 357 11 16 2004 srm.conf
-rwxr-xr-x 1 oracle oinstall 357 11 16 2004 srm.conf.default
-rwxr-xr-x 1 oracle oinstall 7861 5 18 14:51 ssl.conf
-rwxr-xr-x 1 oracle oinstall 8016 5 18 14:46 ssl.conf.15
-rwxr-xr-x 1 oracle oinstall 7192 12 17 2004 ssl.conf.tmp
drwxr-xr-x 4 oracle oinstall 136 5 17 12:23 ssl.crl
drwxr-xr-x 3 oracle oinstall 102 5 17 12:23 ssl.wlt
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ ls *.*.15
dms.conf.15 httpd.conf.15 mod_oc4j.conf.15
ohstarget.xml.15 oracle_apache.conf.15 ssl.conf.15

guppy:conf oracle$
guppy:conf oracle$

以下、変更したconfファイルのdiff。


PowerPC上のORACLE_HOMEのパスとIntel Mac上のパスが異なるのでパス関連及び、ホスト名関連は変更対象。


例えば、PowerMac G5上でOracle HTTP Server用のORACLE_HOMEは

/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex


だったが、Intel Mac上では
/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex
とした。

guppy:conf oracle$
guppy:conf oracle$ diff dms.conf.15 dms.conf
30c30
< Alias /index.html /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/htdocs/index.html
---
> Alias /index.html /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/htdocs/index.html
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ diff mod_oc4j.conf.15 mod_oc4j.conf
15c15
< Allow from localhost g5server g5server
---
> Allow from localhost guppy guppy
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ diff ohstarget.xml.15 ohstarget.xml
5c5
< <Property NAME="OracleHome" VALUE="/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex"/>
---
> <Property NAME="OracleHome" VALUE="/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex" />
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ diff httpd.conf.15 httpd.conf
65c65
< ServerRoot "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache"
---
> ServerRoot "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache"
76c76
< #LockFile /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/httpd.lock
---
> #LockFile /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/httpd.lock
82c82
< PidFile /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/httpd.pid
---
> PidFile /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/httpd.pid
90c90
< ScoreBoardFile /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/httpd.scoreboard
---
> ScoreBoardFile /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/httpd.scoreboard
321c321
< ServerName g5server
---
> ServerName guppy
328c328
< DocumentRoot "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/htdocs"
---
> DocumentRoot "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/htdocs"
353c353
< <Directory "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/htdocs">
---
> <Directory "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/htdocs">
456c456
< TypesConfig /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/mime.types
---
> TypesConfig /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/mime.types
482c482
< MIMEMagicFile /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/magic
---
> MIMEMagicFile /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/magic
502c502
< ErrorLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/error_log
---
> ErrorLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/error_log
547c547
< CustomLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/access_log common
---
> CustomLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/access_log common
553,554c553,554
< #CustomLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/referer_log referer
< #CustomLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/agent_log agent
---
> #CustomLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/referer_log referer
> #CustomLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/agent_log agent
560c560
< #CustomLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/access_log combined
---
> #CustomLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/access_log combined
582,584c582,584
< Alias /icons/ "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/icons/"
< Alias /jservdocs/ "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Jserv/docs/"
< Alias /javacachedocs/ "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/javacache/javadoc/"
---
> Alias /icons/ "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/icons/"
> Alias /jservdocs/ "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Jserv/docs/"
> Alias /javacachedocs/ "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/javacache/javadoc/"
586c586
< Alias /perl/ "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin/"
---
> Alias /perl/ "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin/"
589c589
< <Directory "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/icons">
---
> <Directory "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/icons">
604c604
< ScriptAlias /cgi-bin/ "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin/"
---
> ScriptAlias /cgi-bin/ "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin/"
608c608
< # "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin" should be changed to whatever your ScriptAliased
---
> # "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin" should be changed to whatever your ScriptAliased
611c611
< <Directory "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin">
---
> <Directory "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin">
824a825,826
> AddType text/xml .xbl
> AddType text/x-compoment .htc
938c940
< Allow from localhost g5server g5server
---
> Allow from localhost quppy guppy
950c952
< # Allow from localhost g5server g5server
---
> # Allow from localhsot guppy guppy
989c991
< # CacheRoot "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/proxy"
---
> # CacheRoot "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/proxy"
1038c1040
< SetEnv PERL5LIB "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/lib/5.6.1:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/lib/site_perl/5.6.1"
---
> SetEnv PERL5LIB "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/lib/5.6.1:
/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/lib/site_perl/5.6.1"
1047c1049
< # PerlSetEnv PERL5LIB "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/lib/5.6.1:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/lib/site_perl/5.6.1"
---
> # PerlSetEnv PERL5LIB "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/lib/5.6.1:
/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/lib/site_perl/5.6.1"
1084,1086c1086,1088
< Alias /fastcgi/ "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/fastcgi/"
< ScriptAlias /fcgi-bin/ "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/fcgi-bin/"
< <Directory "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/fcgi-bin">
---
> Alias /fastcgi/ "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/fastcgi/"
> ScriptAlias /fcgi-bin/ "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/fcgi-bin/"
> <Directory "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/fcgi-bin">
1104c1106
< #include "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Jserv/etc/jserv.conf"
---
> #include "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Jserv/etc/jserv.conf"
1128c1130
< Allow from localhost g5server g5server
---
> Allow from localhost guppy guppy
1134c1136
< Allow from localhost g5server g5server
---
> Allow from localhost guppy guppy
1139c1141
< include "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/mod_oc4j.conf"
---
> include "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/mod_oc4j.conf"
1142c1144
< include "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/dms.conf"
---
> include "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/dms.conf"
1148c1150
< include "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/ssl.conf"
---
> include "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/ssl.conf"
1151c1153
< #include "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/mod_osso.conf"
---
> #include "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/mod_osso.conf"
1154c1156
< include "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/oracle_apache.conf"
---
> include "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/oracle_apache.conf"
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ diff oracle_apache.conf.15 oracle_apache.conf
1,2c1,2
< include "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf/plsql.conf"
< include "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf/marvel.conf"
---
> include "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/modplsql/conf/plsql.conf"
> include "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/modplsql/conf/marvel.conf"
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ diff ssl.conf.15 ssl.conf
23,25c23,25
< #SSLSessionCache dbm:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_scache
< #SSLSessionCache shmht:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_scache(512000)
< SSLSessionCache shmcb:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_scache(512000)
---
> #SSLSessionCache dbm:/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_scache
> #SSLSessionCache shmht:/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_scache(512000)
> SSLSessionCache shmcb:/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_scache(512000)
36c36
< SSLMutex file:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_mutex
---
> SSLMutex file:/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_mutex
45c45
< SSLLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_engine_log
---
> SSLLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_engine_log
81,85c81,85
< DocumentRoot "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/htdocs"
< ServerName g5server
< ServerAdmin you@your.address
< ErrorLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/error_log
< TransferLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/access_log
---
> DocumentRoot "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/htdocs"
> ServerName guppy
> ServerAdmin your@mail.address
> ErrorLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/error_log
> TransferLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/access_log
100c100
< SSLWallet file:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/ssl.wlt/default
---
> SSLWallet file/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/ssl.wlt/default
117,118c117,118
< #SSLCARevocationPath /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/ssl.crl
< #SSLCARevocationFile /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf/ssl.crl/ca-bundle.crl
---
> #SSLCARevocationPath /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/ssl.crl
> #SSLCARevocationFile /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf/ssl.crl/ca-bundle.crl
177c177
< <Directory "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin">
---
> <Directory "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/cgi-bin">
186c186
< CustomLog /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_request_log \
---
> CustomLog /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/ssl_request_log \
guppy:conf oracle$


modplsql関連コンフィグファイルの変更
パスの変更とPlsqlDatabaseUsernameとPlsqlDefaultPageの値が変っているので注意。また接続先は、MacBook proにインストールしたOracle 10g R2 10.2.0.4.0 for MacOSX(Intel x86-64)になるので忘れずに変更する。

変更対象の.confファイルは予め、*.conf.15として退避しておいた。

guppy:conf oracle$ pwd
/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/modplsql/conf
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ diff cache.conf.15 cache.conf
12c12
< PlsqlCacheDirectory /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/cache
---
> PlsqlCacheDirectory /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/modplsql/cache
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ diff marvel.conf.15 marvel.conf
1,2c1,2
< Alias /i/ "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/marvel/images/"
< <Location /pls/htmldb>
---
> Alias /i/ "/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/marvel/images/"
> <Location /pls/apex>
7c7
< PlsqlDatabaseUsername HTMLDB_PUBLIC_USER
---
> PlsqlDatabaseUsername APEX_PUBLIC_USER
9,10c9,10
< PlsqlDatabaseConnectString localhost:1521:tiger.macdeoracle.info ServiceNameFormat
< PlsqlDefaultPage htmldb
---
> PlsqlDatabaseConnectString localhost:1521:leopard1.macdeoracle.jp ServiceNameFormat
> PlsqlDefaultPage apex
15c15
< PlsqlNLSLanguage AMERICAN_AMERICA.AL32UTF8
---
> PlsqlNLSLanguage JAPANESE_JAPAN..AL32UTF8
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$
guppy:conf oracle$ diff plsql.conf.15 plsql.conf
9c9
< LoadModule plsql_module /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/bin/modplsql.so
---
> LoadModule plsql_module /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/modplsql/bin/modplsql.so
20c20
< PlsqlLogDirectory /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/logs
---
> PlsqlLogDirectory /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/modplsql/logs
29c29
< include /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf/dads.conf
---
> include /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/modplsql/conf/dads.conf
34c34
< include /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf/cache.conf
---
> include /Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/modplsql/conf/cache.conf
guppy:conf oracle$


Oracle HTTP ServerのHOMEディレクトリ以下にあるtnsnames.oraもMacBook ProのOracle 10g R2へ接続するよう変更(念のため)

変更前のtnsnames.oraはtnsnames.ora.15として事前に退避しておいた。

guppy:admin oracle$ pwd
/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/network/admin
guppy:admin oracle$
guppy:admin oracle$
guppy:admin oracle$ guppy:admin oracle$ diff tnsnames.ora.15 tnsnames.ora
1c1
< (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=tiger.macdeoracle.info))) =
---
> (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=leopard1.macdeoracle.jp))) =
7,16c7
< (SERVICE_NAME = tiger.macdeoracle.info)
< )
< )
< (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=tiger.macdeoracle.info))) =
< (DESCRIPTION =
< (ADDRESS_LIST =
< (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
< )
< (CONNECT_DATA =
< (SERVICE_NAME = tiger.macdeoracle.info)
---
> (SERVICE_NAME = leopard1.macdeoracle.jp)
guppy:admin oracle$

apache起動用シェルスクリプトの書き換え(パスの変更が主)

要変更と判断したshell scriptは事前に、*.15として退避しておいた。

guppy:bin oracle$ pwd
/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/bin
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$ diff apachectl.15 apachectl
25,26c25,26
< ORACLE_HOME=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex; export ORACLE_HOME
< NLS_LANG=${NLS_LANG="JAPANESE_JAPAN.JA16EUC"}; export NLS_LANG
---
> ORACLE_HOME=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex; export ORACLE_HOME
> NLS_LANG=${NLS_LANG="JAPANESE_JAPAN.AL32UTF8"}; export NLS_LANG
29c29
< PERL5LIB=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/lib/5.8.3:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/lib/site_perl/5.8.3 ; export PERL5LIB
---
> PERL5LIB=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/lib/5.8.3:
/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/lib/site_perl/5.8.3; export PERL5LIB
32c32
< TNS_ADMIN=${TNS_ADMIN="/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/network/admin"}; export TNS_ADMIN
---
> TNS_ADMIN=${TNS_ADMIN="/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/network/admin"}; export TNS_ADMIN
37c37
< LD_LIBRARY_PATH=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/lib:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/opmn/lib ; export LD_LIBRARY_PATH
---
> LD_LIBRARY_PATH=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/lib:
/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/opmn/lib; export LD_LIBRAR_PATH
39c39
< LD_LIBRARY_PATH=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/lib:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/opmn/lib:${LD_LIBRARY_PATH} ; export LD_LIBRARY_PATH
---
> LD_LIBRARY_PATH=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/lib:
/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/opmn/lib:${LD_LIBRARY_PATH}; export LD_LIBRARY_PATH
45c45
< DYLD_LIBRARY_PATH=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/lib:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/opmn/lib ; export DYLD_LIBRARY_PATH
---
> DYLD_LIBRARY_PATH=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/lib:
/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/opmn/lib; export DYLD_LIBRARY_PATH
47c47
< DYLD_LIBRARY_PATH=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/lib:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/opmn/lib:${DYLD_LIBRARY_PATH} ; export DYLD_LIBRARY_PATH
---
> DYLD_LIBRARY_PATH=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/lib:
/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/opmn/lib:${DYLD_LIBRARY_PATH}; export DYLD_LIBRARY_PATH
52c52
< PIDFILE=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/httpd.pid
---
> PIDFILE=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/httpd.pid
55c55
< HTTPD=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/bin/httpd
---
> HTTPD=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/bin/httpd
91c91
< if eval $HTTPD -d /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache $args
---
> if eval $HTTPD -d /Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache $args
129c129
< if eval $HTTPD -d /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache $args
---
> if eval $HTTPD -d /Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache $args
149c149
< if eval $HTTPD -d /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache -t $args > /dev/null
---
> if eval $HTTPD -d /Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache -t $args > /del/null
261c261
< if eval $HTTPD -d /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache -t "$args"
---
> if eval $HTTPD -d /Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache -t "$args"
guppy:bin oracle$

以下、apachectlと同じディレクトリにあるが利用されているか不明なシェルスクリプトもとりあえず変更した。

guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$ diff apxs.15 apxs
1c1
< #!/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/bin/perl
---
> #!/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/bin/perl
78,79c78,79
< my $CFG_PREFIX = q(/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache);
# substituted via APACI install
< my $CFG_SBINDIR = q(/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/bin);
# substituted via APACI install
---
> my $CFG_PREFIX = q(/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache);
> my $CFG_SBINDIR = q(/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/bin);
81,82c81,82
< my $CFG_LIBEXECDIR = q(/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/libexec);
# substituted via APACI install
< my $CFG_SYSCONFDIR = q(/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/conf);
# substituted via APACI install
---
> my $CFG_LIBEXECDIR = q(/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/libexec);
> my $CFG_SYSCONFDIR = q(/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/conf);
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$ diff dbmmanage.15 dbmmanage
1c1
< #!/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/bin/perl
---
> #!/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/bin/perl
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$ diff iasobf.15 iasobf
11c11
< ORACLE_HOME=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex
---
> ORACLE_HOME=/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$ diff log_server_status.15 log_server_status
1c1
< #!/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/perl/bin/perl
---
> #!/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/perl/bin/perl
70c70
< $wherelog = "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/graph/";
# Logs will be like "/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/logs/graph/19960312"
---
> $wherelog = "/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/graph/";
# Logs will be like "/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/logs/graph/19960312"
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$ diff root_sh_append.sh.15 root_sh_append.sh
3c3
< ORACLE_HOME="/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex";export ORACLE_HOME
---
> ORACLE_HOME="/Users/shared/u01/app/oracle/oracle/product/10.1.0/apex"; export ORACLE_HOME
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$
guppy:bin oracle$ diff ssl2ossl.15 ssl2ossl
8c8
< ORACLE_HOME=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex
---
> ORACLE_HOME=Users/shared/u01/app/oracle/oracle/product/10.1.0/apex
guppy:bin oracle$


実行してみる(listenerおよびOracle Databaseインスタンスは起動ずみ)
尚、startohsは、apachectl startをalias定義しておいたもの(本エントリの最初の部分参照)

guppy:˜ oracle$ startohs
/Users/Shared/u01/app/oracle/oracle/product/10.1.0/apex/Apache/Apache/bin/apachectl start: httpd started
guppy:˜ oracle$


まずOralce HTTP Serverにアクセスしてみる・・・・OK.

4


APEX3.2の管理画面にアクセスしてログイン.....OK
ログインしたついでに開発ユーザと作業領域を作成しておく。


5

6

13

14


開発ユーザでログインしてみる。......OK。やった〜〜〜!!!!! うまくいった

15

16


最後にバージョン等も確認しておく。

3


19

「Oracle 10g R2 10.2.0.4.0 for MacOSX (Intel x86-64)で未サポートとなっているOracle HTTP ServerとPL/SQL gatewayをOracle 10g R1 10.1.0.3.0 for MacOSX (PowerPC)環境から移動し、Rosettaの力でそれらを動かしてAPEX3.2を使う!。」・・・・・一件落着。。。。次回はOraTweetでも遊んでみますか...



バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)
Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)
Leopard de Oracle10g R2 (Intel x86-64) #11 (APEX3.2と遊ぶ #4)
Leopard de Oracle10g R2 (Intel x86-64) #12 (APEX3.2と遊ぶ #5)
Leopard de Oracle10g R2 (Intel x86-64) #13 (APEX3.2と遊ぶ #6)
Leopard de Oracle10g R2 (Intel x86-64) #14 (APEX3.2と遊ぶ #7)
Leopard de Oracle10g R2 (Intel x86-64) #15 (APEX3.2と遊ぶ #8)
Leopard de Oracle10g R2 (Intel x86-64) #16 (APEX3.2と遊ぶ #9)
Leopard de Oracle10g R2 (Intel x86-64) #17 (APEX3.2と遊ぶ #10)

| | | コメント (0) | トラックバック (0)

2009年6月 2日 (火)

Leopard de Oracle10g R2 (Intel x86-64) #17 (APEX3.2と遊ぶ #10)

Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第九話。

Intel版MacへOracle HTTP ServerとPL/SQL gatewayをORACLE_HOMEごと移動するための環境作りとしてPowerPC上でOracle 10g R1 10.1.0.3.0 + Oracle HTTP Server + HTMLDB 1.5をインストール及び構成する。のづづき。
前回、HTMLDB 1.5の構成でエラーが発生したが今回はそれを解決後、再実行してHTMLDBのログイン画面にアクセスするまで




78


発生したエラーを確認すると、前々回の場合と同様にjavaへのパスがおかしなことになってる。

--------------------------------------
次のConfiguration Assistantが正常に完了していません。これらのアシスタントは、製品を完全に構成するため、完了する必要があります。
ファイル/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/cfgtoollogs/configToolCommandsを実行して、
スキップまたは失敗したすべてのConfiguration Assistantを再実行します。
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/jdk/jre/bin/java
-classpath /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/marvel/jlib/htmlDBConfigClasses.jar:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/jdbc/lib/classes12.jar:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/jdk/jre/lib/rt.jar:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/jlib/ojmisc.jar:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/oui/jlib/OraInstaller.jar: htmlDBConfig
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex
localhost 1521 tiger.macdeoracle.info SYSAUX * 保護された値。ログは取られません。
* * 保護された値。ログは取られません。 *
en,ja (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=tiger.macdeoracle.info)))
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/marvel
--------------------------------------

上記赤太字部分を実行してみると・・・・

G5Server:˜ oracle$ /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/jdk/jre/bin/java
-su: /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/jdk/jre/bin/java: No such file or directory
G5Server:˜ oracle$

javaが見えてないね。細かく見ていくとシンボリックリンクjreが原因。ただ前々回のようにconfigToolCommandsを変更して再実行することはできない。
理由はconfigToolCommands内に全てのパラメータが記録されていない(「保護された値。ログは取られません。」となっている部分があるから...

ということでjavaへパスが通るように修正・・・・・

Oracle HTTP ServerをインストールしたORACLE_HOME/jdkへ移動してシンボリックリンクjreを作り直す。

G5Server:˜ oracle$ cd /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/jdk
drwxr-xr-x 42 oracle oinstall 1428 May 17 12:22 bin
lrwxrwxr-x 1 oracle oinstall 26 May 17 12:22 include -> /Library/Java/Home/include
lrwxrwxr-x 1 oracle oinstall 22 May 17 12:22 jre -> /Library/Java/Home/jre
drwxr-xr-x 38 oracle oinstall 1292 May 17 12:22 lib
lrwxrwxr-x 1 oracle oinstall 22 May 17 12:22 man -> /Library/Java/Home/man
G5Server:˜/u01/app/oracle/product/10.1.0/apex/jdk oracle$ rm jre
G5Server:˜/u01/app/oracle/product/10.1.0/apex/jdk oracle$ ln -s /System/Library/Frameworks/JavaVM.framework/Home jre
G5Server:˜/u01/app/oracle/product/10.1.0/apex/jdk oracle$ ll
total 24
drwxr-xr-x 42 oracle oinstall 1428 May 17 12:22 bin
lrwxrwxr-x 1 oracle oinstall 26 May 17 12:22 include -> /Library/Java/Home/include
lrwxr-xr-x 1 oracle oinstall 48 May 17 12:58 jre -> /System/Library/Frameworks/JavaVM.framework/Home
drwxr-xr-x 38 oracle oinstall 1292 May 17 12:22 lib
lrwxrwxr-x 1 oracle oinstall 22 May 17 12:22 man -> /Library/Java/Home/man
G5Server:˜/u01/app/oracle/product/10.1.0/apex/jdk oracle$


次Oracle HTTP ServerをインストールしたORACLE_HOME/oui/binへ移動後、OUIを起動。HTMLDBだけを再インストール、構成しちゃえば解決!!(これが嫌な場合はJDK1.4.2か1.5をデフォルトにしておいた方が無難だと思う。(但し、それでこの問題が再現しなくなるか確認したわけではありません。)

また、念には念ということでJAVA_HOME環境変数へもシンボリックリンクjreへ設定したパスを設定しておいた。JAVA_HOMEの設定が必要なのかどうか? という点に関して、ちょいとヤッツケモードだったので本当に必要なのかどうか確認していません。m(_ _)m

G5Server:˜/u01/app/oracle/product/10.1.0/apex/oui/bin oracle$ pwd
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/oui/bin
G5Server:˜/u01/app/oracle/product/10.1.0/apex/oui/bin oracle$ ll
total 336
-rwxr-xr-x 1 oracle oinstall 412 May 17 12:22 addNode.sh
-rwxr-xr-x 1 oracle oinstall 20576 Aug 17 2004 lsnodes
drwxr-xr-x 12 oracle oinstall 408 May 17 12:22 resource
-rwxr-xr-x 1 oracle oinstall 136524 Aug 17 2004 runInstaller
-rwxr-xr-x 1 oracle oinstall 105 May 17 12:22 runInstaller.sh
G5Server:˜/u01/app/oracle/product/10.1.0/apex/oui/bin oracle$ export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
G5Server:˜/u01/app/oracle/product/10.1.0/apex/oui/bin oracle$ ./runInstaller &
[1] 7026
G5Server:˜/u01/app/oracle/product/10.1.0/apex/oui/bin oracle$ Starting Oracle Universal Installer...
No pre-requisite checks found in oraparam.ini, no system pre-requisite checks will be executed.
Preparing to launch Oracle Universal Installer from /tmp/OraInstall2009-05-17_01-26-05PM.
Please wait ...Oracle Universal Installer, バージョン 10.1.0.3.0 製品版
Copyright (C) 1999, 2004, Oracle. All rights reserved.

Executing install_name_tool...
Done Executing install_name_tool...



83

84

85

86

87

88

89

90

91

Oracle HTTP Serverにアクセスしてみると・・・OK.うまくいってる。
92

HTML DBにアクセスしてみる・・・OK .準備完了。
93

[1]+  Done                    ./runInstaller
G5Server:˜/u01/app/oracle/product/10.1.0/apex/oui/bin oracle$
G5Server:˜/u01/app/oracle/product/10.1.0/apex/oui/bin oracle$ unset JAVA_HOME
G5Server:˜/u01/app/oracle/product/10.1.0/apex/oui/bin oracle$


うまくいった。わははは!


さてPowerMac上での最後の作業は、Oracle HTTP Server+HTMLDBをインストールしたORACLE_HOMEごとIntel Macへ移動する前にmarvel.confのPlsqlDatabasePasswordを再設定しておきます。(OUIで設定したHTML DBのパスワードとIntel MacにインストールしたAPEX3.2のパスワードを同じパスワードにすればこの作業は不要だと思われます。また、パスワードが分からなくなった場合でもAPEX3.2のパスワードを再設定後、以下の方法でmarvel.confのPlsqlDatabasePasswordを再設定、暗号化すれば解決できます。)

Oracle HTTP Server+HTMLDBをインストールしたORACLE_HOME/Apache/modplsql/confに移動して....
(ちなみに、Oracle HTTP Serverはdads.confでもmarvel.confでも使えるようになっているが、HTML DBの構成情報はmarvel.confに書き込まれるためmarvel.confを編集している。)

G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ pwd 
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ cp dads.conf dads.conf.org
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ cp marvel.conf marvel.conf.org
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ cp marvel.conf dads.conf
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ export ORACLE_HOME=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex
G5Server:~˜u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ perl dadTool.pl -o
----------------------------------------------------------------------------
Error : Cannot invoke utility 'dadobf'
Reason: The environment setting for PATH/LD_LIBRARY_PATH is not setup to load
the required libraries.
Action: Verify the environment settings needed for running this script


----------------------------------------------------------------------------
Hint: Use the '-h' option to get details on how to invoke the script
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ export PATH=$ORACLE_HOME:bin:.:$PATH
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ perl dadTool.pl -o

Information
----------------------------------------------------------------------------
Backed up older dads.conf as /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf/dads.conf.orig.2009-05-17_19-09

All passwords successfully obfuscated. New obfuscations : 0
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$
Information
----------------------------------------------------------------------------
Backed up older dads.conf as /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf/dads.conf.orig.2009-05-17_19-11

All passwords successfully obfuscated. New obfuscations : 1
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ mv dads.conf marvel.conf
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ mv dads.conf.org dads.conf
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$ diff marvel.conf.org marvel.conf
8c8
< PlsqlDatabasePassword apex
---
> PlsqlDatabasePassword @BTpCAhnG7HjcGKTr0ns63KI=
G5Server:˜/u01/app/oracle/product/10.1.0/apex/Apache/modplsql/conf oracle$

Oracle HTTP ServerとHTML DB(関連するPL/SQLパッケージを除く)のORACLE_HOMEごとIntel Macへの移動準備完了。Rosettaの力を借りる。To be continued!



バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)
Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)
Leopard de Oracle10g R2 (Intel x86-64) #11 (APEX3.2と遊ぶ #4)
Leopard de Oracle10g R2 (Intel x86-64) #12 (APEX3.2と遊ぶ #5)
Leopard de Oracle10g R2 (Intel x86-64) #13 (APEX3.2と遊ぶ #6)
Leopard de Oracle10g R2 (Intel x86-64) #14 (APEX3.2と遊ぶ #7)
Leopard de Oracle10g R2 (Intel x86-64) #15 (APEX3.2と遊ぶ #8)
Leopard de Oracle10g R2 (Intel x86-64) #16 (APEX3.2と遊ぶ #9)

| | | コメント (0) | トラックバック (0)

2009年6月 1日 (月)

Leopard de Oracle10g R2 (Intel x86-64) #16 (APEX3.2と遊ぶ #9)

Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第九話。

Intel版MacへOracle HTTP ServerとPL/SQL gatewayをORACLE_HOMEごと移動するための環境作りとしてPowerPC上でOracle 10g R1 10.1.0.3.0 + Oracle HTTP Server + HTMLDB 1.5をインストール及び構成する。のづづき。
CompanionCDからOracle HTTP ServerとHTMLDB(現在はAPEX)をインストール、構成しちゃいます。


OTNからOracle 10g R1 10.1.0.3.0 for MacOSX(PowerPC)のCompanion CDをダウンロード、解凍。解凍後に作成されるDisk1ディレクトリへ移動しOUIによるインストール、構成を行います。

G5Server:˜ oracle$ cd /Volumes/DiscusWork/temp/Disk1/ 
G5Server:/Volumes/DiscusWork/temp/Disk1 oracle$ ll
total 24
drwxr-xr-x 15 oracle oinstall 510 May 16 23:34 doc
drwxr-xr-x 7 oracle oinstall 238 May 16 23:34 fonts
drwxr-xr-x 12 oracle oinstall 408 May 16 23:34 install
-rwxr-xr-x 1 oracle oinstall 1259 Dec 17 2004 runInstaller
drwxr-xr-x 15 oracle oinstall 510 May 16 23:36 stage
-rwxr-xr-x 1 oracle oinstall 4188 Dec 17 2004 welcome.htm
G5Server:/Volumes/DiscusWork/temp/Disk1 oracle$ ./runInstaller &
[1] 5831
G5Server:/Volumes/DiscusWork/temp/Disk1 oracle$ Starting Oracle Universal Installer...
No pre-requisite checks found in oraparam.ini, no system pre-requisite checks will be executed.
・・・・以下略・・・・

以下、OUI各画面のスナップショット。インストール構成が今回のテーマではないのでポイントになる箇所以外解説はしていません。(過去の記事なども参考にしてみてください。)



60

61

ちなみに、インストールするORACLE_HOMEは他のDatabase等とは異なるディレクトリにするところがポイントですよん。
63

64

68

71

HTML DBのパスワードはIntel MacでインストールしたAPEX3.2で設定予定のパスワード(現時点では未設定)と同じにしておくと楽です。(異なっても問題はないです。)
それ以外の項目はこのデータベースのインストールで設定したりした値やパスワードです。
HTML DB(APEX)はSYSAUX以外の表領域でもいいですが、今回はSYSAUX以外にする必要はないのでSYSAUXにしてあります。

73_2

74

77

HTML DB Configuration Assistantでエラーが発生してますが、あとで再実行するのでここでは「OK」ボタンクリック後、エラーを無視してインストールを続行します。
78

79

お約束のroot.shの実行です。指示された場所にあるroot.shをrootユーザで実行します。が・・・・以前とは違いちょっとしたエラーが・・・・
80

お約束のroot.shを実行したら・・・・エラーですか・・・・
root_sh_append.shの該当行を見てみると・・・chownとchmodをfull path指定が間違っていたので修正。

G5Server:˜/u01/app/oracle/product/10.1.0/apex oracle$ sudo sh
Password:
sh-2.05b# /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/root.sh
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/
Apache/bin/root_sh_append.sh: line 7: /usr/bin/chown: No such file or directory
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/
Apache/bin/root_sh_append.sh: line 8: /usr/bin/chmod: No such file or directory
Running Oracle10 root.sh script...
¥nThe following environment variables are set as:
ORACLE_OWNER= oracle
ORACLE_HOME= /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex

Enter the full pathname of the local bin directory: [/usr/local/bin]:

The file "dbhome" already exists in /usr/local/bin. Overwrite it? (y/n) [n]:n
The file "oraenv" already exists in /usr/local/bin. Overwrite it? (y/n) [n]:n
The file "coraenv" already exists in /usr/local/bin. Overwrite it? (y/n) [n]:n

Adding entry to /etc/oratab file...
Entries will be added to the /etc/oratab file as needed by
Database Configuration Assistant when a database is created
Finished running generic part of root.sh script.
Now product-specific root actions will be performed.
sh-2.05b#
sh-2.05b# which chmod
/bin/chmod
sh-2.05b# which chown
/usr/sbin/chown
sh-2.05b#
sh-2.05b#

root_sh_append.shの修正内容

sh-2.05b#
sh-2.05b# vi /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/bin/root_sh_append.sh
sh-2.05b# diff /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/bin/root_sh_append.sh.org
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/Apache/Apache/bin/root_sh_append.sh
7,8c7,8
< /usr/bin/chown root $SSOMIGRATE
< /usr/bin/chmod 6750 $SSOMIGRATE
---
> chown root $SSOMIGRATE
> chmod 6750 $SSOMIGRATE
sh-2.05b#

root.shを再実行。(こんどは上手く行った)

sh-2.05b#
sh-2.05b#
sh-2.05b# /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex/root.sh
Running Oracle10 root.sh script...
¥nThe following environment variables are set as:
ORACLE_OWNER= oracle
ORACLE_HOME= /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/apex

Enter the full pathname of the local bin directory: [/usr/local/bin]:
The file "dbhome" already exists in /usr/local/bin. Overwrite it? (y/n) [n]:n
The file "oraenv" already exists in /usr/local/bin. Overwrite it? (y/n) [n]:n
The file "coraenv" already exists in /usr/local/bin. Overwrite it? (y/n) [n]:n

Adding entry to /etc/oratab file...
Entries will be added to the /etc/oratab file as needed by
Database Configuration Assistant when a database is created
Finished running generic part of root.sh script.
Now product-specific root actions will be performed.
sh-2.05b#

root.shを実行したらOUIを終了しておわり。
81

82


以前は発生していなかったのですが、HTMLDB Configuration Assistantで問題が発生しました。原因は前々回と同じくJavaSE6.0 Developer Preview 1をデフォルトにしていることが影響しているようです。ということで解決方法なども含めて、To be continued.



バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)
Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)
Leopard de Oracle10g R2 (Intel x86-64) #11 (APEX3.2と遊ぶ #4)
Leopard de Oracle10g R2 (Intel x86-64) #12 (APEX3.2と遊ぶ #5)
Leopard de Oracle10g R2 (Intel x86-64) #13 (APEX3.2と遊ぶ #6)
Leopard de Oracle10g R2 (Intel x86-64) #14 (APEX3.2と遊ぶ #7)
Leopard de Oracle10g R2 (Intel x86-64) #15 (APEX3.2と遊ぶ #8)

| | | コメント (0) | トラックバック (0)

2009年5月31日 (日)

Leopard de Oracle10g R2 (Intel x86-64) #15 (APEX3.2と遊ぶ #8)

Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第八話。

Intel版MacへOracle HTTP ServerとPL/SQL gatewayをORACLE_HOMEごと移動するための環境作りとしてPowerPC上でOracle 10g R1 10.1.0.3.0 + Oracle HTTP Server + HTMLDB 1.5をインストール及び構成する。のづづき。
dbcaでデータベースインスタンスを作っちゃいます。Oracle10g R1 10.1.0.3.0 for MacOSXをMacOSX Tigerで動作させるためのお約束の作業から開始!


OTNからダウンロードできるOracle 10g R1 10.1.0.3.0 for MacOSX(PowerPC)MacOSX Tiger以降ではdbcaがこけて起動しないのでUS OTNのフォーラムでもおなじみの以下の方法で解決する。

G5Server:˜ oracle$ cd $ORACLE_HOME/lib
G5Server:˜/u01/app/oracle/product/10.1.0/tiger/lib oracle$ mv libnnz10.dylib libnnz10.dylib.org
G5Server:˜/u01/app/oracle/product/10.1.0/tiger/lib oracle$ relink all
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/bin/genclntsh
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/lib/nautab.o

・・・・中略・・・・

Enterprise Manager native components relinked
IMPORTANT NOTE: To complete this Install/upgrade, please login as root and
execute the root.sh script.
G5Server:˜/u01/app/oracle/product/10.1.0/tiger/lib oracle$ mv libnnz10.dylib.org libnnz10.dylib
G5Server:˜/u01/app/oracle/product/10.1.0/tiger/lib oracle$ cd
G5Server:˜ oracle$

relinkが済んだらdbcaを起動してデータベースを作成。

G5Server:˜ oracle$ dbca &
[1] 5600
G5Server:˜ oracle$

以下dbcaの各画面。dbcaの使い方は今回のメインネタじゃないので各画面毎の解説はしていません。Tiger Server de Oracle10gマニュアルを参考にしてくださいね。)



Dbca_1

Dbca_2

Dbca_3

Dbca_4

Dbca_5_2

Dbca_6

Dbca_7

Dbca_8

Dbca_9_2

Dbca_10_2

Dbca_11

Dbca_12

Dbca_13

Dbca_14

Dbca_15

Dbca_16

Dbca_17

Dbca_18

Dbca_21

Dbca_22


データベースが作成できたら、リスナーの状態を確認してデータベースインスタンスの起動を確認!

G5Server:˜ oracle$ lsnrctl status

LSNRCTL for MacOS X Server: Version 10.1.0.3.0 - Production on 17-5月 -2009 12:06:27

Copyright (c) 1991, 2004, Oracle. All rights reserved.

(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=g5server)(PORT=1521)))に接続中
リスナーのステータス
------------------------
別名 LISTENER
バージョン TNSLSNR for MacOS X Server: Version 10.1.0.3.0 - Production
開始日 17-5月 -2009 11:46:47
稼働時間 0 日 0 時間 19 分 40 秒
トレース・レベル off
セキュリティ ON: Local OS Authentication
SNMP OFF
パラメータ・ファイル /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/admin/listener.ora
ログ・ファイル /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/log/listener.log
リスニング・エンドポイントのサマリー...
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=g5server)(PORT=1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=g5server)(PORT=8080))(Presentation=HTTP)(Session=RAW))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=g5server)(PORT=2100))(Presentation=FTP)(Session=RAW))
サービスのサマリー...
サービス"PLSExtProc"には、1件のインスタンスがあります。
インスタンス"PLSExtProc"、状態UNKNOWNには、このサービスに対する1件のハンドラがあります...
サービス"tiger.macdeoracle.info"には、1件のインスタンスがあります。
インスタンス"tiger"、状態READYには、このサービスに対する1件のハンドラがあります...
サービス"tigerXDB.macdeoracle.info"には、1件のインスタンスがあります。
インスタンス"tiger"、状態READYには、このサービスに対する1件のハンドラがあります...
コマンドは正常に終了しました。
G5Server:˜ oracle$ sqlplus /nolog

SQL*Plus: Release 10.1.0.3.0 - Production on 日 5月 17 12:06:32 2009

Copyright (c) 1982, 2004, Oracle. All rights reserved.

SQL> conn sys@tiger as sysdba
パスワードを入力してください:
接続されました。
SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.1.0.3.0 - Prod
PL/SQL Release 10.1.0.3.0 - Production
CORE 10.1.0.3.0 Production
TNS for MacOS X Server: Version 10.1.0.3.0 - Production
NLSRTL Version 10.1.0.3.0 - Production

SQL> col comp_name for a50
SQL> set linesize 132
SQL> select comp_name,version,status from dba_registry;

COMP_NAME VERSION STATUS
-------------------------------------------------- ------------------------------ -----------
Oracle Database Catalog Views 10.1.0.3.0 VALID
Oracle Database Packages and Types 10.1.0.3.0 VALID
Oracle Workspace Manager 10.1.0.2.0 VALID
JServer JAVA Virtual Machine 10.1.0.3.0 VALID
Oracle XDK 10.1.0.3.0 VALID
Oracle Database Java Packages 10.1.0.3.0 VALID
Oracle Expression Filter 10.1.0.3.0 VALID
Oracle interMedia 10.1.0.3.0 VALID
Oracle Data Mining 10.1.0.3.0 VALID
Oracle Text 10.1.0.3.0 VALID
Oracle XML Database 10.1.0.3.0 VALID
OLAP Analytic Workspace 10.1.0.3.0 VALID
Oracle OLAP API 10.1.0.3.0 INVALID
OLAP Catalog 10.1.0.3.0 VALID
Spatial 10.1.0.3.0 VALID
Oracle Ultra Search 10.1.0.3.0 VALID
Oracle Enterprise Manager 10.1.0.3.0 VALID

17行が選択されました。

SQL>
SQL> select tablespace_name from dba_tablespaces;

TABLESPACE_NAME
------------------------------
SYSTEM
UNDOTBS1
SYSAUX
TEMP
USERS

SQL>


次回は当時HTMLDBと呼ばれていたAPEXとOracle HTTP ServerをPowerMac上でインストール、構成しちゃいます。

今回はここまで、To be continued.




バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)
Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)
Leopard de Oracle10g R2 (Intel x86-64) #11 (APEX3.2と遊ぶ #4)
Leopard de Oracle10g R2 (Intel x86-64) #12 (APEX3.2と遊ぶ #5)
Leopard de Oracle10g R2 (Intel x86-64) #13 (APEX3.2と遊ぶ #6)
Leopard de Oracle10g R2 (Intel x86-64) #14 (APEX3.2と遊ぶ #7)

| | | コメント (0) | トラックバック (0)

Leopard de Oracle10g R2 (Intel x86-64) #14 (APEX3.2と遊ぶ #7)

Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第七話。

Intel版MacへOracle HTTP ServerとPL/SQL gatewayをORACLE_HOMEごと移動するための環境作りとしてPowerPC上でOracle 10g R1 10.1.0.3.0 + Oracle HTTP Server + HTMLDB 1.5をインストール及び構成する。のづづき。
Oracle 10g R1 10.1.0.3.0のインストール中netcaがコケていた。ログから原因を探ると、デフォルトのJDKをJavaSE6.0 Developer Preview 1にしちゃっているのが原因でインストール時にORACLE_HOME内に作成されるjreのシンボリックリンクが正しく作成されていない。(java2 1.4.2のjreへのシンボリックリンクを作成しようとしているみたい・・・)

というところから...


以下、OUIの指示に従い
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/cfgtoollogs/configToolCommandsを実行してみると、/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jdk/jre//bin/javaがNo such file or directoryとなる。
どうもシンポリックリンク先にjreが存在していない。Tiger Server de Oracle10g を試した頃はJDK1.4.2をデフォルトにしていたのでこの問題は発生しなかったがJavaSE6.0 Developer Preview 1に変更してある影響なんだろう。

G5Server:˜ oracle$ /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/cfgtoollogs/configToolCommands
Oracle Net Configuration Assistant
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/cfgtoollogs/configToolCommands: line 2:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jdk/jre//bin/java: No such file or directory
G5Server:˜ oracle$


ということで、/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jdk/jre//bin/javaの部分をjavaに書き換えて再実行すればOK。

G5Server:/u01/app/oracle/product/10.1.0/tiger oracle$ 
--------------------------------------
次のConfiguration Assistantが正常に完了していません。これらのアシスタントは、製品を完全に構成するため、完了する必要があります。
ファイル/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/cfgtoollogs/configToolCommandsを実行して、
スキップまたは失敗したすべてのConfiguration Assistantを再実行します。
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jdk/jre//bin/java
-Duser.dir=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/jlib
-classpath /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jdk/jre//lib/i18n.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jdk/jre//lib/rt.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/ewt3.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/ewtcompat-3_3_15.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/netcfg.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/jlib/netcam.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/jlib/netca.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/help4.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/jewt4.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/oracle_ice.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/share.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/swingall-1_1_1.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/srvmhas.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/srvm.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/tools
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/ldapjclnt9.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/ldapjclnt10.jar
oracle.net.ca.NetCA /orahome /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger
/orahnam OraDb10g_tiger /instype custom /inscomp client,oraclenet,javavm,server
/insprtcl tcp /cfg local /authadp NO_VALUE /nodeinfo NO_VALUE /responseFile
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/install/netca_typ.rsp
--------------------------------------

G5Server:˜ oracle$
G5Server:˜ oracle$ cat /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/cfgtoollogs/configToolCommands
G5Server:˜ oracle$ java
-Duser.dir=/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/jlib
-classpath /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jdk/jre//lib/i18n.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jdk/jre//lib/rt.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/ewt3.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/ewtcompat-3_3_15.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/netcfg.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/jlib/netcam.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/jlib/netca.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/help4.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/jewt4.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/oracle_ice.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/share.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/swingall-1_1_1.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/srvmhas.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/srvm.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/tools
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/ldapjclnt9.jar
:/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/jlib/ldapjclnt10.jar
oracle.net.ca.NetCA /orahome /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger
/orahnam OraDb10g_tiger /instype custom /inscomp client,oraclenet,javavm,server
/insprtcl tcp /cfg local /authadp NO_VALUE /nodeinfo NO_VALUE /responseFile
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/install/netca_typ.rsp
コマンドライン引数の解析:
パラメータ "orahome" = /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger
パラメータ "orahnam" = OraDb10g_tiger
パラメータ "instype" = custom
パラメータ "inscomp" = client,oraclenet,javavm,server
パラメータ "insprtcl" = tcp
パラメータ "cfg" = local
パラメータ "authadp" = NO_VALUE
パラメータ "nodeinfo" = NO_VALUE
パラメータ "responsefile" = /Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/network/install/netca_typ.rsp
コマンドライン引数の解析が終了しました。
Oracle Net Servicesの構成:
デフォルトのローカル・ネーミング構成が完了しました。
次のリスナーを構成中:LISTENER
リスナーの構成が完了しました。
Oracle Net Listenerの起動:
リスナーの制御の実行:
/Volumes/Discus/4oracle/oracle/u01/app/oracle/product/10.1.0/tiger/bin/lsnrctl start LISTENER
リスナーの制御が完了しました。
リスナーの起動に成功しました。
プロファイルの構成が完了しました。
プロファイルの構成が完了しました。
デフォルトのローカル・ネーミング構成が完了しました。
作成されたネット・サービス名: tiger
Oracle Net Servicesの構成に成功しました。終了コードは次のとおりです。0
G5Server:˜ oracle$
G5Server:˜ oracle$

以下、netcaでlistenerとその構成中のスナップショット。(細かな解説は入れてませんのであしからず。)


Netca_1

Netca_2_2

Netca_3

Netca_4

Netca_5

Netca_6

Netca_7

Netca_8

Netca_9

Netca_10_2

Netca_11

Netca_12

Netca_13_2

Netca_14

Netca_15

Netca_16

Netca_16_2

Netca_17_2

Netca_18

次回はdbcaを利用したデータベース作成。

今回はここまで、To be continued.




バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)
Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)
Leopard de Oracle10g R2 (Intel x86-64) #11 (APEX3.2と遊ぶ #4)
Leopard de Oracle10g R2 (Intel x86-64) #12 (APEX3.2と遊ぶ #5)
Leopard de Oracle10g R2 (Intel x86-64) #13 (APEX3.2と遊ぶ #6)

| | | コメント (0) | トラックバック (0)

2009年5月29日 (金)

Leopard de Oracle10g R2 (Intel x86-64) #13 (APEX3.2と遊ぶ #6)

Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第六話。

Intel版MacへOracle HTTP ServerとPL/SQL gatewayをORACLE_HOMEごと移動するための環境作りとしてPowerPCの環境でOracle 10g R1 10.1.0.3.0 + Oracle HTTP Server + HTMLDB 1.5をインストール及び構成するところから。


MacOSX 10.4.11とJavaSE6.0 Developer Preview 1となっている事以外は、Tiger Server De Oracle10g その6と同じである。(ちなみにMacOSXのカーネルパラメータはTiger Server De Oracle10gをやっていた当時のままなので今回の手順ではさらりと書いておくことにした。)

Oracle所有者の環境変数やカーネルパラメータはインストレーションガイド以前のものを参考にしてみてね。

参考資料
Oracle Database 10g Release 1 (10.1) Documentation for Apple Mac OS X

ちなみに、今回の環境変数設定などは以下、あらかじめインストールするディレクトリなどは決めているので事前に設定しておいた。(MySQL関係のは気にしないでね。)


G5Server:˜ oracle$ cat .bash_profile
alias ls='ls -v'
alias ll='ls -vl'
alias startapex='$ORACLE_HOME/../apex/Apache/Apache/bin/apachectl start'
alias stopapex='$ORACLE_HOME/../apex/Apache/Apache/bin/apachectl stop'

umask 022

# oracle environment
TEMP=/tmp
TMPDIR=/tmp
ORACLE_BASE=/Volumes/Discus/4oracle/oracle/u01/app/oracle
ORACLE_HOME=$ORACLE_BASE/product/10.1.0/tiger
ORACLE_SID=tiger
NLS_LANG=Japanese_Japan.AL32UTF8
DYLD_LIBRARY_PATH=$ORACLE_HOME/lib:$ORACLE_HOME/rdbms/lib:$DYLD_LIBRARY_PATH
PATH=$ORACLE_HOME/bin:$PATH
export TEMP TMPDIR ORACLE_BASE ORACLE_HOME ORACLE_SID NLS_LANG DYLD_LIBRARY_PATH PATH

export MYSQL_HOME=/usr/local/mysql
export DATADIR=$MYSQL_HOME/data
export PATH=$MYSQL_HOME/bin:$PATH
G5Server:˜ oracle$

TerminalでunzipしてできるDisk1以下のrunInstallerを起動してOracledatabase 10g R1のインストール開始(Tiger Server de Oracle10gも見てね


Install_db_1

Install_db_2

Install_db_3

Install_db_4_2

Install_db_5

今回はデータベースそのものよりOracle HTTP ServerとAPEXがメインなので必要最低限のオプションだけにしてインストール時間の節約。
Install_db_6_2

Install_db_7

Install_db_8

Install_db_9

インストール時にデータベース作成は行わない。
Install_db_10

Install_db_11

Install_db_12

お約束のroot.shの実行要求が表示されたらTerminalからrootユーザでroot.shを実行する。
Install_db_13

Configuration Assistantの実行でnetcaの実行に失敗するが、JavaSE6.0 Developer Preview 1を利用していることが影響していると考えられる内容であったためエラーを無視してすすめる。(netcaはこの後、手動で実行すれば問題ないので。)
JDK1.4.2又はJavaSE5.0を使っているのならこのエラーには遭遇しないと(思われる)。


Install_db_14

Install_db_15

Install_db_16

Install_db_17_2



OracleDatabase 10g R1のインストールでTiger Server De Oracle10gの頃にはなかったエラーがnetcaの実行中に発生したがこれはJavaSE6.0 Developer Preview 1をデフォルトのJDKに設定している(詳細は次回)ことが影響しているようなのでOUIのインストールではエラーを無視し後でnetcaを手動実行する。

今回はここまで、To be continued.





バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)
Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)
Leopard de Oracle10g R2 (Intel x86-64) #11 (APEX3.2と遊ぶ #4)
Leopard de Oracle10g R2 (Intel x86-64) #12 (APEX3.2と遊ぶ #5)

| | | コメント (0) | トラックバック (0)

2009年5月27日 (水)

Leopard de Oracle10g R2 (Intel x86-64) #12 (APEX3.2と遊ぶ #5)

随分間が空いてしまったが、Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第五話。

前回PowerMac G5へインストールしたOracle HTTP ServerとPL/SQL GatewayをそのままIntel Macへ移植移動しただけでOracle HTTP Server+PL/SQL gateway+Oracle Application Express3.2が動作した結果だけをお見せしたが、今回はその手順を。

前提としてIntel Mac以外に、PowerPC版のMacとMacOSX Tiger 10.4.11(ServerでもClient版でも可。)が必要。おそらく、PowerPC版MacはPantherでも可能(だと思う)。


手順の概要

PowerMac G5 (MacOSX Tiger Server 10.4.11)へ、Oracle 10g R1 10.1.0.3.0 for MacOSX (PowerPC版)のDatabase及び、CompanionCDよりOracle HTTP ServerとAPEX(当時はHTMLDBと呼ばれていた)をインストール及び構成(起動するところまで確認)後、Oracle HTTP Server用のORACLE_HOMEディレクトリをまるごとOracle 10g R2 10.2.0.4.0 for MacOSX(Intel x86-64)及びAPEX3.2インストール済みのMacBook Pro - MacOSX Leopard 10.5.6へ移動しコンフィグファイルを書き換えIntel Mac上で未サポート扱いのOracle HTTP Server + PL/SQL gateway + APEX3.2を使えるようにする。

まず、PowerMacの情報

gccは3.3を使うのでそれ以外の場合はgcc_selectで変更する。また、javaに関してはOracle10g R1のリリース当時はJDK1.4.2だったが、以前デフォルトをJavaSE6.0 Developer Preview 1へ切り替えたままで行うことにした。PowerPC版Macの場合JavaSE6.0は正式にリリースされていないのでJavaSE5.0までのほうが問題は少ない。。はずだが。

G5Server:˜ oracle$ sw_vers
ProductName: Mac OS X Server
ProductVersion: 10.4.11
BuildVersion: 8S169
G5Server:˜ oracle$
G5Server:˜ oracle$ uname -a
Darwin G5Server 8.11.0 Darwin Kernel Version 8.11.0: Wed Oct 10 18:26:00 PDT 2007;
root:xnu-792.24.17˜1/RELEASE_PPC Power Macintosh powerpc
G5Server:˜ oracle$
G5Server:˜ oracle$
G5Server:˜ oracle$ /usr/sbin/system_profiler SPHardwareDataType | grep CPU
CPU Type: PowerPC G5 (3.1)
Number Of CPUs: 2
CPU Speed: 2.7 GHz
L2 Cache (per CPU): 512 KB
G5Server:˜ oracle$
G5Server:˜ oracle$
G5Server:˜ oracle$ /usr/sbin/system_profiler SPApplicationsDataType | grep Xcode
Xcode:
Get Info String: Xcode version 2.4.1
Location: /Developer/Applications/Xcode.app

G5Server:˜ oracle$
G5Server:˜ oracle$ gcc --version
powerpc-apple-darwin8-gcc-4.0.1 (GCC) 4.0.1 (Apple Computer, Inc. build 5367)
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

G5Server:˜ oracle$
G5Server:˜ oracle$ gcc_select -l
Available compiler versions:
3.3 3.3-fast 4.0

G5Server:˜ oracle$
G5Server:˜ oracle$ sudo gcc_select 3.3
Password:
Default compiler has been set to:
gcc version 3.3 20030304 (Apple Computer, Inc. build 1819)
G5Server:˜ oracle$
G5Server:˜ oracle$ gcc -v
Reading specs from /usr/libexec/gcc/darwin/ppc/3.3/specs
Thread model: posix
gcc version 3.3 20030304 (Apple Computer, Inc. build 1819)
G5Server:˜ oracle$

G5Server:˜ oracle$
G5Server:˜ oracle$ java -version
java version "1.6.0-dp"
Java(TM) SE Runtime Environment (build 1.6.0-dp-b88-34)
Java HotSpot(TM) Core VM (build 1.6.0-b88-17-release, interpreted mode)
G5Server:˜ oracle$

次に、Intel Macの情報

Oracle 10g R2 10.2.0.4.0 for MacOSX(intel x86-64)インストール時のまま。

Macintosh:˜ discus$ /usr/sbin/system_profiler SPHardwareDataType | grep -E '(Processor|Cores|Memory)'
Processor Name: Intel Core 2 Duo
Processor Speed: 2.2 GHz
Number Of Processors: 1
Total Number Of Cores: 2
Memory: 2 GB
Macintosh:˜ discus$
Macintosh:˜ discus$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.5.6
BuildVersion: 9G55
Macintosh:˜ discus$
Macintosh:˜ discus$ uname -a
Darwin Macintosh.local 9.6.0 Darwin Kernel Version 9.6.0: Mon Nov 24 17:37:00
PST 2008; root:xnu-1228.9.59˜1/RELEASE_I386 i386
Macintosh:˜ discus$
Macintosh:˜ discus$ gcc --version
i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5488)
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Macintosh:˜ discus$
Macintosh:˜ discus$ java -version
java version "1.6.0_07"
Java(TM) SE Runtime Environment (build 1.6.0_07-b06-153)
Java HotSpot(TM) 64-Bit Server VM (build 1.6.0_07-b06-57, mixed mode)
Macintosh:˜ discus$
Macintosh:˜ discus$ system_profiler SPApplicationsDataType | grep Xcode
Xcode:
Get Info String: Xcode version 3.1.1
Location: /Developer/Applications/Xcode.app
Macintosh:˜ discus$

長くなるので(というより息子が風邪(フツーの)で保育園に行けない。しかも熱が37度台になってきたので走り回ってたり私が集中できないという理由もあり)今日はここまで、次回へつづく。:)

もう、そろそろ昼寝していた息子が起きそうだ...相手してやらないと・・・





バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)
Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)
Leopard de Oracle10g R2 (Intel x86-64) #11 (APEX3.2と遊ぶ #4)

| | | コメント (0) | トラックバック (0)

2009年5月22日 (金)

DB2 で PL/SQL

DB2新版はPL/SQL対応、Oracleユーザーの移行狙う@ ITmedia

へぇ〜、それじゃ、昨日作ったケンブリッジ関数もDB2 9.7で動作するようになるのか〜〜。へぇ〜。mod_plsqlというかPL/SQL gatewayはどうなんだろうか?、Oracleが提供しているPL/SQLパッケージも対応しているのだろうか? 機会があったら質問しておこうっと。

できれば他のRDBMSでもやってくれるとありがたいかも...

PL/SQL : Write Once Run Any RDBMS なんてね。

| | | コメント (0) | トラックバック (0)

2009年5月21日 (木)

PL/SQL de ケンブリッジ関数

先日、ケンブリッジ関数ネタを取り上げたついでなのでPL/SQLで作ってみた。
こんな感じかな。他の言語に比べるとアウェイ感が強いけど。時間があったらほかのでもやるかも..w

ちなみに、OracleはOracle10g R2 10.2.0.4.0で、キャラクタセットはAL32UTF8

関連記事:
ケブンッリジ関数@ どう書く?org
確かに“読めてしまう”コピペに2ch住人が「人間すげー」と驚く@ITmedia
流行りのケブンリッジ関数 - JavaScript 編@IT戦記

ストアドファンクションにしてあります。

CREATE OR REPLACE FUNCTION Cambridge( iWords IN CLOB )
RETURN CLOB
IS
words CLOB := EMPTY_CLOB();
word CLOB := EMPTY_CLOB();
nonwords CLOB := EMPTY_CLOB();
TYPE t_char_table_type IS TABLE OF CHAR(1 CHAR);
chars t_char_table_type := t_char_table_type();
charsInit t_char_table_type := t_char_table_type();
startpos PLS_INTEGER := 1;
endpos PLS_INTEGER;
l PLS_INTEGER;
endpos_nonwords PLS_INTEGER;
tempwords CLOB := EMPTY_CLOB();
done BOOLEAN;
--
FUNCTION getShuffledWord RETURN CLOB
IS
workCLOB CLOB := EMPTY_CLOB();
BEGIN
IF chars.COUNT > 0 THEN
FOR j IN chars.FIRST..chars.LAST LOOP
workCLOB := workCLOB || chars(j);
END LOOP;
END IF;
RETURN workCLOB;
END;
--
BEGIN
words := iWords;
LOOP
endpos := REGEXP_INSTR(words, '(\s+|[[:punct:]]+|$)', startpos);
EXIT WHEN endpos = 0;
--
word := DBMS_LOB.SUBSTR(words, endpos - startpos, startpos);
chars := charsInit;
--
IF DBMS_LOB.getLength(word) > 3 THEN
FOR i IN 1..DBMS_LOB.getLength(word) LOOP
chars.EXTEND();
END LOOP;
chars(chars.FIRST) := DBMS_LOB.SUBSTR(word, 1, 1);
chars(chars.LAST) := DBMS_LOB.SUBSTR(word, 1, DBMS_LOB.getLength(word));
--
FOR k IN 2..DBMS_LOB.getLength(word) - 1 LOOP
done := FALSE;
WHILE NOT done LOOP
l := ROUND(DBMS_RANDOM.VALUE(2, DBMS_LOB.getLength(word) - 1));
IF chars(l) IS NULL THEN
chars(l) := DBMS_LOB.SUBSTR(word, 1, k);
done := TRUE;
END IF;
END LOOP;
END LOOP;
word := getShuffledWord();
END IF;
--
endpos_nonwords := REGEXP_INSTR(words, '(\s+|[[:punct:]]+|$)', startpos, 1, 1);
nonwords := DBMS_LOB.SUBSTR(words, endpos_nonwords - endpos, endpos);
startpos := endpos + 1;
tempwords := tempwords || word || nonwords;
END LOOP;
RETURN tempwords;
END;
/

あたえる文字列はテーブルに格納してあります。

SCOTT> 
SCOTT> set linesize 80
SCOTT> desc wordstable
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
WORDS CLOB

SCOTT> set long 4000
SCOTT> set longchunk 400
SCOTT> select words from wordstable where id = 1;

WORDS
---------------------------------------------------------------------------
こんにちは みなさん おげんき ですか? わたしは げんき です。
この ぶんしょう は いぎりす の ケンブリッジ だいがく の けんきゅう の けっか
にんげん は もじ を にんしき する とき その さしいょ と さいご の もじさえ あっていれば
じゅんばん は めちゃくちゃ でも ちゃんと よめる という けんきゅう に もとづいて
わざと もじの じゅんばん を いれかえて あります。
どうです? ちゃんと よめちゃう でしょ?
ちゃんと よめたら はんのう よろしく


じこっう けっか。 フツー に よまめす よね?
PL/SQLをNative compileすればもっと早くなるだろうけど...

SCOTT> select cambridge(words) from wordstable where id = 1;

CAMBRIDGE(WORDS)
---------------------------------------------------------------------------
こちにんは みさなん おんげき ですか? わたしは げんき です。
この ぶょんしう は いりぎす の ケブンリッジ だいがく の けゅんきう の けっか
にげんん は もじ を にしんき する とき その さしいょ と さいご の もさじえ あてっいれば
じゅんばん は めゃくちちゃ でも ちんゃと よめる という けゅんきう に もとづいて
わざと もじの じばんゅん を いかれえて あまりす。
どうです? ちゃんと よめちゃう でしょ?
ちゃんと よためら はのんう よろしく

経過: 00:00:00.07
SCOTT>


では、次回はLeoparde de Oracle10g R2ネタに戻るとしましょうか・・・

それにしても他の言語で書かれているの見ると随分短いよね。そういえば、Python ChallengeでもPythonなら数行なのにPL/SQL無理矢理で書いていたっけ。。そうだ誰か、PostgreSQLのPL/pgSQLとかMySQLのストアドで書くような人はいないか?なぁ〜。と呟いてみる。w (2009/5/22追記)

| | | コメント (0) | トラックバック (1)

2009年5月18日 (月)

Leopard de Oracle10g R2 (Intel x86-64) #11 (APEX3.2と遊ぶ #4)

Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第四話。

前回、Rosettaの力を借りて、PowerPC版のMacへインストールしたOracle HTTP Server + PL/SQL gatewayをIntel x86版のMacへHomeディレクトリごと移動してしまえば動作するだろう。(但し、構成ファイル等は手動で手直しする必要あり)と書いたが、試してみたところ上手く動作した。Rosetta やっぱり、すげー!(Rosettaを作ったTransitive社はIBMに買収された。

手順は長くなるので次回にするが、環境と動作したスナップショットを。。。。


クリックで拡大表示します。


Ohs_apex_version1

Ohs_apex_version2


環境

Oracle HTTP ServerとPL/SQL gatewayをインストールしたMac (PowerPC)
 ---- PowerMac G5 (PowerPC G5 Dual 2.7Ghz / MacOSX Tiger Server 10.4.11)

PowerPCへインストールしたOracle HTTP ServerとPL/SQL gatewayの移動先のMac (Intel x86)
 ---- MacBook Pro (Intel Core 2 Duo 2.2Ghz / MacOSX Leopard 10.5.6)


今日はここまで、次回へつづく。:)




バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)
Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)

| | | コメント (0) | トラックバック (0)

Leopard de Oracle10g R2 (Intel x86-64) #10 (APEX3.2と遊ぶ #3)

Oracle10g R2 10.2.0.4.0 for MacOSX (Intel x68-64)でAPEX3.2と遊ぶの第三話。
前回まででOracle 10g R2 10.2.0.4.0 for MacOSX (Intel x86-64)へAPEX3.2日本語リソースまでインストールすることができた。今回はOracle 10g R2 10.2.0.4.0 for MacOSX (Intel x86-64)ではCompanion CDにも含まれておらずインストールすらできないOracle HTTP ServerとPL/SQL gatewayなんとかしてみる。(それさえなんとかできればAPEX3.2が使えるはず。。)
方法は幾つ考えられるが、まずは、”なんとかできるはず”と思っている根拠について書いておこうかと・・・・。


”なんとかできるはず”だと考えているのにはそれなりの理由がある。それは、Rosettaの存在。
以前、Oracle Instant Client 10g R1 for MacOSX(PPC) is running under Rosetta on MacOSX LeopardでもRosettaの力でPowerPC版のOracle Instant Client 10g R1がMacOSX Leopard (intel x86)上で動作したという記事を書いたことがあるが、今回もRosettaのお世話になれば実現できるはず!。

いくつか考えられる方法のうち、Oracle10g R1 10.1.0.3.0 for MacOSX (PowerPC)版のCompanion CDを使いMacOSX Leopard (intel x86)上でインストールするという方法は、随分前にも試したことがあるOracl10g Databaseのインストールと同様に上手く行かない。(以下、NGシーンの再現)
Oracle10gr1_companinon_cd_4_powerpc


上記以外で確実な方法として、PowerPC版Mac上でインストール、構成までしたバイナリを、そのままIntel x86版Macへ移動して再構成(もちろん手動でね)すれば使えるようになるはず・・・・・・・。

ということで、次回へつづく。




バックナンバー

Oracle10g R2 for MacOSX (Intel x86-64) released !!!
Leopard de Oracle10g release 2 (Intel x86-64)
Leopard de Oracle10g R2 (Intel x86-64) #1
Leopard de Oracle10g R2 (Intel x86-64) #2
Leopard de Oracle10g R2 (Intel x86-64) #3 (ちょいと寄り道)
Leopard de Oracle10g R2 (Intel x86-64) #4 (Companion CD installation)
Leopard de Oracle10g R2 (Intel x86-64) #5 (dbstart and dbshut does not work!! But....)
Leopard de Oracle10g R2 (Intel x86-64) #6 (onsがバグっている?件..)
Leopard de Oracle10g R2 (Intel x86-64) #7 (iSQL*Plusのメッセージがものすごく怖い件)
Leopard de Oracle10g R2 (Intel x86-64) #8 - (APEX3.2と遊ぶ #1)
Leopard de Oracle10g R2 (Intel x86-64) #9 - (APEX3.2と遊ぶ #2)

| | | コメント (0) | トラックバック (0)

2009年4月 2日 (木)

ちょいと迷惑だったストアドファンクション(おまけ)

〜〜〜〜し〜〜し〜〜でぃ〜〜っ。む、ぶん!
やけに耳の残っちゃうんだよな〜。あの声とアクセント

なんて関係ない話はおいといて。。と



以前、
ちょいと迷惑だったストアドファンクション
ちょいと迷惑だったストアドファンクション(つづき)

ということを書いていたが、ちょいと迷惑だったストアドファンクションの例外ハンドラーで問題解決の手がかりとするためログ出力が実装されていたらどうするの? っていうこともあるのでおまけのおまけということで書いておきます。

以前の例では例外ハンドラーをバッサリ切り捨ててしまったのですが、例外ハンドラーで問題解決の手がかりとするためのログ出力が実装されていて例外ハンドラーを取り除くことが出来ない場合、例外名無しのRAISE文を使えば解決できますよ。:)

ちょいと迷惑だったストアドファンクションの例外ハンドラーで問題解決の手がかりとするためのログ出力を実装していたらという例・・・・(赤太字部分)

尚、この例ではオープンソースのPL/SQL向けロギングフレームワークLog4PLSQLを利用している。

CREATE OR REPLACE
PACKAGE inconvenient_package
AS
TYPE employeesType IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
TYPE membersType IS RECORD (
deptNo dept.deptNo%TYPE
,deptName dept.dName%TYPE
,employees employeesType
);
FUNCTION inconvenient_function(iDeptNo IN dept.deptNo%TYPE)
RETURN membersType;
END inconvenient_package;
/
show errors


CREATE OR REPLACE
PACKAGE BODY inconvenient_package
AS
FUNCTION inconvenient_function(iDeptNo IN dept.deptNo%TYPE)
RETURN membersType
AS
members membersType;
Log4PlsqlCtx plogparam.log_ctx := plog.init(pDBMS_OUTPUT => TRUE); -- Log4PLSQL setting
BEGIN
SELECT
deptNo
,dName
INTO
members.deptNo
,members.deptName
FROM
dept
WHERE
deptNo = iDeptNo
;
--
SELECT *
BULK COLLECT INTO
members.employees
FROM
emp
WHERE
deptno = iDeptNo
;
RETURN members;
EXCEPTION
WHEN NO_DATA_FOUND THEN
plog.warn(Log4PlsqlCtx, sqlerrm()); -- Log4PLSQL warnning
RETURN NULL;
END inconvenient_function;
END inconvenient_package;
/
show errors


変更前ファンクションの例外ハンドラー部分にある"RETURN NULL;"を"RAISE;"(例外名無しのRAISE文)に変更するだけ。

CREATE OR REPLACE
PACKAGE BODY inconvenient_package
AS
FUNCTION inconvenient_function(iDeptNo IN dept.deptNo%TYPE)
RETURN membersType
AS
members membersType;
Log4PlsqlCtx plogparam.log_ctx := plog.init(pDBMS_OUTPUT => TRUE); -- Log4PLSQL setting
BEGIN
SELECT
deptNo
,dName
INTO
members.deptNo
,members.deptName
FROM
dept
WHERE
deptNo = iDeptNo
;
--
SELECT *
BULK COLLECT INTO
members.employees
FROM
emp
WHERE
deptno = iDeptNo
;
RETURN members;
EXCEPTION
WHEN NO_DATA_FOUND THEN
plog.warn(Log4PlsqlCtx, sqlerrm()); -- Log4PLSQL warnning
RAISE;
END inconvenient_function;
END inconvenient_package;
/


例外ハンドラー内で例外名を省略すると該当する例外がさらに外側のブロックへスローされます。詳細はマニュアル(Oracle Database PL/SQL Language Reference 11g Release 1 : RIASE statement)及び、Oracle Database PL/SQL Language Reference 11g Release 1 : How PL/SQL Exceptions Propagate参照のこと。

SCOTT> set linesize 132
SCOTT> set serveroutput on
SCOTT> l
1 DECLARE
2 members inconvenient_package.membersType;
3 Log4PlsqlCtx plogparam.log_ctx := plog.init(pDBMS_OUTPUT => TRUE);
4 BEGIN
5 BEGIN
6 members := inconvenient_package.inconvenient_function(99);
7 DBMS_OUTPUT.PUT_LINE(
8 'Dept name/id:'
9 ||members.deptname
10 ||'/'||TO_CHAR(members.deptno)
11 );
12 FOR i IN members.employees.FIRST..members.employees.LAST LOOP
13 DBMS_OUTPUT.PUT_LINE(' -- '||members.employees(i).ename);
14 END LOOP;
15 EXCEPTION
16 WHEN NO_DATA_FOUND THEN
17 plog.warn(Log4PlsqlCtx, sqlerrm());
18 END;
19* END;
SCOTT> /
12:35:27:02-WARN-block-->SCOTT.INCONVENIENT_PACKAGE ORA-01403: データが見つかりません。
12:35:27:03-WARN-block ORA-01403: データが見つかりません。

PL/SQLプロシージャが正常に完了しました。

SCOTT> l
1 DECLARE
2 members inconvenient_package.membersType;
3 Log4PlsqlCtx plogparam.log_ctx := plog.init(pDBMS_OUTPUT => TRUE);
4 BEGIN
5 BEGIN
6 members := inconvenient_package.inconvenient_function(10);
7 DBMS_OUTPUT.PUT_LINE(
8 'Dept name/id:'
9 ||members.deptname
10 ||'/'||TO_CHAR(members.deptno)
11 );
12 FOR i IN members.employees.FIRST..members.employees.LAST LOOP
13 DBMS_OUTPUT.PUT_LINE(' -- '||members.employees(i).ename);
14 END LOOP;
15 EXCEPTION
16 WHEN NO_DATA_FOUND THEN
17 plog.warn(Log4PlsqlCtx, sqlerrm());
18 END;
19* END;
SCOTT> /
Dept name/id:ACCOUNTING/10
-- CLARK
-- KING
-- MILLER

PL/SQLプロシージャが正常に完了しました。

SCOTT>

| | | コメント (0) | トラックバック (0)

2009年2月 6日 (金)

In Oracle Database 11g, native dynamic SQL now supports statements bigger than 32K characters

久々にOracleネタ。

Oracle11gのPL/SQLのDynamic SQLでCLOBが使えるようになったようなので試してみた。
10gまではexecute immediate文で使えるのは最大でVARCHAR2(32767)までだったワケで、ど〜〜〜〜〜〜しても(書きたくないけど・・)巨大なSQL文をダイナミックに実行しなきゃいけない人には朗報?なんでしょうね。(DWH系ではCLOBが必要なくらい巨大なクエリをダイナミックに生成するなんてこともあるんでしょうね。私は今のところ32Kを超えるクエリは書いたことないけどそれに近いのは過去1度あったかな?! という程度。)

ということで10gと11g for linux(x86)を使って確認!(ClientはMacOSX(PPC)版Instant Clientですよん

まず最初はOracle10g
execute immediate文に利用できるのはVARCHAR2(32767)が最大サイズなのでダイナミックに生成するSQL文もそのサイズに制限されちゃう。VARCHAR2(32767)を超えるとORA-06502が発生する。(尚、10gの場合でもDBMS_SQLを使えばもっと大きいサイズの動的SQLも使用できる。)

G5Server:˜ discus$ sqlplus /nolog

SQL*Plus: Release 10.1.0.3.0 - Production on 金 2月 6 1:19:22 2009

Copyright (c) 1982, 2004, Oracle. All rights reserved.

> conn scott@catfish
パスワードを入力してください:
接続されました。
SCOTT> set serveroutput on
SCOTT> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.1.0.3.0 - Prod
PL/SQL Release 10.1.0.3.0 - Production
CORE 10.1.0.3.0 Production
TNS for Linux Server: Version 10.1.0.3.0 - Production
NLSRTL Version 10.1.0.3.0 - Production

SCOTT> l
1 CREATE OR REPLACE PROCEDURE dynamic_sql_test10g(iLength IN NUMBER)
2 IS
3 vSQL VARCHAR2(32767);
4 vResult NUMBER;
5 BEGIN
6 vSQL := 'SELECT 1';
7 FOR i IN 1.. iLength LOOP
8 vSQL := vSQL || '+1';
9 END LOOP;
10 vSQL := vSQL || ' FROM DUAL';
11 EXECUTE IMMEDIATE vSQL INTO vresult;
12 DBMS_OUTPUT.PUT_LINE('Result:'||TO_CHAR(vresult));
13* END;
SCOTT> /

プロシージャが作成されました。

SCOTT> exec dynamic_sql_test10g(16374);
Result:16375

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec dynamic_sql_test10g(16375);
BEGIN dynamic_sql_test10g(16375); END;

*
行1でエラーが発生しました。:
ORA-06502: PL/SQL: 数値または値のエラー:
文字列バッファが小さすぎます。が発生しました ORA-06512:
"SCOTT.DYNAMIC_SQL_TEST10G", 行10
ORA-06512: 行1

SCOTT>

11gの場合
execute immediate文にCLOBが使えるようになったことで以下のように32K超えの動的SQLも実行できちゃうのだ。

注)
以下のコードで遊ぶときは個人で遊べるデータベースでやってくださいね。(本番データベースや多数の開発者で共有しているテスト/開発データベースでとんでもなく巨大なクエリを実行すると他の方の迷惑になるので・・・・・)

SCOTT> 
SCOTT> conn scott@lampeye
パスワードを入力してください:
接続されました。
SCOTT>
SCOTT> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production
PL/SQL Release 11.1.0.6.0 - Production
CORE 11.1.0.6.0 Production
TNS for Linux: Version 11.1.0.6.0 - Production
NLSRTL Version 11.1.0.6.0 - Production

SCOTT>
SCOTT> l
1 CREATE OR REPLACE PROCEDURE dynamic_sql_test11g(iLength IN NUMBER)
2 IS
3 vSQL CLOB;
4 vResult NUMBER;
5 BEGIN
6 vSQL := 'SELECT 1';
7 FOR i IN 1.. iLength LOOP
8 vSQL := vSQL || '+1';
9 END LOOP;
10 vSQL := vSQL || ' FROM DUAL';
11 EXECUTE IMMEDIATE vSQL INTO vresult;
12 DBMS_OUTPUT.PUT_LINE('Result:'||TO_CHAR(vresult));
13* END;
SCOTT> /

プロシージャが作成されました。

SCOTT> set serveroutput on
SCOTT> exec dynamic_sql_test11g(32767);
Result:32768

PL/SQLプロシージャが正常に完了しました。

SCOTT>
SCOTT> set timi on
SCOTT> exec dynamic_sql_test11g(1000000);
Result:1000001

PL/SQLプロシージャが正常に完了しました。

経過: 00:06:25.93
SCOTT>

| | | コメント (0) | トラックバック (0)

2008年9月13日 (土)

ちょいと迷惑だったストアドファンクション(つづき)

前回の続きです。

ところで何がちょっと迷惑だったのか?

まずは、マニュアルをご覧ください。

もうお分かりですよね!

例の関数、戻り値としてレコード型を返してくれるのはよいのですが、該当データが存在しなかった場合にNULLを返す作りになっています。そのレコード型はどのように比較すれば該当データが存在しないのか外部の仕様からは全く見えません。(当然、レコードがNULLであるか判定する関数も用意されていません。)

レコード型がNULLかテストできないので以下のようなコードは書けないのですよ!

SCOTT> set serveroutput on size 100000
SCOTT> l
1 DECLARE
2 members inconvenient_package.membersType;
3 BEGIN
4 members := inconvenient_package.inconvenient_function(99);
5 IF members IS NULL THEN
6 RAISE NO_DATA_FOUND;
7 ELSE
8 DBMS_OUTPUT.PUT_LINE('Dept name/id:'||members.deptname||'/'||TO_CHAR(members.deptno));
9 FOR i IN members.employees.FIRST..members.employees.LAST LOOP
10 DBMS_OUTPUT.PUT_LINE(' -- '||members.employees(i).ename);
11 END LOOP;
12 END IF;
13* END;
SCOTT> /
IF members IS NULL THEN
*
行5でエラーが発生しました。:
ORA-06550: 行5、列6:
PLS-00306: 'IS NULL'の呼出しで、引数の数または型が正しくありません。
ORA-06550: 行5、列3:PL/SQL: Statement ignored

利用する側からすると、該当データが存在しない場合のコードをどうかいて良いのかAPIからは判断がつかない(該当関数のソースコードを読まない限り判断できない)。
この点がちょいと迷惑だったわけ。

例の関数をそのまま利用した場合以下のようなコードを書かないと該当データが存在しないことを判定できない。
(赤太字部分は利用する関数のコード及びこの関数がアクセスしている表定義を見て判断する必要がある。面倒くさい! なんでAPI見ただけでコード書く事できないような共通関数があるの〜〜〜。)

SCOTT> l
1 DECLARE
2 members inconvenient_package.membersType;
3 BEGIN
4 members := inconvenient_package.inconvenient_function(99);
5 IF members.employees.COUNT = 0
6 OR members.deptno IS NULL
7 THEN
8 DBMS_OUTPUT.PUT_LINE('該当データが存在しません.');
9 ELSE
10 DBMS_OUTPUT.PUT_LINE(
11 'Dept name/id:'
12 ||members.deptname
13 ||'/'||TO_CHAR(members.deptno)
14 );
15 FOR i IN members.employees.FIRST..members.employees.LAST LOOP
16 DBMS_OUTPUT.PUT_LINE(' -- '||members.employees(i).ename);
17 END LOOP;
18 END IF;
19* END;
SCOTT> /
該当データが存在しません.

PL/SQLプロシージャが正常に完了しました。


SCOTT> l
1 DECLARE
2 members inconvenient_package.membersType;
3 BEGIN
4 members := inconvenient_package.inconvenient_function(10);
5 IF members.employees.COUNT = 0
6 OR members.deptno IS NULL
7 THEN
8 DBMS_OUTPUT.PUT_LINE('該当データが存在しません.');
9 ELSE
10 DBMS_OUTPUT.PUT_LINE(
11 'Dept name/id:'
12 ||members.deptname
13 ||'/'||TO_CHAR(members.deptno)
14 );
15 FOR i IN members.employees.FIRST..members.employees.LAST LOOP
16 DBMS_OUTPUT.PUT_LINE(' -- '||members.employees(i).ename);
17 END LOOP;
18 END IF;
19* END;
SCOTT> /
Dept name/id:ACCOUNTING/10
-- CLARK
-- KING
-- MILLER

PL/SQLプロシージャが正常に完了しました。

SCOTT>

例の関数を修正するとしたら、私個人の趣味だとNO_DATA_FOUND例外はそのままスローするように例外ハンドラー部分を削除しますね。
(ファンクションである必要も無さそうなのでプロシージャにして該当データ無しなどを表すステータスを返す方法を好む方も多いのですが。)

例の関数を修正しNO_DATA_FOUND例外をそのままスローする仕様に変更すれば共通関数のソースコードや関連する表定義まで追っかける必要は無くなりますよ!。w
以下、NO_DATA_FOUNDの例外ハンドラーを削除した例

CREATE OR REPLACE PACKAGE BODY inconvenient_package
AS
FUNCTION inconvenient_function(iDeptNo IN dept.deptNo%TYPE)
RETURN membersType
AS
members membersType;
BEGIN
SELECT
deptNo
,dName
INTO
members.deptNo
,members.deptName
FROM
dept
WHERE
deptNo = iDeptNo
;
--
SELECT *
BULK COLLECT INTO
members.employees
FROM
emp
WHERE
deptno = iDeptNo
;
RETURN members;
-- NO_DATA_FOUND例外ハンドラーを削除して利用する側でハンドリングさせる
END inconvenient_function;
END inconvenient_package;
/

前述のように修正すれば以下のようなコードを書くだけでよく余計なことに気を使う事もなくなる。(関数のコードやアクセスしている表定義なんて覗かなくても該当データが存在しない場合、NO_DATA_FOUND例外をキャッチするだけで済む)

SCOTT> l
1 DECLARE
2 members inconvenient_package.membersType;
3 BEGIN
4 BEGIN
5 members := inconvenient_package.inconvenient_function(10);
6 DBMS_OUTPUT.PUT_LINE(
7 'Dept name/id:'
8 ||members.deptname
9 ||'/'||TO_CHAR(members.deptno)
10 );
11 FOR i IN members.employees.FIRST..members.employees.LAST LOOP
12 DBMS_OUTPUT.PUT_LINE(' -- '||members.employees(i).ename);
13 END LOOP;
14 --
15 EXCEPTION
16 WHEN NO_DATA_FOUND THEN
17 DBMS_OUTPUT.PUT_LINE('該当データが存在しません。');
18 END;
19* END;
SCOTT> /
Dept name/id:ACCOUNTING/10
-- CLARK
-- KING
-- MILLER

PL/SQLプロシージャが正常に完了しました。

SCOTT> l
1 DECLARE
2 members inconvenient_package.membersType;
3 BEGIN
4 BEGIN
5 members := inconvenient_package.inconvenient_function(99);
6 DBMS_OUTPUT.PUT_LINE(
7 'Dept name/id:'
8 ||members.deptname
9 ||'/'||TO_CHAR(members.deptno)
10 );
11 FOR i IN members.employees.FIRST..members.employees.LAST LOOP
12 DBMS_OUTPUT.PUT_LINE(' -- '||members.employees(i).ename);
13 END LOOP;
14 --
15 EXCEPTION
16 WHEN NO_DATA_FOUND THEN
17 DBMS_OUTPUT.PUT_LINE('該当データが存在しません。');
18 END;
19* END;
SCOTT> /
該当データが存在しません。

PL/SQLプロシージャが正常に完了しました。

SCOTT>

2009/3/25追記:
この例では該当関数の例外ハンドラーを削除しただけですが、もし該当関数やプロシージャの例外ハンドラーでエラーログ(例えばLog4PLSQLとか)などでエラーなどを追跡しやすくするようなロジックを実装していたら・・・・例外ハンドラーを削除することは避けてくださいね。そのような場合は例外ハンドラー内で同じ例外をスローするように変更すべきです(プロジェクトの規約でユーザー定義例外をスローするようにしているところもあります。)
そのようにすることで該当関数から呼び出しもとへ同じ例外がスローされるようになります。具体的な例は別エントリで書く予定。

ついでなのでOTNオンラインマニュアルのURLを変更。(OTNのオンラインマニュアルは訂正が入るとリビジョンが上がりURLまで変っちゃうのが面倒なのよ。ほんと。。URL変らないように工夫してくれないかな。。

| | | コメント (0) | トラックバック (0)

2008年9月12日 (金)

ちょいと迷惑だったストアドファンクション

以下のような既存パッケージがあり、共通ファンクションがあるのでこれを利用してくださいとのこと。

仕様書には、該当データが存在する場合はユーザ定義レコード型として定義されているmembersType、該当データが存在しない場合この関数はNULLを返すとある。フムフム。・・・・・でもなんとなく怪しい匂いが。。
で、ソースを除いてみたら以下の”ような”コードだった。どこがちょいと迷惑なのか分かりますか?
(私だったらコードレビュー時に指摘して変更させちゃうんだけどね。そのプロジェクトでは此の手のちょいと迷惑なのがかなりあったのよ。w)
2008/9/12 仕様書には、以下に少々追記

以下の例で利用している表は、Scottスキーマのemp表とdept表です。

CREATE OR REPLACE PACKAGE inconvenient_package 
AS
TYPE employeesType IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
TYPE membersType IS RECORD (
deptNo dept.deptNo%TYPE
,deptName dept.dName%TYPE
,employees employeesType
);
FUNCTION inconvenient_function(iDeptNo IN dept.deptNo%TYPE)
RETURN membersType;
END inconvenient_package;
/
show errors

CREATE OR REPLACE PACKAGE PACKAGE BODY inconvenient_package
AS
FUNCTION inconvenient_function(iDeptNo IN dept.deptNo%TYPE)
RETURN membersType
IS
members membersType;
BEGIN
SELECT
deptNo
,dName
INTO
members.deptNo
,members.deptName
FROM
dept
WHERE
deptNo = iDeptNo
;
--
SELECT *
BULK COLLECT INTO
members.employees
FROM
emp
WHERE
deptno = iDeptNo
;
RETURN members;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END inconvenient_function;
END inconvenient_package;
/
show errors

次回へつづく。

| | | コメント (0) | トラックバック (0)

2008年4月26日 (土)

PL/SQL で Python Challenge Level 18 わかちゃったかも・・

Level18は以外と簡単かも〜〜、Pythonならば。と。
当然だよね。Python ChallengeなんだからPythonを使って解くことを前提に作られてるのは。。。。

しかし、今回はもしかするとPL/SQLだけで全部解けちゃうかもしれない・・・久々だ〜。一部はかなり苦労してガリガリ作り込まなければならないと思われるのだが・・・その方法で上手く解けるという自信は70%。チャレンジしてみますよ〜〜。連休中にロジックを考えとく。。。

次回へつづく。;)

| | | コメント (0) | トラックバック (0)

2008年4月17日 (木)

埋込みPL/SQL Gatewayで外部URLアクセス

Oracle11g R1 11.1.0.6.0 for Linux x86 and Oracle Application Expressという記事で埋込みPL/SQL GatewayをでPython Challengeを試してみたのだが・・・埋込みPL/SQL Gatewayを利用した場合、UTL_HTTPパッケージで外部のURLへアクセスすることはできないみたいなので、あきらめて通常のmod_plsqlでやってみるか・・・。Oracle11gからならACLで許可してやればアクセスできるのに、Oracle11gに内蔵されているXDB HTTP Serverではそれは効かないのね。。遊べると思ったのに。。orz.(Oracle11gの埋込みPL/SQL Gatewayの備忘録として残しておくか。。)

エラーメッセージは以下の通り。(Apexを3.1にするとできるようになったりして。。。2008/4/19追記)

ORA-31020:操作できません。 理由:For security reasons, ftp and http access over XDB repository is not allowed on server side

以下、埋込みPL/SQL Gatewayを利用したOracle Application Expressのスクリーンショット
9


4/18追記
どうもこれは埋込みPL/SQL GatewayというよりOracle Application Express側の影響のように思えてきた。10gのmod_plsql経由でもApexでは同じエラーだった。
ちなみに、mod_plsql経由で呼び出すだけならACL定義でアクセスするホストを許可してやるだけでOK。

以下にその時のスクリーンショットを載せておく。
答えにLevel17の答えに結びつく部分はぼかしてますよ〜〜。

19


でも何故、ApexからUTL_HTTPを利用するとXDB絡みのエラーになるんだ? 不思議すぎる。。。これは追々調べておくか。。何か手順から漏れていることがあったりして。。。。つづく。

さらに追記、やはり出来ない(11gからはできなくなったと言ったほうがいいだろう)というのが正しいようだ。10gではApexから直接UTL_HTTPパッケージからHTTPリクエスト出してfeedやらHTMLやらアクセスし放題だったのに〜〜〜、プンプンのプン。 ふ〜普通のmod_plsqlでやりますよ〜〜〜〜だ。

| | | コメント (0) | トラックバック (0)

2008年4月16日 (水)

Oracle11g R1 11.1.0.6.0 for Linux x86 and Oracle Application Express

Python Challenge Level17の記事を書く前に、やっておきたい事があったのでそちらを先に。

何をやりたかったのか? それは、APEX(Oracle Application Express)環境を作っておく事。

Python ChallengeではWeb Applicationとして処理して欲しそうな問題も多数存在する。Level17はそうしたほうが自然な感じをだと思ったので。。。(実際にはそこまでしなくても解けたのですが)
Oracle11gからApexがバンドルされたのと同時に、組み込みPL/SQL gateway機能も追加されたのでその辺りも含めて確認しておきたかった。

この組込PL/SQL Gatewayってなによ? って方もいると思うのだが、簡単に言うと、”Oracle11gのデータベース内部にHTTP Server+mod_plsqが内蔵されちゃっている”と思えばイメージしやすいんじゃないかと。

概要はこのあたりのマニュアルを読めば掴めると思います。はい。

マニュアル(英語)Understanding Oracle Application Express Architecture

データベースサーバがHTTPサーバを内蔵なんて言うといや〜な顔する人もいるだろうなぁ。なんて思ったりもします。(笑)
実際の所組込みPL/SQLゲートウェイは、外部に公開するというより開発目的など内部の閉じられた環境で利用されることがほとんどじゃないかなぁと個人的には思います。

Oracle11gをインストールした場合、OUIによるインストールでは明示的に除外しなければOracle Application Expressも同時にインストールされる。尚、埋込みPL/SQLゲートウェイを利用するためには、Oracle XML DBも必要。詳細はマニュアルに記載されているので、よ〜〜〜く読めば簡単に構成できます。
4.5 Postinstallation Tasks for Oracle Application Express

ちなみに、Oracle11g R1 1011.1.0.6.0に含まれるOracle Application Expressは、3.0.1.00.08とのこと、3.1が既にリリースされているのでアップグレードするのもいいが、今回は面倒なのでそのまま使うことにした。

では”素直に”マニュアルに従い作業を進める。。。(笑)

sysdbaで接続後、 $ORACLE_HOME/apex/apexconfを実行しadminユーザのパスワードとapexのport番号を設定してから、anonymousユーザをアンロックします。

G5Server:˜ discus$ ssh oracle@corydoras
oracle@corydoras's password:
Last login: Tue Apr 15 22:09:38 2008 from 192.168.1.19
[oracle@corydoras ˜]$
[oracle@corydoras ˜]$
[oracle@corydoras ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.6.0 - Production on 火 4月 15 22:46:50 2008

Copyright (c) 1982, 2007, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS> spo apexconfig.log
SYS> @?/apex/apxconf

PORT
----------
8080

Enter values below for the XDB HTTP listener port and the password for the Application Express ADMIN user.
Default values are in brackets [ ].
Press Enter to accept the default value.


Enter a password for the ADMIN user [] apex
Enter a port for the XDB HTTP listener [ 8080]
...changing HTTP Port

PL/SQLプロシージャが正常に完了しました。


PL/SQLプロシージャが正常に完了しました。


セッションが変更されました。

...changing password for ADMIN

PL/SQLプロシージャが正常に完了しました。


コミットが完了しました。

SYS> alter user anonymous account unlock;

ユーザーが変更されました。

SYS>

さて、お継ぎはマニュアルに記載されている通り(コピペでOKです。)外部のネットワークにアクセスする為にACLを変更しました。
(この例ではマニュアルと同様、どこでもアクセスできるようにしておきます。今回の場合は、python challengeのサイトだけアクセス許可しておけば問題ないのですが、いろいろと試したいことがあるので今回マニュアルの例をそのまま利用させてもらいました。)

SYS> l
1 DECLARE
2 ACL_PATH VARCHAR2(4000);
3 ACL_ID RAW(16);
4 BEGIN
5 -- Look for the ACL currently assigned to '*' and give FLOWS_030000
6 -- the "connect" privilege if FLOWS_030000 does not have the privilege yet.
7 SELECT ACL INTO ACL_PATH FROM DBA_NETWORK_ACLS
8 WHERE HOST = '*' AND LOWER_PORT IS NULL AND UPPER_PORT IS NULL;
9
10 -- Before checking the privilege, make sure that the ACL is valid
11 -- (for example, does not contain stale references to dropped users).
12 -- If it does, the following exception will be raised:
13 --
14 -- ORA-44416: Invalid ACL: Unresolved principal 'FLOWS_030000'
15 -- ORA-06512: at "XDB.DBMS_XDBZ", line ...
16 --
17 SELECT SYS_OP_R2O(extractValue(P.RES, '/Resource/XMLRef')) INTO ACL_ID
18 FROM XDB.XDB$ACL A, PATH_VIEW P
19 WHERE extractValue(P.RES, '/Resource/XMLRef') = REF(A) AND
20 EQUALS_PATH(P.RES, ACL_PATH) = 1;
21
22 DBMS_XDBZ.ValidateACL(ACL_ID);
23
24 IF DBMS_NETWORK_ACL_ADMIN.CHECK_PRIVILEGE(ACL_PATH, 'FLOWS_030000', 'connect')
25 IS NULL THEN
26 DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(ACL_PATH,
27 'FLOWS_030000', TRUE, 'connect');
28 END IF;
29
30 EXCEPTION
31 -- When no ACL has been assigned to '*'.
32 WHEN NO_DATA_FOUND THEN
33 DBMS_NETWORK_ACL_ADMIN.CREATE_ACL('power_users.xml',
34 'ACL that lets power users to connect to everywhere',
35 'FLOWS_030000', TRUE, 'connect');
36 DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL('power_users.xml','*');
37* END;
SYS> /

PL/SQLプロシージャが正常に完了しました。

SYS>
SYS> commit;

コミットが完了しました。

SYS>


apexの管理画面にログインするには...以下のような形式のURLでブラウザからアクセスする。もちろん、ホストはOracle11g Databaseインスタンスがあるサーバですよ。

http://host:port/apex/apex_admin

Apex_admin_1

Apex_admin_2

Apex_admin_3

Apex_admin_4

ここまでで、application expressは利用可能な状態なのだが1つ問題がある、それは英語版のままだということ。(これは以前のOracle Application Expressでも同じですね。)
ということで、日本語リソースをロードします。尚、環境変数NLS_LANGは、AL32UTF8である必要があります。(これも以前と同じなので利用したことがあれば間違うことは無いと思いますが、注意しなければならない点ですよ。うっかりミスというのもありますから。)

[oracle@corydoras ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.6.0 - Production on 火 4月 15 23:06:03 2008

Copyright (c) 1982, 2007, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS> !echo $NLS_LANG
japanese_japan.AL32UTF8

SYS> alter session set current_schema=FLOWS_030000;

セッションが変更されました。

SYS> @?/apex/builder/ja/load_ja
. ____ ____ ____ ____
. / \ | \ /\ / | /
.| || / / \ | | |
.| ||--- ---- | | |--
.| || \ / \ | | |
. \____/ | \/ \ \____ |____ \____
.
. Application Express Hosted Development Service Installation.
.........................................................

PL/SQLプロシージャが正常に完了しました。

Install Application Express applications

PL/SQLプロシージャが正常に完了しました。

APPLICATION 4420 - APEX - System Messages
Set Credentials...
Check Compatibility...
API Last Extended:20070525
Your Current Version:20070525
This import is compatible with version: 20070108
COMPATIBLE (You should be able to run this import without issues.)
Set Application ID...
...authorization schemes
...navigation bar entries
...application processes
...application items
...application level computations
...Application Tabs
...Application Parent Tabs
...Shared Lists of values
...Application Trees
...page groups
...PAGE 1.442: メッセージ
...lists
...breadcrumbs

中略

...PAGE 91.4009: 検証の作成 - 検証タイプ
...PAGE 92.4009: 標準タブの作成の確認
...PAGE 93.4009: 既存のタブ・セットの選択
...PAGE 94.4009: タブ情報の指定
...PAGE 95.4009: 親タブ・セット
...PAGE 96.4009: 新しいタブ名
...PAGE 97.4009: 表形式フォーム
...PAGE 98.4009: 検証の作成 - SQL検証
...PAGE 99.4009: SQLレポートの作成
...PAGE 100.4009: カレンダ・ページの作成
...PAGE 101.4009: ボタンのコピー
...PAGE 102.4009: リージョンの削除の確認
...PAGE 103.4009: ビルド・ステータス
...PAGE 104.4009: ビルド・チャート問合せ
...PAGE 105.4009: スクリプト・ファイルのエクスポート
...PAGE 106.4009: ビルド・チャート問合せ
...PAGE 107.4009: 作成ウィザード
...PAGE 108.4009: 作成ウィザード
...PAGE 109.4009: 作成ウィザード
...PAGE 110.4009: 作成ウィザード
...PAGE 111.4009: 作成ウィザード
...PAGE 112.4009: ツリー・ウィザード
...PAGE 113.4009: ツリー・ウィザード
...PAGE 114.4009: ツリー・ウィザード

中略

......scheme 108165525079033088.4709
...done
Adjust instance settings

PL/SQLプロシージャが正常に完了しました。


最後に、job_queue_process初期化パラメータが1以上になっているか確認します。
私も初めて気付いたがデフォルトで1000になっているんですね。びっくり。以前は1桁か2桁だったようなきがするが。

SYS> 
SYS> show parameter job_queue_processes

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
job_queue_processes integer 1000
SYS>

尚、今回はSSLの設定は行わないので早速Apexの管理ユーザログインし直し開発用作業領域と開発ユーザを作っておく。

Apex_ja_1

Apex_ja_2

Apex_ja_3

Apex_ja_4

Apex_ja_7

Apex_ja_8

Apex_ja_10

Apex_ja_9

さて、準備もできたので次へ行くか!

| | | コメント (0) | トラックバック (0)

2008年4月13日 (日)

PL/SQL で Python Challenge Level 17 #2 - 解けた!

さて、前回、解き方は見えたような気がすると書いたがいろいろな罠?に見事に引っかかったが解けました。はい。
今、Level18の問題が見えたのだが、また画像問題なのかな?

Level17は、Web Applicationを作った経験のある方ならLevel17の質問の画像自体がヒント。
なんですが、2重、3重の仕掛けがあるのでイライラするよ。ですが、基本的にはいままでの問題を解いてきたテクニックやコードを再利用すれば十分なので特に新しいことが必要でもない。。。

ということで、次回のPL/SQL で Python ChallengeはLevel17のヒントにならないヒント。でLevel17を締めくくろう。


以下のスクリーンショットは、最後の仕掛けを通り抜ける為のヒントを取り出す事に成功した場面。Level18のURLを導きだした時のスクリーンショットではありません。:)
Level17_no2

| | | コメント (0) | トラックバック (0)

2008年3月26日 (水)

PL/SQL で Python Challenge Level 17

さて、ココログのデータベースのデフラグ?作業でログインできなかったので今書いてますが。
Level 17、少々ハマったが解き方は見えた(ような気がする)ので次回はその経過でも。

Level 17は、Web Applicationを作った経験のあるかたなら案外すんなり解けるかもしれない。
問題の画像にそのすべてのヒントが!


Enjoy! Python Challenge!
(Python じゃなくても楽しめるのだ!)

| | | コメント (0) | トラックバック (0)

2008年3月24日 (月)

PL/SQL で Python Challenge Level 16完

前回の続きです

さて、Level 16のページには、"let me get this straight" というタイトルが付けられている。これ自体が大きなヒント。なのでこれ以上は何も言うまい。

問題の解くjavaアプリケーションの内容は、以前、Level 11の処理の流れを書いていたが、その3、4以外の部分は同じ。
但し、Level7で利用した Level7.getBfile()とLevel9で作成したLevel9.writeImageFile()というファンクションとプロシージャを以下のPL/SQLパッケージに纏めた。

CREATE OR REPLACE PACKAGE PYTHONCHALLENGE_UTL AS

FUNCTION getBfile
(
i_dir_name IN VARCHAR2,
i_file_name IN vARCHAR2
) RETURN BFILE;

PROCEDURE writeImageFile(
directoryName IN VARCHAR2,
fileName IN VARCHAR2,
srcImage IN OUT NOCOPY BLOB
);

END PYTHONCHALLENGE_UTL;

CREATE OR REPLACE PACKAGE BODY PYTHONCHALLENGE_UTL AS

FUNCTION getBfile
(
i_dir_name IN VARCHAR2,
i_file_name IN VARCHAR2
) RETURN bfile AS
v_file BFILE;
BEGIN
v_file := BFILENAME(UPPER(i_dir_name),i_file_name);
RETURN v_file;
END getBfile;

PROCEDURE writeImageFile(
directoryName IN VARCHAR2,
fileName IN VARCHAR2,
srcImage IN OUT NOCOPY BLOB
) IS
file utl_file.file_type;
BEGIN
file := UTL_FILE.FOPEN(UPPER(directoryName), fileName, 'w', 32767);
DBMS_LOB.OPEN(srcImage, DBMS_LOB.LOB_READONLY);

DECLARE
v_raw RAW(32767);
amount INTEGER;
offset INTEGER;
BEGIN
offset := 1;
LOOP
amount := 32767;
DBMS_LOB.READ(srcImage, amount, offset, v_raw);
UTL_FILE.PUT_RAW(file, v_raw, true);
offset := offset + amount;
END LOOP;
EXCEPTION
WHEN no_data_found THEN
NULL;
WHEN OTHERS THEN
UTL_FILE.FCLOSE(file);
DBMS_LOB.CLOSE(srcImage);
RAISE_APPLICATION_ERROR(-20001, sqlerrm());
END;

UTL_FILE.FCLOSE(file);
DBMS_LOB.CLOSE(srcImage);
END writeImageFile;

END PYTHONCHALLENGE_UTL;
/

上記のパッケージをJava Stored Procedureから利用するわけです。javaのソースコードは載せませんよ。ネタバレになっちゃいますから。

次に、デプロイしたjava classは以下のようにしてJava Stored Procedureとして定義します。

CREATE OR REPLACE PACKAGE LEVEL16 AS
FUNCTION getResult RETURN NUMBER AS
LANGUAGE JAVA NAME 'jp.macdeoracle.Level16.getResult() return java.lang.boolean';
END LEVEL16;

では、SQL*Plusから実行してみましょう!

Last login: Sun Mar 23 20:29:41 on ttyp2
Welcome to Darwin!
G5Server:˜ discus$ ssh oracle@corydoras
oracle@corydoras's password:
Last login: Sun Mar 23 20:31:49 2008 from 192.168.1.19
[oracle@corydoras ˜]$ cd python_challenge
[oracle@corydoras python_challenge]$ ll
合計 264
-rw-r--r-- 1 oracle oinstall 254006 3月 23 16:26 level16.gif

[oracle@corydoras python_challenge]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.6.0 - Production on 日 3月 23 20:58:34 2008

Copyright (c) 1982, 2007, Oracle. All rights reserved.

> conn scott/tiger
接続されました。
SCOTT> l
1 select
2 case
3 when level16.getResult() = 1 then '成功!'
4 else '失敗!'
5 end as "Level16"
6* from dual
SCOTT> /

Level16
---------
成功!

SCOTT> !ls -l
合計 504
-rw-r--r-- 1 oracle oinstall 254006 3月 23 16:26 level16.gif
-rw-r--r-- 1 oracle oinstall 239515 3月 23 21:02 level16_result.png

SCOTT>

これだけでもかなりのヒントになっているような気もしますが。。。
MacOSXのChicken of the VNC(MacOSX向けのVNC Client)経由でLinux(CentOS5)にある結果を確認!


Level16_res1

Level16_res2


ということで、Python Challenge Level 16完。 Level 17は気が向いたら載せると思います。 
Enjoy Python Challenge!

| | | コメント (0) | トラックバック (0)

2008年3月 7日 (金)

Oracle de XMLDB #26 - XMLTYPEの更新

OTN-Jで、PL/SQLでXMLTYPEのあるの要素の値を変更したり、ある要素の値を参照したいというスレッドがあったのでレスしておいた。

そういえば、XQueryやXPathを使ったOracle XMLDBネタもやっているが更新系のはやってないな(というかまだ、XMLの更新、追加、削除に関する標準が定まっていないわけだから仕方ない。)

ということで、前述したOTN-Jのネタをもう少しフラッシュアップして?!(for updateなどが抜けていたので。。)残しておく事にした。

まず、hogehoge表に以下のようなXMLを登録する。

SCOTT> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

SCOTT>
SCOTT> desc hogehoge
名前 NULL? 型
----------------------------------------- -------- ----------------------------
SEQNO NOT NULL NUMBER
XMLINFO XMLTYPE

SCOTT>
SCOTT> insert into hogehoge values(1,xmltype('<?xml version="1.0"?><addressbook>
<person id="1"><name>岡田</name><age>30</age>
<address zipcode="123456">東京都ホゲ市ホゲ町1−1</address></person><person id="2">
<name>安倍</name><age>50</age>
<address zipcode="1050011">東京都ホゲ区ホゲ田町1−1−1</address>
</person></addressbook>'));

1行が作成されました。

SCOTT> commit;

コミットが完了しました。

SCOTT>
SCOTT>
SCOTT> set timi on
SCOTT> set long 4000
SCOTT> col xmlinfo for a80
SCOTT> set linesize 132
SCOTT> set pagesize 1000
SCOTT> select * from hogehoge where seqno=1;

SEQNO XMLINFO
---------- ------------------------------------------------------------
1 <?xml version="1.0"?><addressbook><person id="1"><name>岡田<
/name><age>30</age><address zipcode="123456">東京都ホゲ市ホ
ゲ町1−1</address></person><person id="2"><name>安倍</name>
<age>50</age><address zipcode="1050011">東京都ホゲ区ホゲ田
町1−1−1</address></person></addressbook>


経過: 00:00:00.01
SCOTT>

上記のようなXML文書を対象に特定のid属性値を持つperson要素に含まれるname要素の値を変更するストアドファンクション。
(もうすこし平たく言うと、指定した名前を変更するストアドファンクション。略し過ぎか・・・)

XML文書の変更方法は他にもあるが、PL/SQLパッケージで行うにはDOM APIであるDBMS_XMLDOMパッケージを利用して行う。
尚、XMLTYPEをDBMS_XMLDOMパッケージで更新する際のポイントは、XMLTYPEインスタンスからDOMインスタンスを生成し、DOMインスタンに対して必要な更新操作行った上で、XMLTYPEインスタンスを表へ書き戻すという所。(該当部分は赤太字にしてあります。)

SCOTT> l
1 create or replace
2 function changePersonName
3 (
4 iKey in number,
5 iPersonId in varchar2,
6 iNewName in varchar2
7 ) return xmltype
8 is
9 xml xmlType;
10 personNodeList dbms_xmldom.DomNodeList;
11 personNode dbms_xmldom.DomNode;
12 nameNodeList dbms_xmldom.DomNodeList;
13 personAttrNodeMap dbms_xmldom.DomNamedNodeMap;
14 idAttrNode dbms_xmldom.DomNode;
15 wKey number;
16 wPersonId varchar2(38);
17 wNewName varchar2(60);
18 begin
19 wKey := iKey;
20 wPersonId := iPersonId;
21 wNewName := iNewName;
22
23 select xmlinfo into xml from hogehoge where seqno = wKey for update;
24
25 personNodeList := dbms_xmldom.getElementsByTagName(
26 dbms_xmldom.newDomDocument(xml)
27 ,'person'
28 );
29
30 if not dbms_xmldom.isNull(personNodeList) then
31 for i in 0..dbms_xmldom.getLength(personNodeList) loop
32 personNode := dbms_xmldom.item(personNodeList, i);
33 personAttrNodeMap := dbms_xmldom.getAttributes(personNode);
34 idAttrNode := dbms_xmldom.getNamedItem(personAttrNodeMap, 'id');
35 if dbms_xmldom.getNodeValue(idAttrNode) = wPersonId then
36 nameNodeList := dbms_xmldom.getChildrenByTagName(
37 dbms_xmldom.makeElement(personNode)
38 ,'name'
39 );
40 dbms_xmldom.setNodeValue(
41 dbms_xmldom.getFirstChild(dbms_xmldom.item(nameNodeList, 0))
42 ,wNewName
43 );
44 update hogehoge set xmlinfo = xml where seqno = wKey;
45 exit;
46 end if;
47 end loop;
48 end if;
49 return xml;
50* end;
SCOTT> /

ファンクションが作成されました。

経過: 00:00:00.04
SCOTT>
SCOTT>
SCOTT>
SCOTT> l
1 declare
2 xml xmlType;
3 begin
4 xml := changePersonName(1, '1', '福田ほげ蔵');
6 commit;
7* end;
SCOTT> /

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.03
SCOTT>
SCOTT>
SCOTT> select * from hogehoge where seqno=1;
SEQNO XMLINFO
---------- --------------------------------------------------------------------------------
1 <?xml version="1.0"?>
<addressbook>
<person id="1">
<name>福田ほげ蔵</name>
<age>30</age>
<address zipcode="123456">東京都ホゲ市ホゲ町1−1</address>
</person>
<person id="2">
<name>安倍</name>
<age>50</age>
<address zipcode="1050011">東京都ホゲ区ホゲ田町1−1−1</address>
</person>
</addressbook>


経過: 00:00:00.00
SCOTT>

もうお気づきだと思うが、Oracle11gでも発生しているこちらからは操作できないところでwhitespaceが追加されたり、されなかったりする。まあこれ自体は大抵の場合問題にはならないのだが。。

あともうひとつの話題は特定の要素の値を取り出したいということ。これだけならDBMS_XMLDOMパッケージを使わなくても、SQL/XMLを使えば簡単ですよね。
XMLQuery()関数を使って、id属性値が "1"であるperson要素以下にあるage要素の値を参照する例です。

SCOTT> set linesize 80
SCOTT> l
1 select
2 xmlquery(
3 'let $n := $xmldoc//person[@id="1"]/age/text()
4 return $n'
5 passing by value xmlinfo as "xmldoc"
6 returning content
7 ) as "age"
8* from hogehoge where seqno=1
SCOTT> /

age
--------------------------------------------------------------------------------
30

経過: 00:00:00.04
SCOTT>

ちなみに、Oracleのマニュアルは以下の辺りを参照するとよいと思いますが、DOM、XQueryやXPathに関してはXML関連のサイトもググって調べたほうがよいと思います。XMLの操作の基本を理解していれば、あとはどんなツールを使ってそれを行うかということだけですから。

マニュアル
Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス 10g R2 - DBMS_XMLDOM
Oracle Database SQLリファレンス 10g R2 - XMLQUERY

Enjoy!

| | | コメント (0) | トラックバック (0)

2007年12月21日 (金)

太った空白

書こうか迷っていたのだが、意外とハマっている方が多いらしいので書いておく事にした。
まあ、エラーメッセージがわかりずらいのでしょうがないとは思うが。

PL/SQLで開発する際に出くわす事がある。
(私もたまにやっちゃいますけどハマることはないです。こればかりは慣れるしかないですね。)

ということで太った空白のお話。

PL/SQLではマルチバイト文字の空白(全角の空白と言ったほうがわかりやすいでしょうか?)が原因で意味不明のコンパイルエラーを引き起こす。
そう、”太った空白”と書いたのはマルチバイト文字の空白。半角の空白が2つあるように見えるが全角の空白が1文字。

まずは、以下の例を見てください。

Last login: Fri Dec 21 08:25:31 on console
Welcome to Darwin!
G5Server:˜ discus$ su - oracle
Password:
G5Server:˜ oracle$ ssh oracle@corydoras
oracle@corydoras's password:
Last login: Sun Dec 16 18:02:52 2007 from 192.168.1.19
[oracle@corydoras ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.6.0 - Production on 金 12月 21 19:50:10 2007

Copyright (c) 1982, 2007, Oracle. All rights reserved.

> conn scott/tiger
接続されました。
SCOTT> create or replace function now
2 return timestamp
3 is
4 begin
5  return systimestamp;
6 end;
7 /

警告: ファンクションが作成されましたが、コンパイル・エラーがあります。

SCOTT> show errors
FUNCTION NOWのエラーです。

LINE/COL ERROR
-------- -----------------------------------------------------------------
5/3 PLS-00103: 記号""が見つかりました。 次のうちの1つが入るとき:
( begin case declare exit for goto if loop mod null pragma
raise return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge
記号"" は無視されました。

上記では、now()というストアドファンクションを作成しようとして5行目の3カラム目でコンパイルエラーが発生している。
(ストアドファンクション自体は意味の無いものなので気にしないでね。コンパイルエラーに注目してください。)

5行目の3カラム目は、"r"だなと思ったあなたは、もうすでにハマっていますよ。

実は、5行目の先頭の空白は半角の空白2文字ではなく”全角の空白1文字”なんです。

もうお分かりですよね。では、全角空白1文字を半角空白2文字に置換して再実行してみましょう。

SCOTT> list 5
5*  return systimestamp;
SCOTT> c / return/ return
5* return systimestamp;
SCOTT> l
1 create or replace function now
2 return timestamp
3 is
4 begin
5 return systimestamp;
6* end;
SCOTT> /

ファンクションが作成されました。

SCOTT> select now() from dual;

NOW()
---------------------------------------------------------------------------
07-12-21 19:56:19.046993000

SCOTT> /

| | | コメント (0) | トラックバック (0)

2007年11月22日 (木)

Oracle de XMLDB #8 - またまた少々脱線

久々にOracle11gのXML DBネタなのだが、XQueryなどのXMLマスタープロフェッショナル(データベース)絡みのネタからはまた脱線。SQL/XMLネタを一つ。

以前、Oracle10g R1/R2上でRSS FEEDをPL/SQLで取得してOracle Application Expressで表示なんてことをやっていた。
(33回も連載してたっけ。。。。)

Mac De PL/SQL RSS Reader #1
Mac De PL/SQL RSS Reader #2

それらのエントリで、参照するXML文書の文字エンコーディングとデータベースキャラクタセットが異なると文字化けを起こしてしまうHttpUriType型の仕様?のことを書いていたのだが、先日リリースされたOracle11g R1ではどうなったのか?


確かめてみた! 

まずは、SQL/XMLを利用した例。(以下は、Oracle11g R1 for Linux x86を利用。)

XML文書の文字エンコーディングはUTF-8、Oracleデータベースのキャラクタセットは、JA16SJISTILDE。

SCOTT> r
1 select *
2 from
3 xmltable(
4 'for $i in //*
5 return $i'
6 passing httpuritype('https://discus-hamburg.cocolog-nifty.com/test/atom.xml').getXML()
7* )
passing httpuritype('https://discus-hamburg.cocolog-nifty.com/test/atom.xml').getXML()
*
行6でエラーが発生しました。:
ORA-31011: XML解析に失敗しました
ORA-19202: XML処理
LPX-00244: 小なり不等号 ('<')文字の使用が無効です。 (&lt;を使用します)
Error at line 30
中にエラーが発生しました
ORA-06512: "SYS.XMLTYPE", 行5
ORA-06512: "SYS.HTTPURITYPE", 行97


SCOTT>


あ〜〜〜〜やっぱり、ダメね〜。上記はXML文書のパースエラーとして現れているのだが、その原因は文字化けなのである。


XML文書とOracleデータベースのキャラクタセットが異なる場合(前述の例では、UTF-8とSJIS)にはHttpUriType型を利用せず、UTL_HTTPパッケージを利用し、

UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');

のようにすることで文字化けせずにXML文書を取込むことができる。
尚、XML文書、データベースキャラクタセットが同一である場合(XML文書がUTF-8でデータベースキャラクタセットがAL32UTF8のような場合)には文字化けは発生しない。これは私個人の想像なのだが、HttpUriType型が裏でHTTP通信する際に利用する文字エンコーディングはデータベースキャラクタセットから導出しているのだと思われる。今のところHttpUriType型には文字エンコーディングは指定できないのでオラクルさんが方針を変えない限り文字化けの回避策は前述の2つしかないだろう。

2007/11/28追記
もしどうしてもXML文書の文字エンコーディングとデータベースキャラクタセットが異なる環境でHTTPURITYPE型を使いたいということであれば、代替策としてUTL_HTTPパッケージを利用したユーザ定義型を作成すれば可能かもしれない。
(あくまで推測。。あとで試してみるか=>TODO)

以前、DBMS_XMLPARSER.PARSE(parser, uri)を使ってFEEDを直接参照した場合も同様のエラーだったが、UTL_HTTPパッケージを利用して文字化けを回避した。
以下URL参照のこと。(下記のエントリの内容はOracle10g R1/R2で試したものです。)

Mac De PL/SQL RSS Reader #3

SQL/XML機能が充実してきたので今後はあまり使わなくなるかもしれないが、次いでなのでDBMS_XMLPARSER.PARSER(parser,uri)でも試してみた。
DBMS_XMLPARSER.PARSER(parser, uri)でもHttpUriType型を利用しているだろうから結果は同じはずだ。
(また、XMLマスタープロフェッショナル(データベース)ネタからは離れてく〜〜。w)

以前作成したコードを少々手直してOracle11gで実行!(Oracle SQL Developer for MacOSXを利用した)
Xmldb8_jdev


● ソース

CREATE OR REPLACE PROCEDURE GET_FEED(i_url IN VARCHAR2)
AS
v_url VARCHAR2(32767);
v_myParser DBMS_XMLPARSER.Parser;
v_rssDoc DBMS_XMLDOM.DomDocument;
v_xml XMLTYPE;

PROCEDURE println(str IN VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(str);
END;

PROCEDURE println
(
i_text IN CLOB
)
IS
v_tempStr VARCHAR(32767);
v_numOfPieces PLS_INTEGER;
BEGIN
v_numOfPieces := CEIL(LENGTH(i_text)/1000);
FOR i IN 1..v_numOfPieces LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(i_text, ((i-1)*1000), 1000));
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(
'println(CLOB) internal procedure:'
|| sqlerrm()
);
RAISE;
END println;

--****************************************************************************
--* Main
--****************************************************************************
BEGIN
DBMS_OUTPUT.ENABLE(200000);
println('begin...');


v_url := i_url;
IF v_url IS NULL THEN
RAISE_APPLICATION_ERROR(-20000, 'URLを指定してください。');
END IF;

v_myParser := DBMS_XMLPARSER.NEWPARSER();
DBMS_XMLPARSER.PARSE(v_myParser, v_url);
v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
v_xml := DBMS_XMLDOM.GETXMLTYPE(v_rssDoc);
println('DBMS_XMLPARSERバージョン:'||DBMS_XMLPARSER.GETRELEASEVERSION());
println('DBMS_XMLDOMバージョン :'||DBMS_XMLDOM.GETVERSION(v_rssDoc));
println(' ');
println('=== パース済みDOMから取得したRSSソース ===');
println(XmlType.GETCLOBVAL(v_xml));
println('====================');
println(' ');
DBMS_XMLPARSER.FREEPARSER(v_myParser);

println('...End');

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(
'Main Procedure:'
|| sqlerrm()
);
RAISE;

END GET_FEED;


● 結果

RSS FEED(UTF-8)をデータベースキャラクタセット(JA16SJISTILDE)から取込んだ場合。
Connecting to the database Oracle11g R1 Scott(leaffish) - JA16SJISTILDE.
ORA-31011: XML解析に失敗しました
ORA-19202: XML処理
LPX-00244: 小なり不等号 ('<')文字の使用が無効です。 (&lt;を使用します)
Error at line 30
中にエラーが発生しました
ORA-06512: "SCOTT. GET_FEED", 行161
ORA-06512: 行6
begin...
Main Procedure:ORA-31011: XML解析に失敗しました
ORA-19202: XML処理
LPX-00244: 小なり不等号 ('<')文字の使用が無効です。 (&lt;を使用します)
Error at line 30
中にエラーが発生しました
Process exited.
Disconnecting from the database Oracle11g R1 Scott(leaffish) - JA16SJISTILDE.


RSS FEED(UTF-8)をデータベースキャラクタセット(AL32UTF8)から取込んだ場合。文字エンコーディングが同じ場合は問題なし!
ところで、Oracle11g R1でも、DBMS_XMLPARSERのパージョンは10.1.0.2.0なのね〜。(^^;;;

Connecting to the database Oracle11g R1 Scott(guppy) - AL32UTF8.
begin...
DBMS_XMLPARSERバージョン:10.1.0.2.0
DBMS_XMLDOMバージョン :1.0

=== パース済みDOMから取得したRSSソース ===
<?xml version="1.0" encoding="utf-8"?>

<feed xmlns="http://www.w3.org/2005/Atom">
<title>Mac De Oracle</title>
<link rel="alternate" type="text/html" href="https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/" />
<link rel="service.post" type="application/x.atom+xml" href="http://app.cocolog-nifty.com/t/atom/weblog/blog_id=121907" title="Mac De Oracle" />
<id>tag:app.cocolog-nifty.com,2003:weblog-121907</id>
<updated>2007-11-19T09:16:39Z</updated>


・・・・中略・・・・


<summary>前回、何かが違うと感じたと書いたがそれはなにか? 実はwhitespaceに関す...</summary>
<author>
<name>discus</name>
</author>
<category term="Linux" />
<category term="Oracle" />
<category term="Oracle Database 11g" />
<category term="XML" />




</entry>

</feed>
====================

...End
Process exited.
Disconnecting from the database Oracle11g R1 Scott(guppy) - AL32UTF8.


尚、Oracle11g R1ではセキュリティが強化されている。(Oracle10g R2以前のセキュリティレベルにもできるようだが。。)上記のコードをそのまま実行しようとするとエラーになる。

ということで、セキュリティ関連も含めて続きは次回ということで。



おまけ

以下の資料はSQL/XMLって何? ってとこからスタートする方にはよいかも..。
オープンソースカンファレンス2007 .DBの資料「XQuery、XPath 及び SQL / XML に関する標準化動向と今後の取組み状況」 (PDF)

以下のサイトにはSQL/XML関連の情報がいろいろ(英語です。)
SQLX.org

その他、OTN-Jなどにもあったかも。。。(未確認)

| | | コメント (0) | トラックバック (0)

2007年10月17日 (水)

DBMS_OUTPUT.PUT_LINE()/PUT()の出力制限

Oracle10g R2からDBMS_OUTPUT.PUT_LINE()/PUT()関数で出力できる1行最大サイズが、255バイトから32767バイトへ拡張されたことはご存知だろうか?
PL/SQLでプログラミングしている方ならほとんどの方がお世話になったことがあると思うDBMS_OUTPUT.PUT_LINE()/PUT()関数。これらの関数は、Oracle7のころから、ず〜〜〜〜〜っと255バイトが最大サイズだった。
それが、Oracle10g R2になって、いきなり32767バイト(このサイズは、VARCHAR2型のPL/SQL内での最大サイズに等しい)まで拡張された。(うれしいというか、便利にというか、楽にはなったかもしれない。)
だが、いままで工夫して利用してきているだけに、意外とそのまんまになっていたり、32767バイトまで拡張されたということを知らない方も多いようなので、ネタとしてはあまり新しいとは思えないのだが取り上げておく事にした。

ということで、Oracle10g R1 10.1.0.4.0とOracle10g R2 10.2.0.1.0を利用して実際に違いを確認。

以下の無名PL/SQLブロックでは、 255バイト/256バイト/32767バイト/32768バイトの各文字列をDBMS_OUTPUT.PUT_LINE()関数で出力するコードである。
DBMS_OUTPUT.PUT_LINE()/PUT()関数の制限により、Oracle10g R2より前のリリースでは、256バイト以上の文字列を出力しようと場合/Oracle10g R2以降では32768バイト以上の文字列を出力しようとするとエラーになる。

● Oracle10g R2 10.2.0.1.0の例

32768バイトの文字列を出力しようとした場合だけがエラーになっていることが確認できる。
SCOTT> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

SCOTT> set serveroutput on size 100000
SCOTT> l
1 declare
2 str255 varchar2(255) := rpad('255',255,'*');
3 str256 varchar2(256) := rpad('256',256,'*');
4 str32767 varchar2(32767) := rpad('32767',32767,'*');
5 begin
6 dbms_output.put_line(str255);
7 begin
8 dbms_output.put_line(str256);
9 exception
10 when others then
11 dbms_output.put_line(sqlerrm());
12 end;
13 dbms_output.put_line(str32767);
14 begin
15 dbms_output.put_line(str32767||'*');
16 exception
17 when others then
18 dbms_output.put_line(sqlerrm());
19 end;
20* end;
SCOTT> /
255*****************************************************************************
********************************************************************************
********************************************************************************
***************
256*****************************************************************************
********************************************************************************
********************************************************************************
****************
32767***************************************************************************
********************************************************************************
・・・・・中略・・・・・
********************************************************************************
********************************************************************************
********************************************************************************
***********************************************
ORA-20000: ORU-10028: line length overflow, limit of 32767 bytes per line

PL/SQLプロシージャが正常に完了しました。

SCOTT>

● Oracle10g R1 10.1.0.4.0の例

256バイト以上の文字列を出力しようとした場合、全てがエラー。当然!
SCOTT> conn scott@fishtank
パスワードを入力してください:
接続されました。
SCOTT> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Prod
PL/SQL Release 10.1.0.4.0 - Production
CORE 10.1.0.4.0 Production
TNS for Linux: Version 10.1.0.4.0 - Production
NLSRTL Version 10.1.0.4.0 - Production

SCOTT> set serveroutput on size 100000
SCOTT> l
1 declare
2 str255 varchar2(255) := rpad('255',255,'*');
3 str256 varchar2(256) := rpad('256',256,'*');
4 str32767 varchar2(32767) := rpad('32767',32767,'*');
5 begin
6 dbms_output.put_line(str255);
7 begin
8 dbms_output.put_line(str256);
9 exception
10 when others then
11 dbms_output.put_line(sqlerrm());
12 end;
13 begin
14 dbms_output.put_line(str32767);
15 exception
16 when others then
17 dbms_output.put_line(sqlerrm());
18 end;
19 begin
20 dbms_output.put_line(str32767||'*');
21 exception
22 when others then
23 dbms_output.put_line(sqlerrm());
24 end;
25* end;
SCOTT> /
255*****************************************************************************
********************************************************************************
********************************************************************************
***************
ORA-20000: ORU-10028: line length overflow, limit of 255 chars per line
ORA-20000: ORU-10028: line length overflow, limit of 255 chars per line
ORA-20000: ORU-10028: line length overflow, limit of 255 chars per line

PL/SQLプロシージャが正常に完了しました。

SCOTT>

以上で、Oracle10g R2以降でのDBMS_OUTPUT.PUT_LINE()/PUT()関数の拡張は確認できた。PL/SQL内では、32767バイトがVARCHAR2型の最大サイズなので、32767バイト以内の出力であればDBMS_OUTPUT.PUT_LINE()/PUT()関数1行で済むということはかなり便利だ。(32768バイト以上の出力には今まで通りのコードが必要だが。。。)

ここまでやったついでに、以前紹介したPL/SQLの条件付きコンパイルを利用してOracle9i R2 9.2.0.6.0以降、Oracle10g R1 10.1.0.4.0以降、それに、Oracle10g R2及び、Oracle11g R1の各リリースでコード共有の例を1つ。

以下のサンプルコードは、最大32767バイトまでの文字列を引数で受け取り、DBMS_OUTPUT.PUT_LINE()で表示するという単純なコードだ。


● Oracle10g R1 10.1.0.4.0

PL/SQLの条件付きコンパイルは、Oracle10g R1ではデフォルトでは機能しない。隠しパラメータをTRUEに設定する必要がある。
SYS> show parameter _plsql_conditional

NAME TYPE VALUE
------------------------------------ ---------------------- ------------------------------
_plsql_conditional_compilation boolean TRUE
SYS> conn scott@fishtank
パスワードを入力してください:
接続されました。
SCOTT> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Prod
PL/SQL Release 10.1.0.4.0 - Production
CORE 10.1.0.4.0 Production
TNS for Linux: Version 10.1.0.4.0 - Production
NLSRTL Version 10.1.0.4.0 - Production

SCOTT> set serveroutput on size 100000
SCOTT> l
1 CREATE OR REPLACE PROCEDURE dbms_output_test
2 (
3 i_str IN VARCHAR2
4 )
5 IS
6 v_str VARCHAR2(32767);
7 $IF DBMS_DB_VERSION.VERSION <= 9
8 OR (DBMS_DB_VERSION.VERSION = 10
9 AND DBMS_DB_VERSION.RELEASE < 2)
10 $THEN
11 v_pices PLS_INTEGER;
12 C_NumOfChars CONSTANT PLS_INTEGER := 85;
13 $END
14 BEGIN
15 v_str := i_str;
16 $IF DBMS_DB_VERSION.VERSION <= 9
17 OR (DBMS_DB_VERSION.VERSION = 10
18 AND DBMS_DB_VERSION.RELEASE < 2)
19 $THEN
20 v_pices := CEIL(LENGTHB(v_str)/C_NumOfChars);
21 FOR i IN 1..v_pices LOOP
22 DBMS_OUTPUT.PUT_LINE(SUBSTR(v_str,(i-1)*C_NumOfChars+1,C_NumOfChars));
23 END LOOP;
24 $ELSE
25 DBMS_OUTPUT.PUT_LINE(v_str);
26 $END
27* END;
SCOTT> /

プロシージャが作成されました。

SCOTT>

Oracle10g R1環境で条件付きコンパイル後のコードを確認!

SCOTT> exec dbms_preprocessor.print_post_processed_source('PROCEDURE','SCOTT','DBMS_OUTPUT_TEST');
PROCEDURE dbms_output_test
(
i_str IN VARCHAR2
)
IS
v_str VARCHAR2(32767);
v_pices PLS_INTEGER;
C_NumOfChars CONSTANT PLS_INTEGER := 85;
BEGIN
v_str := i_str;
v_pices := CEIL(LENGTHB(v_str)/C_NumOfChars);
FOR i IN 1..v_pices LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(v_str,(i-1)*C_NumOfChars+1,C_NumOfChars));
END LOOP;
END;

PL/SQLプロシージャが正常に完了しました。

SCOTT>
SCOTT> exec dbms_output_test(rpad('32767',32767,'*'));
32767********************************************************************************
*************************************************************************************
*************************************************************************************
・・・・・中略・・・・・
*************************************************************************************
*************************************************************************************
******************************************

PL/SQLプロシージャが正常に完了しました。

SCOTT>

● Oracle10g R2 10.2.0.1.0

Oracle10g R2ではPL/SQLの条件付きコンパイルは最初から有効になっている。
SCOTT> set serveroutput on size 100000
SCOTT> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

SCOTT> l
1 CREATE OR REPLACE PROCEDURE dbms_output_test
2 (
3 i_str IN VARCHAR2
4 )
5 IS
6 v_str VARCHAR2(32767);
7 $IF DBMS_DB_VERSION.VERSION <= 9
8 OR (DBMS_DB_VERSION.VERSION = 10
9 AND DBMS_DB_VERSION.RELEASE < 2)
10 $THEN
11 v_pices PLS_INTEGER;
12 C_NumOfChars CONSTANT PLS_INTEGER := 85;
13 $END
14 BEGIN
15 v_str := i_str;
16 $IF DBMS_DB_VERSION.VERSION <= 9
17 OR (DBMS_DB_VERSION.VERSION = 10
18 AND DBMS_DB_VERSION.RELEASE < 2)
19 $THEN
20 v_pices := CEIL(LENGTHB(v_str)/C_NumOfChars);
21 FOR i IN 1..v_pices LOOP
22 DBMS_OUTPUT.PUT_LINE(SUBSTR(v_str,(i-1)*C_NumOfChars+1,C_NumOfChars));
23 END LOOP;
24 $ELSE
25 DBMS_OUTPUT.PUT_LINE(v_str);
26 $END
27* END;
SCOTT> /

プロシージャが作成されました。

Oracle10g R2で条件コンパイル後のコードを確認!(コードがこんなにシンプルに!)

SCOTT> exec dbms_preprocessor.print_post_processed_source('PROCEDURE','SCOTT','DBMS_OUTPUT_TEST');
PROCEDURE dbms_output_test
(
i_str IN VARCHAR2
)
IS
v_str VARCHAR2(32767);
BEGIN
v_str := i_str;
DBMS_OUTPUT.PUT_LINE(v_str);
END;

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec dbms_output_test(rpad('32767',32767,'*'));
32767***************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
・・・・・中略・・・・・
********************************************************************************
********************************************************************************
***********************************************

PL/SQLプロシージャが正常に完了しました。

SCOTT>

| | | コメント (0) | トラックバック (0)

2007年10月16日 (火)

Oracle de XMLDB #3 - Oracle10g XEではどうよ?

OracleのXMLDBを試す簡単な環境は? ということで、Oracle10g R2 Express Editionを試してみた。
だが・・・・・今のところ利用できないようだ。次のリリースに期待。

バックナンバー:
Oracle de XMLDB #1
Oracle de XMLDB #2


以下、前回、Oracle Database 11gを構築したPC(CentOS 5)に、Oracle10g R2 Express Edition for Linux x86を追加インストールして試した記録。
尚、Oracle10g EXへは、同環境へインストール済みOracle Database 11g R1のSQL*Plusから接続した。(MacOSXからssh接続しているため、その方が楽だったので・・・)

G5Server:˜ oracle$ ssh oracle@glasscatfish
oracle@glasscatfish's password:
[oracle@glasscatfish ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.6.0 - Production on 火 10月 16 0:04:43 2007

Copyright (c) 1982, 2007, Oracle. All rights reserved.

> conn sys@xe as sysdba
パスワードを入力してください:
接続されました。
SYS>
SYS> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Product
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

SYS> select comp_name,version,status from dba_registry;

COMP_NAME VERSION STATUS
-------------------------------------------------- ------------------------------ ----------
Oracle Database Catalog Views 10.2.0.1.0 VALID
Oracle Database Packages and Types 10.2.0.1.0 VALID
Oracle Text 10.2.0.1.0 VALID
Oracle XML Database 10.2.0.1.0 VALID

SYS>
SYS> !
[oracle@glasscatfish ˜]$ ll
合計 8
drwxr-xr-x 2 oracle oinstall 4096 10月 14 15:32 Desktop
[oracle@glasscatfish ˜]$ pwd
/home/oracle
[oracle@glasscatfish ˜]$ mkdir scott_xmldir
[oracle@glasscatfish ˜]$ ll
合計 16
drwxr-xr-x 2 oracle oinstall 4096 10月 14 15:32 Desktop
drwxr-xr-x 2 oracle oinstall 4096 10月 14 15:44 scott_xmldir
[oracle@glasscatfish ˜]$ cd scott_xmldir
[oracle@glasscatfish scott_xmldir]$ cat list1.xml
<?xml version="1.0" encoding="UTF-8" ?>
<顧客名簿>
<顧客 id="128">
<住所>
<郵便番号>123-4567</郵便番号>
<都道府県>千葉県</都道府県>
<市町村名>浦安市</市町村名>
<町名番地>舞浜1-2-3</町名番地>
</住所>
<電話 タイプ="会社">03-1234-5678</電話>
<電話 タイプ="自宅">03-5555-6666</電話>
<電話 タイプ="携帯">090-1112-4444</電話>
<ファックス>03-8888-7777</ファックス>
<Eメール>hoge@macdeoracle.jp</Eメール>
</顧客>
<顧客 id="256">
<住所>
<郵便番号>155-5555</郵便番号>
<都道府県>東京都</都道府県>
<市町村名>世田谷区</市町村名>
<町名番地>用賀1-2-3</町名番地>
</住所>
<電話 タイプ="会社">03-7575-7575</電話>
<電話 タイプ="自宅">03-8585-8585</電話>
<電話 タイプ="携帯">090-9191-9191</電話>
<ファックス>03-4141-4141</ファックス>
<Eメール>hoge@discushamburg.jp</Eメール>
</顧客>
<顧客 id="512">
<住所>
<郵便番号>345-4567</郵便番号>
<都道府県>大阪府</都道府県>
<市町村名>中央区</市町村名>
<町名番地>難波2-2-2</町名番地>
</住所>
<電話 タイプ="会社">06-1234-5678</電話>
<電話 タイプ="自宅">06-5555-6666</電話>
<電話 タイプ="携帯">090-0606-0606</電話>
<ファックス>06-8888-7777</ファックス>
<Eメール>hoge@lampeye.jp</Eメール>
</顧客>
</顧客名簿>
[oracle@glasscatfish scott_xmldir]$ exit
exit

SYS>
SYS> create directory scott_xmldir as '/home/oracle/scott_xmldir';

ディレクトリが作成されました。

SYS> grant read on directory scott_xmldir to scott;

権限付与が成功しました。

SYS> grant write on directory scott_xmldir to scott;

権限付与が成功しました。

SYS>

SYS>
SYS>
SYS> conn scott@xe
パスワードを入力してください:
接続されました。
SCOTT>
SCOTT> declare
2 isSuccess boolean;
3 begin
4 isSuccess := dbms_xdb.createfolder('/public/scott');
5 end;
6 /

PL/SQLプロシージャが正常に完了しました。

SCOTT>
SCOTT> l
1 declare
2 r boolean;
3 begin
4 r := dbms_xdb.createresource(
5 '/public/scott/list1.xml',
6 bfilename('SCOTT_XMLDIR','list1.xml'),
7 nls_charset_id('AL32UTF8'));
8* end;
SCOTT> /

PL/SQLプロシージャが正常に完了しました。

SCOTT>
SCOTT> xquery
2 let $n := let("/public/scott/list1.xml")
3 return $n
4 /
ERROR:
ORA-19114: XQuery式を解析中にエラーが発生しました:
ORA-06550:行1、列13:
PLS-00201: 識別子SYS.DBMS_XQUERYINTを宣言してください。
ORA-06550:行1、列7:
PL/SQL: Statement ignored


SCOTT>

この日本語のメッセージでググっても、おそらく Oracle10g R2 XE上のXMLDBに関するものはヒットしないだろうということで・・・・

SCOTT> xquery
2 let $n := fn:doc("/public/scott/list1.xml")
3 return $n
4 /
ERROR:
ORA-19114: error during parsing the XQuery expression:
ORA-06550: line 1, column 13:
PLS-00201: identifier 'SYS.DBMS_XQUERYINT' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

英語メッセージに切り替え、英語のメッセージを表示させてからググってみた。
すると、ありましたよ。US OTNに。

http://forums.oracle.com/forums/message.jspa?messageID=1280038#1280038

ということで、Express Editionでは今のところこの機能は動作しないようだ。
次のリリースに期待するしかなさそうですね。



ついに、奥さんがMacBook Pro 15inchを購入、本日届いた! これで、このCentOS5+Oracle11gの環境も初期化して、初代のiMac G5どもども下取だな、そろそろ。
さてさて、LinuxのOracle11g環境を別に作らねば。。

| | | コメント (0) | トラックバック (0)

2007年10月15日 (月)

Oracle de XMLDB #1

前回のエントリでXML Master Professional Database認定試験向けのお勉強用環境をOracle10g/11g環境で作ろうかな。と書いたが早速作ってみた。

Oracle10g R2/Oracle11g R1をインストールすればXMLDBは標準でインストールされているので特に問題になることはないと思う。

ところで、Oracle10g R2からSQL*PlusからXQueryを直接実行できるようになっているのだが、ご存知だろうか?
この例では、Oracle XML RepositoryへXML文書を登録し、SQL*PlusからXQueryを直接発行する環境を作ってみる。

参考:
Oracle XML DB開発者ガイド 10gリリース2(10.2)- 20 Oracle XML DBリポジトリのデータへのアクセス
Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス 10g リリース2(10.2)- 120 DBMS_XDB

まず、今回利用した環境の情報から。
OSは、CentOS4.4(Final) 32bit そう、今年始めに構築した、Oracle10g R2 の Linux RAC環境を利用した。 (Oracle11g R1 EEの環境もあるのだが手順は同じなので Oracle10g R2で構築した例を載せておく。)

[oracle@discus1 ˜]$ uname -svr
Linux 2.6.9-42.EL #1 Sat Aug 12 09:17:58 CDT 2006
[oracle@discus1 ˜]$ cat /etc/redhat-release
CentOS release 4.4 (Final)
[oracle@discus1 ˜]$ srvctl status database -d orcl
インスタンスorcl1はノードdiscus1で実行中です。
インスタンスorcl2はノードdiscus2で実行中です。
[oracle@discus1 ˜]$ srvctl status nodeapps -n discus1
VIPはノードで実行中です: discus1
GSDはノードで実行中です: discus1
リスナーはノードで実行中です: discus1
ONSデーモンはノードで実行中です: discus1
[oracle@discus1 ˜]$ srvctl status nodeapps -n discus2
VIPはノードで実行中です: discus2
GSDはノードで実行中です: discus2
リスナーはノードで実行中です: discus2
ONSデーモンはノードで実行中です: discus2
[oracle@discus1 ˜]$


SYS> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

SYS>
SYS> select comp_name,version,status from dba_registry where comp_name like '%XML%';

COMP_NAME VERSION STATUS
-------------------------------------------------- ------------------------------ ----------
Oracle XML Database 10.2.0.1.0 VALID

SYS>


テストに利用するXMLファイル(list1.xml)を $ORACLE_HOME/scott_xmldir以下に作成

[oracle@discus1 scott_xmldir]$ ll
合計 4
-rw-r--r-- 1 oracle dba 2078 10月 13 21:28 list1.xml
[oracle@discus1 scott_xmldir]$ pwd
/u01/app/oracle/scott_xmldir/
[oracle@discus1 scott_xmldir]$ cat list1.xml
<?xml version="1.0" encoding="UTF-8"?>
<顧客名簿>
<顧客 id="128">
<住所>
<郵便番号>123-4567</郵便番号>
<都道府県>千葉県</都道府県>
<市町村名>浦安市</市町村名>
<町名番地>舞浜1-2-3</町名番地>
</住所>
<電話 タイプ="会社">03-1234-5678</電話>
<電話 タイプ="自宅">03-5555-6666</電話>
<電話 タイプ="携帯">090-1112-4444</電話>
<ファックス>03-8888-7777</ファックス>
<Eメール>hoge@macdeoracle.jp</Eメール>
</顧客>
<顧客 id="256">
<住所>
<郵便番号>155-5555</郵便番号>
<都道府県>東京都</都道府県>
<市町村名>世田谷区</市町村名>
<町名番地>用賀1-2-3</町名番地>
</住所>
<電話 タイプ="会社">03-7575-7575</電話>
<電話 タイプ="自宅">03-8585-8585</電話>
<電話 タイプ="携帯">090-9191-9191</電話>
<ファックス>03-4141-4141</ファックス>
<Eメール>hoge@discushamburg.jp</Eメール>
</顧客>
<顧客 id="512">
<住所>
<郵便番号>345-4567</郵便番号>
<都道府県>大阪府</都道府県>
<市町村名>中央区</市町村名>
<町名番地>難波2-2-2</町名番地>
</住所>
<電話 タイプ="会社">06-1234-5678</電話>
<電話 タイプ="自宅">06-5555-6666</電話>
<電話 タイプ="携帯">090-0606-0606</電話>
<ファックス>06-8888-7777</ファックス>
<Eメール>hoge@lampeye.jp</Eメール>
</顧客>
</顧客名簿>
[oracle@discus1 scott_xmldir]$


$ORACLE_HOME/scott_xmldirをdirectoryオブジェクトとして作成。今の所読み込みだけで十分だが、書き込み権限も付与しておく。

SYS> create directory scott_xmldir as '/u01/app/oracle/scott_xmldir/';

ディレクトリが作成されました。

SYS> grant read on directory scott_xmldir to scott;

権限付与が成功しました。

SYS> grant write on directory scott_xmldir to scott;

権限付与が成功しました。

SYS>

SYS>


この例では、XMLファイルをOracle XML repositoryへ登録する。/publicフォルダ以下に scott/xmlフォルダを追加作成後、/public/scott/xml以下へ対象のXMLファイルを登録する。
尚、データベースキャラクタセットは、AL32UTF8であり、登録するXMLファイルのエンコーディングは、UTF-8とした。

まず、/publicフォルダ以下に、scottフォルダを作成する。
SYS> conn scott@orcltest
パスワードを入力してください:
接続されました。

SCOTT> declare
2 r boolean;
3 begin
4 r := dbms_xdb.createfolder('/public/scott');
5 end;
6 /

PL/SQLプロシージャが正常に完了しました。

次に、/public/scottフォルダ以下に、xmlフォルダを作成する。
SCOTT> l
1 declare
2 r boolean;
3 begin
4 r := dbms_xdb.createfolder('/public/scott/xml');
5* end;
SCOTT> /

PL/SQLプロシージャが正常に完了しました。

/public/scott/xmlフォルダ直下へSCOTT_XMLDIRディレクトリオブジェクト以下にあるxmlファイル(list1.xml)を登録し、パスを/public/scott/xml/list1.xmlとする。
SCOTT> l
1 declare
2 r boolean;
3 begin
4 r := dbms_xdb.createresource(
5 '/public/scott/xml/list1.xml',
6 bfilename('SCOTT_XMLDIR','list1.xml'),
7 nls_charset_id('AL32UTF8'));
8* end;
SCOTT> /

PL/SQLプロシージャが正常に完了しました。

SCOTT>
SCOTT>


最後に、確認の意味を含めて、XML repositoryへ登録したxmlをXQueryを利用して、SQL*Plusから直接問い合わせてみる。

SCOTT> set long 4000
SCOTT> l
1 xquery
2 let $d := fn:doc("/public/scott/xml/list1.xml")
3* return $d
SCOTT> /

Result Sequence
--------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?><顧客名簿><顧客 id="128"><住所><郵便番号>123-4567</郵便番号><都道府県>千葉県</都道府県><市町村
名>浦安市</市町村名><町名番地>舞浜1-2-3</町名番地></住所><電話 タイプ="会社">03-1234-5678</電話><電話 タイプ="自宅">03-5555-6666</電
話><電話 タイプ="携帯">090-1112-4444</電話><ファックス>03-8888-7777</ファックス><Eメール>hoge@macdeoracle.jp</Eメール></顧客><顧客 i
d="256"><住所><郵便番号>155-5555</郵便番号><都道府県>東京都</都道府県><市町村名>世田谷区</市町村名><町名番地>用賀1-2-3</町名番地></
住所><電話 タイプ="会社">03-7575-7575</電話><電話 タイプ="自宅">03-8585-8585</電話><電話 タイプ="携帯">090-9191-9191</電話><ファック
ス>03-4141-4141</ファックス><Eメール>hoge@discushamburg.jp</Eメール></顧客><顧客 id="512"><住所><郵便番号>345-4567</郵便番号><都道府
県>大阪府</都道府県><市町村名>中央区</市町村名><町名番地>難波2-2-2</町名番地></住所><電話 タイプ="会社">06-1234-5678</電話><電話 タ
イプ="自宅">06-5555-6666</電話><電話 タイプ="携帯">090-0606-0606</電話><ファックス>06-8888-7777</ファックス><Eメール>hoge@lampeye.jp
</Eメール></顧客></顧客名簿>



SCOTT>

ここまでできれば、XQuery1.0の実行は、SQL*Plusからダイレクトに行えるようになる。 簡単!。

あとは、いろいろなXQueryを試すのみ! Enjoy!

| | | コメント (0) | トラックバック (1)

2007年10月13日 (土)

DBMS_COMPARISONパッケージ で表データの比較 #6

「DBMS_COMPARISONパッケージで表データの比較」のつづき。

マニュアルは、DBMS_COMPARISONを利用するための前提が記載されているいるので、前提を満たしていないとどんなエラーになるか試してみた。

バックナンバー:
DBMS_COMPARISONパッケージ で表データの比較 #1
DBMS_COMPARISONパッケージ で表データの比較 #2
DBMS_COMPARISONパッケージ で表データの比較 #3
DBMS_COMPARISONパッケージ で表データの比較 #4
DBMS_COMPARISONパッケージ で表データの比較 #5



● もしも、2つの表に前提を満たす主キー制約が無かったら。。。。。

SCOTT> alter table emp2 drop constraint emp2_pk;

表が変更されました。

SCOTT> create table emp3 as select * from emp2;

表が作成されました。

SCOTT> begin
2 dbms_comparison.create_comparison(
3 comparison_name => '索引の無い表の比較',
4 schema_name => 'SCOTT',
5 object_name => 'EMP2',
6 dblink_name => null,
7 remote_schema_name => 'SCOTT',
8 remote_object_name => 'EMP3'
9 );
10 end;
11 /
begin
*
行1でエラーが発生しました。:
ORA-23626: 適格な索引が表SCOTT.EMP2にありません
ORA-06512: "SYS.DBMS_COMPARISON", 行4197
ORA-06512: "SYS.DBMS_COMPARISON", 行420
ORA-06512: 行2

ちなみに、ローカル表には主キー制約があり、リモート表(実際にリモートデータベーベースにある表、マテリアライズドビューである必要はない)に主キー制約が存在しない場合は、DBMS_COMPARISONパッケージは可能だった。
ただしその場合、データを比較する際リモート表が全表走査されることになるだろう(実際にトレース取ったわけではないが・・)から、主キー制約、または一意制約はあったほうがよいだろう。(この点には注意が必要だが、主キーベースのレプリケーション環境では影響はないかと。。)



そういえば、XML Master Professional Databaseが今年の12月から開始予定とのことなので、そろそろ、XML絡みのネタを増やしていこうか・・・、Oracle9i/10g さらに11gとXML DBとしての機能も強化してきたOracleなのでOracleを使ってXML Master Professional Databaseの受験対策環境作っておくのもいいなぁ・・・準備しておくか・・・

| | | コメント (0) | トラックバック (0)

DBMS_COMPARISONパッケージ で表データの比較 #5

「DBMS_COMPARISONパッケージで表データの比較」の続きです。

表の内容を比較すると比較結果がどんどん記録されていく。不要になった比較結果は削除しときましょ。

バックナンバー:
DBMS_COMPARISONパッケージ で表データの比較 #1
DBMS_COMPARISONパッケージ で表データの比較 #2
DBMS_COMPARISONパッケージ で表データの比較 #3
DBMS_COMPARISONパッケージ で表データの比較 #4



purge_comparison()drop_comparison()、関数名だけだと何を削除するのか分かりにくいな〜。ということでこれらの関数の確認も。。

● 比較結果をpurge_comparison()関数でパージする。

DBMS_COMPARISON.COMPARE()関数を実行すると、比較結果の詳細やサマリーなどが記録される。
不要になった結果はpurge_comparison()関数で、後始末。

SCOTT> select scan_id,comparison_name from user_comparison_scan order by scan_id;

SCAN_ID COMPARISON_NAME
---------- ------------------------------
1 EMP表の比較
2 EMP表の比較
3 EMP表の比較
4 EMP表の比較
5 EMP表の比較
6 EMP表の比較
7 EMP表の比較

7行が選択されました。

SCOTT> select scan_id,comparison_name from user_comparison_scan_summary order by scan_id;

SCAN_ID COMPARISON_NAME
---------- ------------------------------
1 EMP表の比較
2 EMP表の比較
3 EMP表の比較
4 EMP表の比較
5 EMP表の比較
6 EMP表の比較
7 EMP表の比較

7行が選択されました。

SCOTT> select scan_id,comparison_name from user_comparison_scan_values order by scan_id;

SCAN_ID COMPARISON_NAME
---------- ------------------------------
1 EMP表の比較
2 EMP表の比較
3 EMP表の比較
4 EMP表の比較
5 EMP表の比較
6 EMP表の比較
7 EMP表の比較

7行が選択されました。


SCOTT> select scan_id,comparison_name from user_comparison_row_dif order by scan_id;

SCAN_ID COMPARISON_NAME
---------- ------------------------------
3 EMP表の比較
4 EMP表の比較
4 EMP表の比較
6 EMP表の比較

SCOTT>

以下、指定したcomparisonに関連する比較結果全てをパージする例。
SCOTT> exec dbms_comparison.purge_comparison('EMP表の比較');

PL/SQLプロシージャが正常に完了しました。

SCOTT> select scan_id,comparison_name from user_comparison_scan order by scan_id;

レコードが選択されませんでした。

SCOTT> select scan_id,comparison_name from user_comparison_scan_summary order by scan_id;

レコードが選択されませんでした。

SCOTT> select scan_id,comparison_name from user_comparison_scan_values order by scan_id;

レコードが選択されませんでした。

SCOTT> select scan_id,comparison_name from user_comparison_row_dif order by scan_id;

レコードが選択されませんでした。

SCOTT>


● comparisonをdrop_comparison()関数で削除する。

作成したcomparisonが不要になったら、drop_comparison()関数で削除。
SCOTT> select comparison_name from user_comparison;

COMPARISON_NAME
------------------------------
EMP表の比較

SCOTT> select comparison_name,column_position from user_comparison_columns;

COMPARISON_NAME COLUMN_POSITION
------------------------------ ---------------
EMP表の比較 1
EMP表の比較 2
EMP表の比較 3
EMP表の比較 4
EMP表の比較 5
EMP表の比較 6
EMP表の比較 7
EMP表の比較 8

8行が選択されました。

SCOTT> exec dbms_comparison.drop_comparison('EMP表の比較');

PL/SQLプロシージャが正常に完了しました。

SCOTT> select comparison_name from user_comparison;

レコードが選択されませんでした。

SCOTT> select comparison_name,column_position from user_comparison_columns;

レコードが選択されませんでした。

SCOTT>

| | | コメント (0) | トラックバック (0)

2007年10月12日 (金)

DBMS_COMPARISONパッケージ で表データの比較 #4

「DBMS_COMPARISONパッケージで表データの比較」の続きです。

バックナンバー:
DBMS_COMPARISONパッケージ で表データの比較 #1
DBMS_COMPARISONパッケージ で表データの比較 #2
DBMS_COMPARISONパッケージ で表データの比較 #3




奥さんが、新しいMacBook Proを購入したので、今奥さんが使っているiMac G5とOracle Database 11g R1 + CentOS 5で遊んでいるIBM ThinkPad R52が下取り処分に。。。

あ〜〜〜、もう少しだけ遊ばせて〜〜〜! と慌てたり。。。まあ、DBMS_COMPARISONパッケージを試す程度はできそうですが。。。さて、Oracle11gで遊ぶ環境が無くなる〜。

どうしよう。。。既存の環境をつぶさなければインストールできそうにない、TurboLinux(United Linux 1.0)+Oracle9i R2/Oracle10g R1環境を潰すか。。

なんて、前置きはこれくらいにして。

● CONVERGE()関数でデータを一致させる。

EMP表の内容にEMP2表の内容を合わせるように指定して実行!。異なるデータだけが処理対象となっていることにお気づきだろうか。。
SCOTT> set serveroutput on
SCOTT> l
1 DECLARE
2 scanInfo DBMS_COMPARISON.COMPARISON_TYPE;
3 BEGIN
4 DBMS_COMPARISON.CONVERGE(
5 comparison_name => 'EMP表の比較',
6 scan_id => 2,
7 scan_info => scanInfo,
8 converge_options => DBMS_COMPARISON.CMP_CONVERGE_LOCAL_WINS

9 );
10 DBMS_OUTPUT.PUT_LINE('ScanID:'||to_char(scanInfo.scan_id));
11 DBMS_OUTPUT.PUT_LINE('Local rows merged:'||to_char(scanInfo.loc_rows_merged));
12 DBMS_OUTPUT.PUT_LINE('Remote rows merged:'||to_char(scanInfo.rmt_rows_merged));
13 DBMS_OUTPUT.PUT_LINE('Local rows deleted:'||to_char(scanInfo.loc_rows_deleted));
14 DBMS_OUTPUT.PUT_LINE('Remote rows deleted:'||to_char(scanInfo.rmt_rows_deleted));
15* END;
SCOTT> /
ScanID:2
Local rows merged:0
Remote rows merged:2
Local rows deleted:0
Remote rows deleted:1

PL/SQLプロシージャが正常に完了しました。

SCOTT>
SCOTT> select * from emp order by empno;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7000 PLECO MANAGER 7839 07-10-06 2500 30
7369 SMITH CLERK 7902 80-12-17 800 20
7499 ALLEN SALESMAN 7698 81-02-20 1600 300 30
7521 WARD SALESMAN 7698 81-02-22 1250 500 30
7566 JONES MANAGER 7839 81-04-02 2975 20
7654 MARTIN SALESMAN 7698 81-09-28 1250 1400 30
7698 BLAKE MANAGER 7839 81-05-01 2850 30
7782 CLARK MANAGER 7839 81-06-09 2450 10
7788 SCOTT ANALYST 7566 87-04-19 3000 20
7839 KING PRESIDENT 81-11-17 5000 10
7844 TURNER SALESMAN 7698 81-09-08 1500 0 30
7876 ADAMS CLERK 7788 87-05-23 1100 20
7900 JAMES CLERK 7698 81-12-03 950 30
7902 FORD ANALYST 7566 81-12-03 3000 20
7934 MILLER CLERK 7782 82-01-23 1300 10
9999 DISCUS ANALYST 7839 07-10-06 3000 20

16行が選択されました。

SCOTT> select * from emp2 order by empno;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7000 PLECO MANAGER 7839 07-10-06 2500 30
7369 SMITH CLERK 7902 80-12-17 800 20
7499 ALLEN SALESMAN 7698 81-02-20 1600 300 30
7521 WARD SALESMAN 7698 81-02-22 1250 500 30
7566 JONES MANAGER 7839 81-04-02 2975 20
7654 MARTIN SALESMAN 7698 81-09-28 1250 1400 30
7698 BLAKE MANAGER 7839 81-05-01 2850 30
7782 CLARK MANAGER 7839 81-06-09 2450 10
7788 SCOTT ANALYST 7566 87-04-19 3000 20
7839 KING PRESIDENT 81-11-17 5000 10
7844 TURNER SALESMAN 7698 81-09-08 1500 0 30
7876 ADAMS CLERK 7788 87-05-23 1100 20
7900 JAMES CLERK 7698 81-12-03 950 30
7902 FORD ANALYST 7566 81-12-03 3000 20
7934 MILLER CLERK 7782 82-01-23 1300 10
9999 DISCUS ANALYST 7839 07-10-06 3000 20

16行が選択されました。

SCOTT>

となっており、2表の内容は一致したようだ。(黙視しただけなので、RECHECK()関数でも確認してみることに。。。)


● RECHECK()関数で再度比較する。

前回の比較時に返されたscan_idとcomparison名を指定して再チェックするだけ。
SCOTT> l
1 BEGIN
2 DBMS_OUTPUT.PUT_LINE('======== 再比較結果 ========');
3 IF DBMS_COMPARISON.RECHECK('EMP表の比較', 2, true) THEN
4 DBMS_OUTPUT.PUT_LINE('EMP表とEMP2表の内容は同じです。');
5 ELSE
6 DBMS_OUTPUT.PUT_LINE('EMP表とEMP2表の内容に違いがあります。');
7 END IF;
8* END;
SCOTT> /
======== 再比較結果 ========
EMP表とEMP2表の内容は同じです。

PL/SQLプロシージャが正常に完了しました。

SCOTT>

2つの表の内容は一致しました!。めでたしめでたし!


ちょ〜〜〜〜〜っと待った〜〜〜〜〜っ!

マニュアルでは、RECHECK()関数は、2つの表の内容に違いが無かった場合FALSEを返すと記載されています。COMPARE()関数とは逆と記載されています。

しか〜〜〜し。Oracle Database 11g 11.1.0.6.0では、RECHECK()を実行し、2つの表の内容が同一の場合、COMPARE()関数と同様に、TRUEを返してきます。

そうなんです、マニュアルバグのようです!。
いずれ修正されると思うので現時点のマニュアルバグの証拠写真をカシャっ!

Dbms_comparison_manual_bug

以下はマニュアルからの引用


Return Values

This function returns TRUE when differences are found in the database objects being compared. This function returns FALSE when no differences are found in the database objects being compared.

となっており、違いがあれば、TRUEを、同じであれば、FALSEを返すと書かれています。皆さんご注意を。


また、RECHECK()関数は、同一か否かを判定するだけで、どのデータが違うといった情報までは取得していない。データが一致しているか一致していないかだけしか確認できない。どのデータが一致していないのかを確認するには、再度COMPARE()関数を実行する必要がある。とのこと。間違いやすいので要注意ですな〜〜。


Note:
This function does not identify new differences in the database objects that have appeared since the specified scan was run. To identify new differences, run the COMPARE function in this package.

| | | コメント (0) | トラックバック (0)

2007年10月11日 (木)

DBMS_COMPARISONパッケージ で表データの比較 #3

DBMS_COMPARISONパッケージで表データの比較の続きです。

バックナンバー:
DBMS_COMPARISONパッケージ で表データの比較 #1
DBMS_COMPARISONパッケージ で表データの比較 #2



前回は、2つの表を比較し内容が同一であることを確認した。というところまでだった。今回は、emp/emp2の内容が異なっている場合の例を。

● まず、emp表、emp2表で内容の一致しないデータを準備しておく。

SCOTT> 
SCOTT> insert into emp values(9999,'DISCUS','ANALYST',7839,sysdate,3000,null,20);

1行が作成されました。

SCOTT> insert into emp2 values(8888,'NEON','CLERK',7839,sysdate,1300,null,20);

1行が作成されました。

SCOTT> insert into emp values(7000,'PLECO','MANAGER',7839,sysdate,2500,null,30);

1行が作成されました。

SCOTT> insert into emp2 values(7000,'PLEGO','MANAGER',7839,sysdate,2500,null,30);

1行が作成されました。

SCOTT> commit;

コミットが完了しました。

SCOTT>

● COMPARE関数で比較してみる。

SCOTT> l
1 DECLARE
2 isSame BOOLEAN;
3 scanInfo DBMS_COMPARISON.COMPARISON_TYPE;
4 BEGIN
5 isSame := DBMS_COMPARISON.COMPARE(
6 comparison_name => 'EMP表の比較'
7 ,scan_info => scanInfo
8 ,perform_row_dif => true
9 );
10 DBMS_OUTPUT.PUT_LINE('========= 結果 =======');
11 DBMS_OUTPUT.PUT_LINE('scan_id='||TO_CHAR(scanInfo.scan_id));
12 IF isSame THEN
13 DBMS_OUTPUT.PUT_LINE('EMP表とEMP2表の内容は同じです。');
14 ELSE
15 DBMS_OUTPUT.PUT_LINE('EMP表とEMP2表の内容に違いがあります。');
16 END IF;
17* END;
SCOTT> /
========= 結果 =======
scan_id=2
EMP表とEMP2表の内容に違いがあります。

PL/SQLプロシージャが正常に完了しました。

SCOTT>

● user_comparison_scan_summaryビューや、user_comparison_row_difビューなどで比較結果を確認。

尚、2票間に同一キーを持つデータは存在するがキー以外の列値が異なっている場合、どの列値が異なっているか? というところまで確認することはできない。
SCOTT> l
1 select
2 c.comparison_name,
3 s.scan_id,
4 s.current_dif_count
5 from
6 user_comparison c join user_comparison_scan_summary s
7 on c.comparison_name = s.comparison_name
8 where
9* s.scan_id = 2
SCOTT> /

COMPARISON_NAM SCAN_ID CURRENT_DIF_COUNT
-------------- ---------- -----------------
EMP表の比較 2 3

SCOTT>
SCOTT> l
1 select
2 s.root_scan_id as scan_id,
3 s.comparison_name,
4 d.index_value,
5 s.status,
6 case
7 when local_rowid is not null
8 and remote_rowid is not null then
9 'ローカル表、リモート表に同一キーを持つ行が存在しますが、キー以外で列値に違いがあります。'
10 when local_rowid is null
11 and remote_rowid is not null then
12 'ローカル表に存在しない行がリモート表に存在します。'
13 when local_rowid is not null
14 and remote_rowid is null then
15 'ローカル表に存在する行がリモート表に存在しません。'
16 end as description
17 from
18 user_comparison_scan_summary s join user_comparison_row_dif d
19 on s.scan_id = d.scan_id
20 where
21 s.root_scan_id = 2
22 and s.parent_scan_id is not null
23 order by
24* d.index_value
SCOTT> /

SCAN_ID COMPARISON_NAME INDEX_VAL STATUS DESCRIPTION
---------- -------------------- --------- ---------------- ---------------------------------------------------------------------------
2 EMP表の比較 7000 ROW DIF ローカル表、リモート表に同一キーを持つ行が存在しますが、キー以外で列値に違いがあります。
2 EMP表の比較 8888 ROW DIF ローカル表に存在しない行がリモート表に存在します。
2 EMP表の比較 9999 ROW DIF ローカル表に存在する行がリモート表に存在しません。

SCOTT>

今日はここまで。
次回は、2つの表の内容を一致させてから再度比較してみる。

| | | コメント (0) | トラックバック (0)

2007年10月10日 (水)

DBMS_COMPARISONパッケージ で表データの比較 #2

DBMS_COMPARISONパッケージで表データの比較の続きです。

バックナンバー:
DBMS_COMPARISONパッケージ で表データの比較 #1



DBMS_COMPARISONパッケージを利用し2つの表を比較!。(今回は、同一データベース、同一スキーマ内でテーブルの内容を比較してみることにする。)


● 表データを比較する手順

COMPARISONを作成する。

(比較仕様を作成する。 とでも書いたほうがわかりやすいか?。。。)

・作成したCOMPARISONを元に比較する

(比較仕様を元に比較を実行する。 とでも書いたほうがわかりやすいか?。。。)




では、比較実験開始!

emp表を複製してemp2表を作成する。(まずは、表、索引まで完全に複製して比較。)

SCOTT> create table emp2 as select * from emp;

表が作成されました。

SCOTT>
SCOTT> alter table emp2 add constraint emp2_pk primary key (empno);

表が変更されました。

SCOTT>
SCOTT> select * from emp order by empno;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7369 SMITH CLERK 7902 80-12-17 800 NULL 20
7499 ALLEN SALESMAN 7698 81-02-20 1600 300 30
7521 WARD SALESMAN 7698 81-02-22 1250 500 30
7566 JONES MANAGER 7839 81-04-02 2975 NULL 20
7654 MARTIN SALESMAN 7698 81-09-28 1250 1400 30
7698 BLAKE MANAGER 7839 81-05-01 2850 NULL 30
7782 CLARK MANAGER 7839 81-06-09 2450 NULL 10
7788 SCOTT ANALYST 7566 87-04-19 3000 NULL 20
7839 KING PRESIDENT NULL 81-11-17 5000 NULL 10
7844 TURNER SALESMAN 7698 81-09-08 1500 0 30
7876 ADAMS CLERK 7788 87-05-23 1100 NULL 20
7900 JAMES CLERK 7698 81-12-03 950 NULL 30
7902 FORD ANALYST 7566 81-12-03 3000 NULL 20
7934 MILLER CLERK 7782 82-01-23 1300 NULL 10

14行が選択されました。

SCOTT> select * from emp2 order by empno;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7369 SMITH CLERK 7902 80-12-17 800 NULL 20
7499 ALLEN SALESMAN 7698 81-02-20 1600 300 30
7521 WARD SALESMAN 7698 81-02-22 1250 500 30
7566 JONES MANAGER 7839 81-04-02 2975 NULL 20
7654 MARTIN SALESMAN 7698 81-09-28 1250 1400 30
7698 BLAKE MANAGER 7839 81-05-01 2850 NULL 30
7782 CLARK MANAGER 7839 81-06-09 2450 NULL 10
7788 SCOTT ANALYST 7566 87-04-19 3000 NULL 20
7839 KING PRESIDENT NULL 81-11-17 5000 NULL 10
7844 TURNER SALESMAN 7698 81-09-08 1500 0 30
7876 ADAMS CLERK 7788 87-05-23 1100 NULL 20
7900 JAMES CLERK 7698 81-12-03 950 NULL 30
7902 FORD ANALYST 7566 81-12-03 3000 NULL 20
7934 MILLER CLERK 7782 82-01-23 1300 NULL 10

14行が選択されました。

SCOTT>


create_comparison関数でcomparisonを作成する。
2つの表(empとemp2)は、どちらも同一データベースのSCOTTスキーマに存在するため、dblink_name引数にはnullを指定する。

SCOTT> l
1 begin
2 dbms_comparison.create_comparison(
3 comparison_name => 'EMP表の比較'
4 ,schema_name => 'SCOTT'
5 ,object_name => 'EMP'
6 ,dblink_name => null
7 ,remote_schema_name => 'SCOTT'
8 ,remote_object_name => 'EMP2'
9 );
10* end;
SCOTT> /

PL/SQLプロシージャが正常に完了しました。

SCOTT>

作成されたcomparisonを確認するには、user_comparisonビュー/user_comparison_columnsビュー問い合わせる

SCOTT> l
1 select
2 comparison_name
3 ,scan_mode
4 ,schema_name||'.'||object_name||'(type='||object_type||')' as local
5 ,remote_schema_name||'.'||remote_object_name||'(type='||remote_object_type||')' as remote
6 ,dblink_name
7 ,last_update_time
8 from
9 user_comparison
10 where
11* comparison_name = 'EMP表の比較'
SCOTT> /

COMPARISON_NAME SCAN_MODE LOCAL REMOTE DBLINK_NAME LAST_UPDATE_TIME
--------------- --------- -------------------------- -------------------------- ----------- ------------------------------
EMP表の比較 FULL SCOTT.EMP(type=TABLE) SCOTT.EMP2(type=TABLE) NULL 07-10-06 15:58:36.665370

SCOTT>
SCOTT> select * from user_comparison_columns order by column_position;

COMPARISON_NAME COLUMN_POSITION COLUMN_NAME I
------------------------------ --------------- ------------------------------ -
EMP表の比較 1 EMPNO Y
EMP表の比較 2 ENAME N
EMP表の比較 3 JOB N
EMP表の比較 4 MGR N
EMP表の比較 5 HIREDATE N
EMP表の比較 6 SAL N
EMP表の比較 7 COMM N
EMP表の比較 8 DEPTNO N

8行が選択されました。

SCOTT>

compare関数を利用して比較する。

事前に作成されたcomparisonを元に、compare関数を利用して比較を行う。この時、比較処理事に採版されるscan_idを記録しておくとよいだろう。
scan_idは、内容に相違がある場合、その詳細をディクショナリビューから問い合わせたり、再比較する際に利用する。

SCOTT> set serveroutput on format wrapped
SCOTT> l
1 DECLARE
2 isSame BOOLEAN;
3 scanInfo DBMS_COMPARISON.COMPARISON_TYPE;
4 BEGIN
5 isSame := DBMS_COMPARISON.COMPARE(
6 comparison_name => 'EMP表の比較'
7 ,scan_info => scanInfo
8 ,perform_row_dif => true
9 );
10 DBMS_OUTPUT.PUT_LINE('========= 結果 =======');
11 DBMS_OUTPUT.PUT_LINE('scan_id='||TO_CHAR(scanInfo.scan_id));
12 IF isSame THEN
13 DBMS_OUTPUT.PUT_LINE('EMP表とEMP2表の内容は同じです。');
14 ELSE
15 DBMS_OUTPUT.PUT_LINE('EMP表とEMP2表の内容に違いがあります。');
16 END IF;
17* END;
SCOTT> /
========= 結果 =======
scan_id=1
EMP表とEMP2表の内容は同じです。

PL/SQLプロシージャが正常に完了しました。

SCOTT>

ということで、次回は表の内容が異なっている場合はどうなるかという例を。

| | | コメント (0) | トラックバック (0)

2007年10月 8日 (月)

DBMS_COMPARISONパッケージ で表データの比較 #1

Oracle Database 11gで新たに追加されたDBMS_COMPARISONパッケージ
このパッケージは2つのTable/Single-table view/Materialized view(シノニムも可)を比較したり、データを一致させたりするための機能を提供するパッケージだ。
レプリケーション関連のマニュアルへのリンクもあるのでレプリケーション環境を意識した機能ではあるようだが、単に2つの表の内容を比較したり一致させることにも利用できるのでレプリケーション環境以外でも利用できる。

ただし、マニュアルに記載されているように利用できるデータ型や索引などの制限があるので、利用に際して事前調査は必要でしょうね。


参考:
Oracle Database PL/SQL Packages and Types Reference 11g Release 1 (11.1.) 31 DBMS_COMPARISON

ということで、早速試してみる。


環境は、先日作成したIBM Thinkpad R52 のCentOS 5上に構築したOracle Database 11g R1 11.1.0.6.0 for Linux (x86)を利用した。)

例によって、MacOSXのTerminalからssh経由で利用した。
(CentOS5/Oracle11gともエンコーディングにUTF-8を利用しているため、TerminalのエンコーティングもUTF-8に設定した。


Dbms_comparison001

まずは、Oracle Database 11gから新規追加されたDBMS_COMPARISONパッケージ関連のディクショナリービューの確認。(尚、DBA_*ビューも追加されている。)

[oracle@glasscatfish ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.6.0 - Production on 月 10月 8 08:50:38 2007

Copyright (c) 1982, 2007, Oracle. All rights reserved.

> conn scott/tiger
接続されました。
SCOTT>
SCOTT>
SCOTT> desc user_comparison_scan
名前 NULL? 型
----------------------------------------- -------- ----------------------------
COMPARISON_NAME NOT NULL VARCHAR2(30)
SCAN_ID NOT NULL NUMBER
PARENT_SCAN_ID NUMBER
STATUS VARCHAR2(16)
COUNT_ROWS NUMBER
SCAN_NULLS VARCHAR2(1)
LAST_UPDATE_TIME TIMESTAMP(6)


SCOTT> desc user_comparison_scan_summary
名前 NULL? 型
----------------------------------------- -------- ----------------------------
COMPARISON_NAME NOT NULL VARCHAR2(30)
SCAN_ID NOT NULL NUMBER
PARENT_SCAN_ID NUMBER
ROOT_SCAN_ID NUMBER
STATUS VARCHAR2(16)
CURRENT_DIF_COUNT NUMBER
INITIAL_DIF_COUNT NUMBER
COUNT_ROWS NUMBER
SCAN_NULLS VARCHAR2(1)
LAST_UPDATE_TIME TIMESTAMP(6)

SCOTT> desc user_comparison_scan_values
名前 NULL? 型
----------------------------------------- -------- ----------------------------
COMPARISON_NAME NOT NULL VARCHAR2(30)
SCAN_ID NOT NULL NUMBER
COLUMN_POSITION NOT NULL NUMBER
MIN_VALUE VARCHAR2(4000)
MAX_VALUE VARCHAR2(4000)
LAST_UPDATE_TIME TIMESTAMP(6)

SCOTT> desc user_comparison_row_dif
名前 NULL? 型
----------------------------------------- -------- ----------------------------
COMPARISON_NAME NOT NULL VARCHAR2(30)
SCAN_ID NOT NULL NUMBER
LOCAL_ROWID ROWID
REMOTE_ROWID ROWID
INDEX_VALUE VARCHAR2(4000)
STATUS VARCHAR2(3)
LAST_UPDATE_TIME TIMESTAMP(6)

SCOTT>
SCOTT> desc user_comparison
名前 NULL? 型
----------------------------------------- -------- ----------------------------
COMPARISON_NAME NOT NULL VARCHAR2(30)
COMPARISON_MODE VARCHAR2(5)
SCHEMA_NAME VARCHAR2(30)
OBJECT_NAME VARCHAR2(30)
OBJECT_TYPE VARCHAR2(17)
REMOTE_SCHEMA_NAME VARCHAR2(30)
REMOTE_OBJECT_NAME VARCHAR2(30)
REMOTE_OBJECT_TYPE VARCHAR2(17)
DBLINK_NAME VARCHAR2(128)
SCAN_MODE VARCHAR2(9)
SCAN_PERCENT NUMBER
CYCLIC_INDEX_VALUE VARCHAR2(4000)
NULL_VALUE VARCHAR2(4000)
LOCAL_CONVERGE_TAG RAW(2000)
REMOTE_CONVERGE_TAG RAW(2000)
MAX_NUM_BUCKETS NUMBER
MIN_ROWS_IN_BUCKET NUMBER
LAST_UPDATE_TIME TIMESTAMP(6)

SCOTT>
SCOTT> desc user_comparison_columns
名前 NULL? 型
----------------------------------------- -------- ----------------------------
COMPARISON_NAME NOT NULL VARCHAR2(30)
COLUMN_POSITION NOT NULL NUMBER
COLUMN_NAME NOT NULL VARCHAR2(30)
INDEX_COLUMN VARCHAR2(1)


DBMS_COMPARISIONパッケージを利用するには、EXECUTEオブジェクト権限、及び、EXECUTE_CATALOG_ROLEロール(データディクショナリ内のオブジェクトに対するEXECUTE/SELECT権限)を実行するユーザへ付与する必要がある

SCOTT> conn / as sysdba
接続されました。
SYS> grant execute on dbms_comparison to scott;

権限付与が成功しました。

SYS> grant execute_catalog_role to scott;

権限付与が成功しました。

SYS> conn scott/tiger
接続されました。
SCOTT> desc dbms_comparison
FUNCTION COMPARE RETURNS BOOLEAN
引数名 タイプ In/Out Default?
------------------------------ ----------------------- ------ --------
COMPARISON_NAME VARCHAR2 IN
SCAN_INFO RECORD OUT
SCAN_ID NUMBER OUT
LOC_ROWS_MERGED NUMBER OUT
RMT_ROWS_MERGED NUMBER OUT
LOC_ROWS_DELETED NUMBER OUT
RMT_ROWS_DELETED NUMBER OUT
MIN_VALUE VARCHAR2 IN DEFAULT
MAX_VALUE VARCHAR2 IN DEFAULT
PERFORM_ROW_DIF BOOLEAN IN DEFAULT

・・・・・・・中略・・・・・・・

FUNCTION RECHECK RETURNS BOOLEAN
引数名 タイプ In/Out Default?
------------------------------ ----------------------- ------ --------
COMPARISON_NAME VARCHAR2 IN
SCAN_ID NUMBER IN
PERFORM_ROW_DIF BOOLEAN IN DEFAULT

SCOTT>

これでDBMS_COMPARISONパッケージを利用する準備は整った。というところで本日は終わり。

次回へ続く。

| | | コメント (0) | トラックバック (0)

2007年9月15日 (土)

MERGE文 #1 - 重複行の削除

ははは、久々のOracleネタです。

最近、OTN-Jのフォーラムを閲覧したり返信したりする回数を控えている。Feedの概要だけ眺めている事が多くなってきていることも影響しているが、興味をそそる内容のものが少ないことや、本家US OTNのフォーラムOracle Aceの方々のブログのほうが面白いのもその理由の一つ)

で、しばらく振りで、面白そうなネタだったのでレスを付けた。
ちょいと前のネタになるが、

定義の同じ2つの表のデータを纏めて表示したい。ただし、重複データを除く。また、重複していた件数も取得したい。。。。

というスレッド。

ただ質問内容を読むと、最近よくある実行環境(Oracleのバージョンなど)を記述せず、●●したいというタイプの質問。
(単に書き忘れかもしれないが、問題が発生している環境やOracleのバージョンを書かずに意図した答えを待ってるなんて時間がもったいないじゃん。そこんとこをちゃんと公開していれば、もっと早く意図した解答得られたと思うが。。)
そして、最後まで、実行環境に関する情報は未提示のままスレッドは終わるのであった〜〜(笑)

ということで質問内容など詳細は下記URLで
http://otn.oracle.co.jp/forum/message.jspa?messageID=35016369&tstart=15

私がレスした内容は以下のようなもの。


(質問内容のカラム名が ”日本語”だったので例題も引用識別子で囲んだ日本語の表/列名にしてあります。私は実際の開発では使いませんけどね、日本語オブジェクト名なんて。。


主キー制約や一意制約、その他の制約などが提示されていないので勝手に作ってありますが、merge文を使えば出来ますよ。
尚、Oracle10gを利用していものと仮定していますので、Oracle9i以前である場合、merge文でdeleteを実施することはできません。

また、暗黙カーソルを利用していますので、sql%rowcount属性で重複行数(マージした行数)を取得しています。
(明示カーソルにした場合は、カーソル名%rowcount)

他の方法もあると思いますので、あとは処理速度等比較し、よい方を採用すれば良いと思います。

詳細は、SQLや、PL/SQLリファレンスマニュアルの merge文及び、カーソル属性あたりを確認してみてください。

SCOTT> create table "テーブルA"
2 ("商品コード" char(5) not null,
3 "送品年月日" date not null,
4 "個数" number(4) not null
5 );

表が作成されました。

SCOTT> create table "テーブルB" as select * from "テーブルA";

表が作成されました。

SCOTT>

データの登録・・・・中略・・・・

SCOTT> alter session set nls_date_format = 'yyyy/mm/dd';

セッションが変更されました。

SCOTT> select * from "テーブルA";

商品コード 送品年月日 個数
-------- ---------- ----------
00001 2007/07/18 5
00002 2007/07/18 6
00003 2007/07/19 7
00004 2007/07/18 5
00005 2007/07/20 4
00001 2007/07/19 8
00002 2007/07/19 2
00003 2007/07/20 0

8行が選択されました。

SCOTT> select * from "テーブルB";

商品コード 送品年月日 個数
-------- ---------- ----------
00001 2007/08/18 5
00002 2007/08/18 6
00003 2007/07/19 9
00004 2007/08/18 5
00005 2007/08/20 4
00001 2007/07/19 10
00002 2007/08/19 2
00003 2007/08/20 0

8行が選択されました。


SCOTT> set serveroutput on
SCOTT> l
1 begin
2 merge
2 into "テーブルA" dest
3 using "テーブルB" src
4 on (
5 dest."商品コード"=src."商品コード"
6 and dest."送品年月日"=src."送品年月日"
7 )
8 when matched then update set dest."個数"=-1
9 delete where dest."個数"=-1;
10 dbms_output.put_line(sql%rowcount);
11* end;
SCOTT> /

2

PL/SQLプロシージャが正常に完了しました。

SCOTT>
SCOTT> select * from "テーブルA";

商品コード 送品年月日 個数
-------- ---------- ----------
00001 2007/07/18 5
00002 2007/07/18 6
00004 2007/07/18 5
00005 2007/07/20 4
00002 2007/07/19 2
00003 2007/07/20 0

6行が選択されました。

SCOTT>

マニュアルにもしっかり記述されているが、merge文のdelete句を利用する場合のポイントは、マージ条件に一致し、update句で更新対象となったデータがdelete句の処理対象となるという点だ。delete句がupdate句とは個別に実行されるわけではない。
前述の例では、update句でマージ条件に一致したデータ(この場合は重複しているデータ)の”個数”を-1に変更し、delete句で "個数” = -1となったデータ(重複したデータ)を削除している。
このことを理解していないと意図した結果を得られないので要注意。(一度理解してしまえば間違ることもなくなると思うが。。。)

merge文を利用したついでに、merge文のTipsも1つ紹介しておきましょう。(Oracle9iでmerge文が追加された際、OTN-Jでも同じ話題があったので投稿したネタですが使えるTipsなのでね。。
今そのスレッドの日付を見たら、なんと、2002年。。。時の経つのは早いもので。。。)

では、次回へつづく。

| | | コメント (0) | トラックバック (0)

2007年8月29日 (水)

Oracle de Fizzbuzz #1 - いまごろ・・・ですが・・

え〜〜っと、前回のエントリで予告していたとおり、あの話題。
今頃。。。という感じのネタですが、少々視点を変えて、fizzbuzzをOracle上で解かせるための簡単な環境作りを中心に。


では、早速、環境作りから。

Oracleではプロファイルによるリソース制限を設定することができる。この機能はOracle7のころから提供されている。(使っている方はどれだけいるか定かでないが。。。)
この機能を利用すれば、セッション単位で、CPU利用時間や、接続時間、アイドル時間などを制限することが可能だ。

ここまで書けば、なにを言いたいか想像できると思う。 

そう、プロファイルを利用すれば、fizzbuzz問題の解答時間制限を設定できるんです!

以下のように、プロファイルで connect_time(接続時間)制限を2分と設定しておけば、接続から2分強経過した時点でオラクルとのセッションが強制的にdisconnectされる。

fizzbuzzユーザに接続して、2分程度でfizzbuzzを解いてみて!? という環境が簡単に作れるわけです。

この制限は、SQL*Plusから接続しても、
iSQL*Plusから接続しても、Oracle SQL Developer、JDeveloperから接続しても同じなので、

例えば、

Linux/MacOSX/Windowのクライアントを用意しておき、
好きなクライアントからSQL*Plusを利用して、SQL又は、PL/SQLでfizzbuzzを解いて! とか

Linux/MacOSXのクライアントからSQL*plusを利用して、SQL又は、PL/SQLでfizzbuzz解いて! 
ただし、editコマンドで起動するのはvi  とか

前提条件を絞って、SQLだけで、fizzbuzz解いて! とか、 

データベースサーバーとして、Oracle9iを用意しておいて、SQLだけで、fizzbuzz解いて! とか

nullカラムで且つ、10行登録されている表を使って、fizzbuzz解いて!

ということを制限時間内に行わせてみる環境が作れるわけです。

(こんなこと考えていると、PL/SQL で Python Challengeをやっていたことを思いだす。。。。。)

注)
コネクションプーリングしているユーザにconnect_timeとか、idle_timeなんて制限付けたりしないでくださいね!。
そんなことする方は居ないと思うけど。。念のため。

以下、fizzbuzzというプロファイルを作成(接続時間制限2分)、fizzbuzzユーザを作成し、fizzbuzzプロファイルを設定。
最後に、初期化パラメータ、 resource_limitをtrueにしてあげれば、準備完了。 という例です。


尚、作成するユーザには、必要最小限のシステム権限やオブジェクト権限だけを付与おくといいですね。例えば、表やシーケンスを作成できないようにしておくとか。

SYS> show parameter resource_limit 

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
resource_limit boolean FALSE
SYS>
SYS> create profile fizzbuzz limit connect_time 2;

プロファイルが作成されました。

SYS> select * from dba_profiles where profile='FIZZBUZZ';

PROFILE RESOURCE_NAME RESOURCE LIMIT
---------- -------------------------------- -------- ----------
FIZZBUZZ COMPOSITE_LIMIT KERNEL DEFAULT
FIZZBUZZ SESSIONS_PER_USER KERNEL DEFAULT
FIZZBUZZ CPU_PER_SESSION KERNEL DEFAULT
FIZZBUZZ CPU_PER_CALL KERNEL DEFAULT
FIZZBUZZ LOGICAL_READS_PER_SESSION KERNEL DEFAULT
FIZZBUZZ LOGICAL_READS_PER_CALL KERNEL DEFAULT
FIZZBUZZ IDLE_TIME KERNEL DEFAULT
FIZZBUZZ CONNECT_TIME KERNEL 2
FIZZBUZZ PRIVATE_SGA KERNEL DEFAULT
FIZZBUZZ FAILED_LOGIN_ATTEMPTS PASSWORD DEFAULT
FIZZBUZZ PASSWORD_LIFE_TIME PASSWORD DEFAULT
FIZZBUZZ PASSWORD_REUSE_TIME PASSWORD DEFAULT
FIZZBUZZ PASSWORD_REUSE_MAX PASSWORD DEFAULT
FIZZBUZZ PASSWORD_VERIFY_FUNCTION PASSWORD DEFAULT
FIZZBUZZ PASSWORD_LOCK_TIME PASSWORD DEFAULT
FIZZBUZZ PASSWORD_GRACE_TIME PASSWORD DEFAULT

16行が選択されました。

SYS>
SYS> create user fizzbuzz identified by jazz
2 default tablespace users
3 temporary tablespace temp
4 quota unlimited on users
5 profile fizzbuzz;

ユーザーが作成されました。

SYS> grant create session,create procedure to fizzbuzz;

権限付与が成功しました。

SYS>
SYS> alter system set resource_limit = true scope=both;

システムが変更されました。

SYS>


fizzbuzzユーザにconnectして、モタモタしていると・・・・・・。Oracleさんからdisconnectされちゃいます!

FIZZBUZZ> select 'Zzzzzz......' from dual:
select 'Zzzzzz......' from dual
*
行1でエラーが発生しました。:
ORA-02399: 最大接続時間を超えました。ログオフ中です。


FIZZBUZZ>

もし、他の前提条件なしで、Oracle9i以降の環境を利用してfizzbuzz解いて! といきなり言われたとしたら、私なら、linuxのクライアントから以下のような無名PL/SQLブロックだけで解くだろうな〜。
(無難な方法だし、1分もかからないでできるだろう。。ただ、SQLだけで、Oracle9iという前提だと、1分強かもしれないな〜。まず、どうんな方法で100行準備するかを決めなきゃならないからね)

FIZZBUZZ> set serveroutput on 
FIZZBUZZ> l
1 begin
2 for i in 1..100 loop
3 dbms_output.put_line(
4 case when mod(i,15)=0 then 'fizzbuzz'
5 when mod(i,3)=0 then 'fizz'
6 when mod(i,5)=0 then 'buzz'
7 else to_char(i)
8 end
9 );
10 end loop;
11* end;
FIZZBUZZ> /
1
2
fizz
4
・・・・中略・・・・
98
fizz
buzz

PL/SQLプロシージャが正常に完了しました。

FIZZBUZZ>
経過: 00:00:00.05

次回は、幾つかの前提条件を設けて、Oracle de fizzbuzz する予定(そんなにfizzbuzzネタで引っ張るつもりはないですよ。w)

| | | コメント (0) | トラックバック (0)

2007年8月19日 (日)

PL/SQL de Conditional Compile #5

さて、PL/SQL条件付きコンパイルもネタが無くなってきたので、最後に、DBMS_DB_VERSIONパッケージを利用して、各リリース毎にコードを切り替えてみよう!
ということで、Oracle database 10g以前のリリースとOracle databse 11g以降のリリースでコードを切り替える例。

● 条件付きコンパイルが無い場合、Oracle11gより前のリリースで以下ようなコードを書いてしまったら....

当然コンパイルエラー!
FUNCTION CONDITIONAL_COMP_SAMPLE3 RETURN NUMBER
IS
vSeq# NUMBER;
BEGIN
vSeq# := mySeq.NEXTVAL;
RETURN vSeq#;
END CONDITIONAL_COMP_SAMPLE3;
/

警告: ファンクションが変更されましたが、コンパイル・エラーがあります。

SCOTT> show errors
FUNCTION CONDITIONAL_COMP_SAMPLE3のエラーです。

LINE/COL ERROR
-------- -----------------------------------------------------------------
9/5 PL/SQL: Statement ignored
9/20 PLS-00357:
表またはビュー、シーケンス参照MYSEQ.NEXTVALは、このコンテキストで
は使用できません。

SCOTT>

● しかし、DBMS_DB_VERSIONパッケージでリリース毎にコードを選択し切り替えたら・・・・便利ですよね。

CREATE OR REPLACE FUNCTION CONDITIONAL_COMP_SAMPLE3 RETURN NUMBER 
IS
vSeq# NUMBER;
BEGIN

$IF DBMS_DB_VERSION.Ver_LE_10 $THEN
SELECT mySeq.NEXTVAL INTO vSeq# FROM DUAL;
$ELSE
vSeq# := mySeq.NEXTVAL;
$END

RETURN vSeq#;

END CONDITIONAL_COMP_SAMPLE3;

DBMS_DB_VERSIONパッケージをリリース番号でどのようにソースコードが変化するか確認。
● Oracle database 10g以前のバージョンでコンパイルした場合

SCOTT> 
SCOTT> set serveroutput on
SCOTT> exec dbms_preprocessor.print_post_processed_source('FUNCTION','SCOTT','CONDITIONAL_COMP_SAMPLE3');
FUNCTION CONDITIONAL_COMP_SAMPLE3 RETURN NUMBER
IS
vSeq# NUMBER;
BEGIN
SELECT mySeq.NEXTVAL INTO vSeq# FROM DUAL;
RETURN vSeq#;
END CONDITIONAL_COMP_SAMPLE3;

PL/SQLプロシージャが正常に完了しました。


● Oracle database 11g以降でコンパイルした場合

SCOTT> exec dbms_preprocessor.print_post_processed_source('FUNCTION','SCOTT','CONDITIONAL_COMP_SAMPLE3');
FUNCTION CONDITIONAL_COMP_SAMPLE3 RETURN NUMBER
IS
vSeq# NUMBER;
BEGIN
vSeq# := mySeq.NEXTVAL;
RETURN vSeq#;
END CONDITIONAL_COMP_SAMPLE3;

PL/SQLプロシージャが正常に完了しました。

SCOTT>


● 各リリース毎のDBMS_DB_VERSIONパッケージの違い
参考 http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_dbver.htm

● 10.2:
package DBMS_DB_Version is 
Version constant pls_integer := 10;
Release constant pls_integer := 2;
Ver_LE_9_1 constant boolean := false;
Ver_LE_9_2 constant boolean := false;
Ver_LE_9 constant boolean := false;
Ver_LE_10_1 constant boolean := false;
Ver_LE_10_2 constant boolean := true;
Ver_LE_10 constant boolean := true;
end DBMS_DB_Version;
● 10.1:
package DBMS_DB_Version is 
Version constant pls_integer := 10;
Release constant pls_integer := 1;
Ver_LE_9_1 constant boolean := false;
Ver_LE_9_2 constant boolean := false;
Ver_LE_9 constant boolean := false;
Ver_LE_10_1 constant boolean := true;
Ver_LE_10 constant boolean := true;
end DBMS_DB_Version;
● 9.2:
package DBMS_DB_Version is 
version constant pls_integer := 9;
release constant pls_integer := 2;
Ver_LE_9_1 constant boolean := false;
Ver_LE_9_2 constant boolean := true;
Ver_LE_9 constant boolean := true;
end DBMS_DB_Version;




PL/SQL de Conditional Compile #1
PL/SQL de Conditional Compile #2
PL/SQL de Conditional Compile #3
PL/SQL de Conditional Compile #4

| | | コメント (0) | トラックバック (0)

2007年8月18日 (土)

PL/SQL de Conditional Compile #4

PL/SQLの条件付きコンパイルのつづきです。

今回は、エラーディレクティブ($ERROR)を利用して、ちょっとした物忘れ対策を!

どのような物忘れ対策かというと、コード中に書かなければならないロジックがあるが、今は書かずに後で追加しようとコメントでメモを残しておいた。
だが〜〜、うっかりミスで、コーディングしないまま、結合テストに〜〜〜〜〜!。あ〜〜〜、大変だ〜!!!! 

なんてことを、防止できるかも・・・・・・・・。しれないという使い方!。

たとえば、以下のように、後でコードを追加しようとストアドファンクションをロジックなして仮に作成しておいたとする。
後でロジックを追加するというこを、すっかり忘れてしまっていると、テスト段階になって、あれ? 結果が帰ってこない!!! ソースを見ると。
あ〜〜〜〜、忘れてた〜〜〜 (^^;;;;;;;

なんてことに・・・・ コンパイルが通ってしまうので、実行してみないと気付かない。。なんてことになっちゃいます。

SCOTT> l
1 create or replace
2 FUNCTION CONDITIONAL_COMP_SAMPLE2
3 RETURN TIMESTAMP
4 IS
5 vNow TIMESTAMP;
6 BEGIN
7 -- TODO - 結合テストまでに、ここにロジック書かなきゃ!
8 RETURN vNow;
9* END CONDITIONAL_COMP_SAMPLE2;
10 /

ファンクションが作成されました。

SCOTT> set null 'NULL!'
SCOTT> select conditional_comp_sample2 from dual;

CONDITIONAL_COMP_SAMPLE2
---------------------------------------------------------------------------
NULL!

SCOTT>

しかし、$ERRORディレクティブを利用して、コンパイルエラーになるようにしておけば。。。。
さらに、エラーメッセージには、やることを思い出させてくれるようなコメントを設定しておけば、これまた、物忘れの激しい方でもなんとかなるかもしれません。

SCOTT> l
1 CREATE OR REPLACE
2 FUNCTION CONDITIONAL_COMP_SAMPLE2
3 RETURN TIMESTAMP
4 IS
5 vNow TIMESTAMP;
6 BEGIN
7
8 -- TODO - 結合テストまでに、ここにロジック書かなきゃ!
9 $ERROR
10 '結合テストまでに、ここにロジック書かなきゃ!'
11 $END
12
13
14 RETURN vNow;
15
16* END CONDITIONAL_COMP_SAMPLE2;
SCOTT> /

警告: ファンクションが変更されましたが、コンパイル・エラーがあります。

SCOTT> show errors
FUNCTION CONDITIONAL_COMP_SAMPLE2のエラーです。

LINE/COL ERROR
-------- -----------------------------------------------------------------
7/3 PLS-00179: $ERROR: 結合テストまでに、ここにロジック書かなきゃ!
SCOTT>

応用編としては、上記のように、単純に、コンパイルエラーにはせず、前々回のエントリで利用したPLSQL_DEBUG初期化パラメータ等と組み合わせ、デバッグ時はコンパイル正常に行わせるという使い方も考えられます。


参考
http://www.oracle.com/technology/oramag/oracle/06-jul/o46plsql.html
http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14261/fundamentals.htm#BEIJFDHG

| | | コメント (0) | トラックバック (0)

2007年8月17日 (金)

PL/SQL de Conditional Compile #3

PL/SQL条件付きコンパイルの続きです。

前回までのおさらい。

初期化パラメータをセッション単位で変更してコンパイルしていたが、Oracle10g R1 10.1.0.4.0以降及び、Oracle9i R2 9.2.0.6.0以降へバックポートされた条件付きコンパイル機能では一部利用できない初期化パラメータがあり、全リリースで共通のソースコードを利用するには、各リリースで利用できる初期化パラメータ PLSQL_DEBUG を利用するようにした。というところまでだった。

今回はいままでとは異なる方法で条件付きコンパイルを行ってみることにする。

その方法とは、初期化パラメータを利用せずパッケージの仕様部で宣言した定数を利用する方法だ。
この方法であれば、リリース毎にいくつかの制限のある初期化パラメータを利用せずに条件コンパイルが可能だ。

但し、その都度、パッケージ定数を変更するという手間はかかる。
実際に利用する際には、定数を変更する為のスクリプトを事前に用意しておき、そのスクリプトを実行するだけで行えるようにすると良いだろう。
また、パッケージをどのスキーマに定義するか、という点なども、よ〜〜〜く検討した上で利用した方が良いだろう。
ルール無しで利用した場合、後々混乱の原因になる可能性があるということは容易に想像できると思う。

● 条件コンパイルのフラグだけを定義したパッケージを作成する

以下の例では、C_DEBUG_ON定数だけを定義し、値はfalseとした。
CREATE OR REPLACE PACKAGE MyPROPERTIES AS

C_DEBUG_ON CONSTANT BOOLEAN := false;

END MyPROPERTIES;

● 以下、条件コンパイルのサンプルコード

以前作成したコードとの違いは、初期化パラメータではなく、前述したパッケージのC_DEBUG_ON定数を参照している点である。
CREATE OR REPLACE FUNCTION CONDITIONAL_COMPILATION_SAMPLE
RETURN TIMESTAMP
IS
vNow TIMESTAMP;
BEGIN
$IF myproperties.c_debug_on $THEN
DBMS_OUTPUT.ENABLE(20000);
DBMS_OUTPUT.PUT_LINE('======= start =====');
$END

SELECT SYSTIMESTAMP INTO vNow FROM dual;

$IF myproperties.c_debug_on $THEN
DBMS_OUTPUT.PUT_LINE('======= end =======');
$END

RETURN vNow;

END CONDITIONAL_COMPILATION_SAMPLE;

● MYPROPERTIES.C_DEBUG_ON=falseで上記サンプルコードをコンパイルし実行した例

Conditional_compilation137 Conditional_compilation138 Conditional_compilation139Conditional_compilation1310


● MYPROPERTIES.C_DEBG_ON=trueで、上記サンプルコードをコンパイルし実行した例

Conditional_compilation131 Conditional_compilation132Conditional_compilation133
Conditional_compilation134 Conditional_compilation135 Conditional_compilation136

| | | コメント (0) | トラックバック (0)

2007年8月16日 (木)

PL/SQL de Conditional Compile #2



とにかく、熱過ぎる! 沖縄の方が気温が低いってどうよ!? 避暑のために、石垣島なんていいかも。
午前10時の気温。東京:摂氏34.6度、沖縄:摂氏29.9度
沖縄のほうが気温が低い!(爆笑)

http://www.weather-eye.com/amedas_graph/index.html

yanokami (矢野顕子 × レイ・ハラカミ) - yanokami - EP - You Showed Me yanokami (矢野顕子 × レイ・ハラカミ) - yanokami - EP - You Showed Me



さて、PL/SQL条件付きコンパイルの続きです。

前回は、本機能が正式にサポートされているOracle10g R2 10.2.0.2.0で行ったが、今回は、バックポートされた Oracle10g R1 10.1.0.4.0で行う。
PL/SQL条件付きコンパイル機能は、前回も書いたように、Oracle10g R1 10.1.0.4.0以降、Oracle9i R2 9.2.0.6.0以降へバックポートされている。

但し、Oracle10g R1 10.1.0.4.0以降及び、Oracle9i R2 9.2.0.6.0以降へバックポートされたPL/SQL条件付きコンパイルでは幾つかの制限があり、Oracle10g R2 からサポートされている同機能と比べて利用できない機能などが存在するので注意が必要だ。
ちなみに、前回のエントリで紹介したPLSQL_CCFLAG初期化パラメータは、パックポートされたOracle10g R1 10.1.0.4.0及び、Oracle9i R2 9.2.0.6.0以降では利用できないと記載されている。

参考:plsql_conditional_compilation.pdfのFunctionality restrictions in 10.1 and 9.2 (P.61)


では、前述の制限を踏まえつつ、以下、Oracle10g 10.1.0.4.0でのテストを!

環境:Oracle10g R1 10.1.0.4.0 for Linux x86
 (TurboLinux Enterprise Server 8 SP3- powered by United Linux 1.0)

前回の例では、ユーザ定義フラグとして、独自に DEBUGONブラグをPLSQL_CCFLAGS初期化パラメータで設定していたが、前述の制限の通り、ハックポートされた機能では利用できない。

そのため、バックポートされた条件付きコンパイルでも利用できるよう、PLSQL_DEBUG初期化パラメータを利用することにした。
このパラメータを利用すれば、Oracle10g R2/R1(10.1.0.4.0以降)/Oracle9i R2(9.2.0.6.0以降)それにOracle11gでも利用できるコードにすることができる。

注)初期化パラメータをしなくても各リリースでソースコードを共有できる方法がある。
但し、初期化パラメータを利用する方法に比べ、いちいちコードに手を加えなければならないなど、イマイチな感じはある。(別途掲載予定)

Conditional_compilation_sql_develop

● まずは、PLSQL関連の初期化パラメータの確認から。

PL/SQL関連の隠しパラメータが幾つかあるが、ここで大切なのは、_plsql_conditional_compilationという初期化パラメータでこれがTRUEであれば、PL/SQLの条件コンパイルが機能する。
また、Oracle10g R2には存在していた、PLSQL_CCFLAGS初期化パラメータが存在していないことも確認できる。前述した通り、今回は、PLSQL_DEBUG初期化パラメータを利用して条件コンパイルを行うことにする。

SYS> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Prod
PL/SQL Release 10.1.0.4.0 - Production
CORE 10.1.0.4.0 Production
TNS for Linux: Version 10.1.0.4.0 - Production
NLSRTL Version 10.1.0.4.0 - Production

SYS> l
1 select
2 a.ksppinm,
3 b.ksppstvl,
4 b.ksppstdf
5 from
6 x$ksppi a join x$ksppcv b
7 on a.indx = b.indx
8 and a.ksppinm like '%plsql%'
9 order by
10* a.ksppinm
SYS> /

KSPPINM KSPPSTVL KSPPSTDF
---------------------------------- ------------------------------ ------------------------------
_plsql_anon_block_code_type INTERPRETED TRUE
_plsql_conditional_compilation TRUE FALSE
_plsql_dump_buffer_events TRUE
plsql_code_type INTERPRETED TRUE
plsql_compiler_flags INTERPRETED, NON_DEBUG TRUE
plsql_debug FALSE TRUE
plsql_native_library_dir TRUE
plsql_native_library_subdir_count 0 TRUE
plsql_optimize_level 2 TRUE
plsql_v2_compatibility FALSE TRUE
plsql_warnings DISABLE:ALL TRUE

11行が選択されました。

SYS>

● 前回のコードから変更したコードは赤字部分

SCOTT> l
1 create or replace
2 FUNCTION CONDITIONAL_COMPILATION_SAMPLE
3 RETURN TIMESTAMP
4 IS
5 vNow TIMESTAMP;
6 BEGIN
7 $IF $$PLSQL_DEBUG $THEN
8 DBMS_OUTPUT.ENABLE(20000);
9 DBMS_OUTPUT.PUT_LINE('======= start =====');
10 $END
11 SELECT SYSTIMESTAMP INTO vNow FROM dual;
12 $IF $$PLSQL_DEBUG $THEN
13 DBMS_OUTPUT.PUT_LINE('======= end =======');
14 $END
15 RETURN vNow;
16* END CONDITIONAL_COMPILATION_SAMPLE;
17 /

ファンクションが作成されました。

● では、PLSQL_DEBUG=falseとして確認!

SCOTT> set serveroutput on
SCOTT> alter session set plsql_debug=false;

セッションが変更されました。

SCOTT> alter function conditional_compilation_sample compile;

ファンクションが変更されました。

SCOTT> exec dbms_preprocessor.print_post_processed_source('FUNCTION','SCOTT','CONDITIONAL_COMPILATION_SAMPLE');
FUNCTION CONDITIONAL_COMPILATION_SAMPLE
RETURN TIMESTAMP
IS
vNow TIMESTAMP;
BEGIN
SELECT SYSTIMESTAMP INTO vNow FROM dual;
RETURN vNow;
END CONDITIONAL_COMPILATION_SAMPLE;

PL/SQLプロシージャが正常に完了しました。


SCOTT> select conditional_compilation_sample from dual;

CONDITIONAL_COMPILATION_SAMPLE
---------------------------------------------------------------------------
07-08-13 22:53:14.161995000


● 次に、PLSQL_DEBUG=trueとして確認!

SCOTT> alter session set plsql_debug=true;

セッションが変更されました。

SCOTT> alter function conditional_compilation_sample compile;

ファンクションが変更されました。

SCOTT> exec dbms_preprocessor.print_post_processed_source('FUNCTION','SCOTT','CONDITIONAL_COMPILATION_SAMPLE');
FUNCTION CONDITIONAL_COMPILATION_SAMPLE
RETURN TIMESTAMP
IS
vNow TIMESTAMP;
BEGIN
DBMS_OUTPUT.ENABLE(20000);
DBMS_OUTPUT.PUT_LINE('======= start =====');
SELECT SYSTIMESTAMP INTO vNow FROM dual;
DBMS_OUTPUT.PUT_LINE('======= end =======');
RETURN vNow;
END CONDITIONAL_COMPILATION_SAMPLE;

PL/SQLプロシージャが正常に完了しました。

SCOTT> select conditional_compilation_sample from dual;

CONDITIONAL_COMPILATION_SAMPLE
---------------------------------------------------------------------------
07-08-13 22:54:01.071903000

======= start =====
======= end =======
SCOTT>
SCOTT>


次回は、初期化パラメータを利用しない場合コードの例を載せる予定。

| | | コメント (0) | トラックバック (0)

2007年8月15日 (水)

PL/SQL de Conditional Compile #1

PL/SQLで条件付きコンパイル? 耳慣れないのも方も多いかもしれない。 C/C++では、おなじみの#ifdefなどのディレクティブ!

そう、それです! プリプロセッサといえば分かり易いでしょうか。 
Oracle10g R2以降で、正式に、PL/SQLのプリプロセッサが実装されています。(正式にと書いたのには理由があります、実は、この条件付きコンパイルは、Oracle10g R1 10.1.0.4以降、Oracle9i R2 9.2.0.6.0以降にバックポートされています。) 

マニュアルでは、 conditional compilation (条件付きコンパイル)として解説されています。

PL/SQLの条件付きコンパイル、マニュアルには記載はされているものの、この機能を解説している日本語のサイトはほとんど見た事がありませんし、実際の所、私もまだ本格的に利用したことはありません。しかし、うまく使えばそれなりの効果は期待できそうですね。

では、早速、条件付きコンパイルの例を。

● 正式に機能追加されたリリースである、Oracle10g R2で試す。

SYS> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.2.0 - Prod
PL/SQL Release 10.2.0.2.0 - Production
CORE 10.2.0.2.0 Production
TNS for 32-bit Windows: Version 10.2.0.2.0 - Production
NLSRTL Version 10.2.0.2.0 - Production

初期化パラメータにいくつかのPL/SQLコンパイル用パラメータが追加されている。 (Oracle10g R1や、Oracle9i R2と比較するわかりやすいかも。。) 以下は、初期化パラメータの幾つかをリストしたもの。詳細はマニュアル参照のこと。
SYS> show parameter plsql

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
plsql_ccflags string
plsql_code_type string INTERPRETED
plsql_compiler_flags string INTERPRETED, NON_DEBUG
plsql_debug boolean FALSE
plsql_native_library_dir string
plsql_native_library_subdir_count integer 0
plsql_optimize_level integer 2
plsql_v2_compatibility boolean FALSE
plsql_warnings string DISABLE:ALL


● 条件付きコンパイル、PLSQL_CCFLAG初期化パラメータで独自フラグを利用する例

以下は、PLSQL_CCFLAG初期化パラメータで、DEBUGONという独自のフラグを設定し、条件付きコンパイルを行う例で、選択ディレクティブである$IF - $THENを利用してデバッグ用ログの表示コードを制御しています。

PLSQL_CCFLAGで、debugonフラグをtrueに設定すれば、DBMS_OUTPUT.PUT_LINE()でデバッグ用ログを表示するコードがコンパイルされ、false又はフラグが設定されていなければ、表示しないコードでコンパイルされます。

SCOTT> l
1 create or replace
2 FUNCTION CONDITIONAL_COMPILATION_SAMPLE
3 RETURN TIMESTAMP
4 IS
5 vNow TIMESTAMP;
6 BEGIN
7 $IF $$DEBUGON $THEN
8 DBMS_OUTPUT.ENABLE(20000);
9 DBMS_OUTPUT.PUT_LINE('======= start =====');
10 $END
11 SELECT SYSTIMESTAMP INTO vNow FROM dual;
12 $IF $$DEBUGON $THEN
13 DBMS_OUTPUT.PUT_LINE('======= end =======');
14 $END
15 RETURN vNow;
16* END CONDITIONAL_COMPILATION_SAMPLE;
17 /

ファンクションが作成されました。


● dbms_preprocessorパッケージによる条件付きコンパイル後のソースの確認

dbms_preprocessorパッケージのprint_post_processed_source()プロシージャを利用すると、条件付きコンパイル後のソースを確認することができる。(all_sourceビューには、条件付きコンパイル前のソースが格納されているため、条件付きコンパイル後のソースとは異なってしまう。)
DBMS_PREPROCESSORパッケージに関する詳細は、マニュアルを参照のこと。

SCOTT> set serveroutput on
SCOTT> exec dbms_preprocessor.print_post_processed_source('FUNCTION','SCOTT','CONDITIONAL_COMPILATION_SAMPLE');
FUNCTION CONDITIONAL_COMPILATION_SAMPLE
RETURN TIMESTAMP
IS
vNow TIMESTAMP;
BEGIN
SELECT SYSTIMESTAMP INTO vNow FROM dual;
RETURN vNow;
END CONDITIONAL_COMPILATION_SAMPLE;

PL/SQLプロシージャが正常に完了しました。

上記のコードは、PLSQL_CCFLAG初期化パラメータにdebugonフラグが未定義又は、falseで定義された場合のコードである。


● 実行してみると、デバッグ用ログは表示されていない。(当然!)

SCOTT> set serveroutput on
SCOTT> select conditional_compilation_sample from dual;

CONDITIONAL_COMPILATION_SAMPLE
---------------------------------------------------------------------------
07-08-13 18:20:39.234000000

SCOTT> set serveroutput off

● PLSQL_CCFLAG初期化パラメータに debugonフラグをtrueで設定してみると・・・。

SCOTT> 
SCOTT> alter session set plsql_ccflags='debugon:true';

セッションが変更されました。

SCOTT> l
1 create or replace
2 FUNCTION CONDITIONAL_COMPILATION_SAMPLE
3 RETURN TIMESTAMP
4 IS
5 vNow TIMESTAMP;
6 BEGIN
7 $IF $$DEBUGON $THEN
8 DBMS_OUTPUT.ENABLE(20000);
9 DBMS_OUTPUT.PUT_LINE('======= start =====');
10 $END
11 SELECT SYSTIMESTAMP INTO vNow FROM dual;
12 $IF $$DEBUGON $THEN
13 DBMS_OUTPUT.PUT_LINE('======= end =======');
14 $END
15 RETURN vNow;
16* END CONDITIONAL_COMPILATION_SAMPLE;
17 /

ファンクションが作成されました。


SCOTT> exec dbms_preprocessor.print_post_processed_source('FUNCTION','SCOTT','CONDITIONAL_COMPILATION_SAMPLE');
FUNCTION CONDITIONAL_COMPILATION_SAMPLE
RETURN TIMESTAMP
IS
vNow TIMESTAMP;
BEGIN
DBMS_OUTPUT.ENABLE(20000);
DBMS_OUTPUT.PUT_LINE('======= start =====');
SELECT SYSTIMESTAMP INTO vNow FROM dual;
DBMS_OUTPUT.PUT_LINE('======= end =======');
RETURN vNow;
END CONDITIONAL_COMPILATION_SAMPLE;

PL/SQLプロシージャが正常に完了しました。

SCOTT>
SCOTT> set serveroutput on
SCOTT> select conditional_compilation_sample from dual;

CONDITIONAL_COMPILATION_SAMPLE
---------------------------------------------------------------------------
07-08-13 18:26:13.906000000

======= start =====
======= end =======
SCOTT>
ということで、デバッグ用ログが表示されるコードでコンパイルされています!。

次回は、Oracle10g R1 10.1.0.4.0で試す予定。
Conditional_compilation_sql_develop

| | | コメント (0) | トラックバック (0)

2007年8月 5日 (日)

Estimate of rollback completion time

さて、前回、おおよそのロールバック完了時刻を見積もる関数でも作ってみるか! なんて言っていたが実際に作ってみた。

尚、今回は、Oracle SQL Developer 1.2 for MacOSXからWindows XP professional上のOracle10g R2 EEへ接続。

Estimate_rollback_complete1 Estimate_rollback_complete2 Estimate_rollback_complete3

● 準備

まずは、テーブルの作成から。
SCOTT> 
SCOTT> desc test
名前 NULL? 型
----------------------------------------- -------- ----------------------------
DATA VARCHAR2(4000)

SCOTT>
SCOTT>
SCOTT>

● データの登録

ロールバックに長時間を要するデータを登録する。(更新でも削除でも構わないが、この例ではINSERT文で。)
SCOTT> begin 
2 for i in 1..400000 loop
3 insert into test values(lpad('x',4000,'x'));
4 end loop;
5 end;
6 /

PL/SQLプロシージャが正常に完了しました。

経過: 00:05:30.06

SCOTT>
SCOTT>

● ロールバック!

準備が整ったので、早速、ロールバック!!
SCOTT>
SCOTT> rollback;

以下、ロールバック処理中に別途起動した、SQL*plusより実行

========以下、ロールバック中、別途起動したSQL*plusから実行した内容==============

● おおよそのロールバック完了時刻を確認する。

自作関数estimateRollbackCompleteTime()を以下のようにスカラ関数として実行すれば、おおよそのロールバック完了日時を確認できる。尚、関数の引数は、ロールバック完了時刻を見積もりたいセッションのSIDとSERIAL#で事前にv$session、v$transactionを問い合わせて確認しておく。

尚、今回は、dbms_lockパッケージの実行権限と、select any dictionaryシステム権限をSCOTTユーザへ付与し、estimateRollbackCompleteTime()関数を作成してある。

SCOTT> select estimateRollbackCompleteTime(146,91) as "Estimate Time",sysdate as now from dual;
Estimate Time NOW
------------------- -------------------
2007/08/02 16:29:46 2007/08/02 16:21:16

SCOTT>


● 以下、UNDOブロックのモニタリング。

ロールバックが進み、used_ublkの値が減少していくことが確認できる。ロールバックの完了時刻は、前述の自作関数で求めたおおよそのロールバック完了時間に近い。
環境などにも影響sれるのだが、予想時間を超えることもあるし、短くなる場合もあるので何度か実行してみるといいですね。または、関数を改造して、見積もりのベースとしている30秒間の処理件数を1分間の処理件数に変更するなどして、精度を高めることは可能かもしれない。
ただ、いつ終わるか分からないロールバック処理時間のおおよその時間を知るにはこのままでも十分なのではないかと思う。

SYSTEM> r
1 select
2 s.sid,
3 s.serial#,
4 s.username,
5 t.used_ublk
6 ,to_char(sysdate,'yyyy/mm/dd hh24:mi:ss') as time
7 from
8 v$session s join v$transaction t
9 on t.addr = s.taddr
10 where
11* s.username = 'SCOTT'

SID SERIAL# USERNAME USED_UBLK TIME
---------- ---------- ---------- ---------- -------------------
146 91 SCOTT 1813 2007/08/02 16:19:13

経過: 00:00:00.01

SYSTEM> /

SID SERIAL# USERNAME USED_UBLK TIME
---------- ---------- ---------- ---------- -------------------
146 91 SCOTT 1594 2007/08/02 16:24:21

経過: 00:00:00.00
SYSTEM> /

SID SERIAL# USERNAME USED_UBLK TIME
---------- ---------- ---------- ---------- -------------------
146 91 SCOTT 882 2007/08/02 16:26:45

経過: 00:00:00.01
SYSTEM> /

SID SERIAL# USERNAME USED_UBLK TIME
---------- ---------- ---------- ---------- -------------------
146 91 SCOTT 747 2007/08/02 16:27:10

経過: 00:00:00.01
SYSTEM> /

SID SERIAL# USERNAME USED_UBLK TIME
---------- ---------- ---------- ---------- -------------------
146 91 SCOTT 668 2007/08/02 16:27:24

経過: 00:00:00.01
SYSTEM> /

SID SERIAL# USERNAME USED_UBLK TIME
---------- ---------- ---------- ---------- -------------------
146 91 SCOTT 28 2007/08/02 16:29:23

経過: 00:00:00.01
SYSTEM> /

レコードが選択されませんでした。

SYSTEM> select to_char(sysdate,'yyyy/mm/dd hh24:mi:ss') as now from dual;

NOW
-------------------
2007/08/02 16:29:30

経過: 00:00:00.01
SYSTEM>

ということで、今回は、たまたま、見積もり時刻より早めに終わったようですね。ラッキー!

===========ロールバック中に別途起動したSQL*Plusでのログ、ここまで==============

ロールバックが完了しました。

経過: 00:14:34.83
SCOTT>


以下、ロールバック完了時間の見積もり関数の例。
内容は単純で、2点間のused_ublk数の残量から単位時間あたりのロールバック処理時間をもとめ、その時点のused_ublk数を元にロールバックが完了するおおよその日時を算出しているだけ。FORループを利用しているのは、単に、明示カーソルのオープン/フェッチ/クローズと例外処理を書かずにすむから。特に必要性がなければ、このプログラミングスタイルが個人的には好き。(この辺りは、好みの問題だけど。)

CREATE OR REPLACE FUNCTION estimateRollbackCompleteTime
(
iSid IN NUMBER,
iSerial# IN NUMBER
)
RETURN DATE IS
C_INTERVAL CONSTANT PLS_INTEGER := 30;

vStartTimestamp TIMESTAMP;
vEndTimestamp TIMESTAMP;
vStartUsedUndoBlocks NUMBER;
vEndUsedUndoBlocks NUMBER;
vEstimatedSecs NUMBER;
vIsFound BOOLEAN;

CURSOR csr_undoblocks
(
pSid IN NUMBER,
pSerial# IN NUMBER
) IS
SELECT
s.username,
t.used_ublk,
SYSTIMESTAMP AS now
FROM
v$session s JOIN v$transaction t
ON t.addr = s.taddr
WHERE
s.sid = pSid
AND s.serial# = pSerial#;
BEGIN
vIsFound := FALSE;
FOR rUndoBlocks IN csr_undoBlocks(iSid, iSerial#) LOOP
vStartTimeStamp := rUndoBlocks.now;
vStartUsedUndoBlocks := rUndoBlocks.used_ublk;
vIsFound := TRUE;
END LOOP;

IF NOT vIsFound THEN
RETURN NULL;
ELSE
vIsFound := FALSE;
END IF;

DBMS_LOCK.SLEEP(C_INTERVAL);

FOR rUndoBlocks IN csr_undoBlocks(iSid, iSerial#) LOOP
vEndTimestamp := rUndoBlocks.now;
vEndUsedUndoBlocks := rUndoBlocks.used_ublk;
vIsFound := TRUE;
END LOOP;

IF NOT vIsFound
OR (vStartUsedUndoBlocks - vEndUsedUndoBlocks) <= 0
THEN
RETURN NULL;
ELSE
vEstimatedSecs :=
CEIL(vEndUsedUndoBlocks / (vStartUsedUndoBlocks - vEndUsedUndoBlocks)) * C_INTERVAL;
RETURN SYSDATE + NUMTODSINTERVAL(vEstimatedSecs, 'SECOND');
END IF;
END;

| | | コメント (0) | トラックバック (0)

2007年7月14日 (土)

Oracle Database 11g - PL/SQL - PL/Scope

これまた、渋い機能追加ですね。
Cscopeならぬ、PL/Scopeですか!。 待望の!?のクロスリファレンスツールの登場のようです。
前のエントリでもかいたプロファイリング機能や、クロスリファレンス機能などは、Oracle SQL Developerでも利用できるんでしょうかねぇ。(別途調べてみますか。。)


そういえば、Oracle7のころ、PL/SQLのクロスリファレンスって取れないのか? なんてPro*Cで、ディクショナリビューを問い合せてクロスリファレンスもどきをリストするプログラムを試しに作ったことあったっけ。。。
遠い目。。。プログラムが複雑かつ巨大になるとディクショナリビューの検索に時間がかかり過ぎ、重くて使い物にならなかったので没でしたけど。。。(笑)


Oracle Database 11g Application Development whitepaperより引用。

PL/SQL: PL/Scope

PL/Scope allows you to browse PL/SQL source code analogously to the way that
Cscope (see http://cscope.sourceforge.net/) allows you to browse C source code.
You can search for and display all types of definitions, declarations, assignments
and references in the PL/SQL source code. The PL/SQL compiler can optionally
derive the metadata needed to support PL/Scope and store it in the database
catalog. The metadata takes into account the nuances of the language, including
scoping and overloading. You can generate reports (especially hyperlinked HTML
reports) with supplied report generators. PL/Scope supports increased developer
productivity, especially for those who need to maintain someone else's code.

| | | コメント (0) | トラックバック (0)

Oracle Database 11g - PL/SQL - Sequences

お〜。これは便利。
PL/SQL内から直接、シーケンスにアクセスできるようになるんですね。
これまでは、PL/SQL内でシーケンスにアクセスするには

select hoge_sequence.nextval into vSeq# from dual;

なんてクエリをPL/SQL内で発行する必要があった訳ですけど、これからはダイレクトに

vSeq# := hoge_sequence.nextval;

と書けるということなんでしょうね。便利です。

眠いので今日はこの辺りで。。。。。

Oracle Database 11g Application Development  whitepaperより引用。

PL/SQL: Sequences

The sequence generator provides a sequential series of numbers to applications.
The sequence generator is especially useful in multiuser environments for
generating unique sequential numbers such as an employee id without the overhead
of disk I/O or transaction locking.
In the previous release of Oracle Database, when a PL/SQL program needed to get
a value from syntax, it used SQL. This is a usability irritation for PL/SQL
programmers. In Oracle Database 11g, it is now possible to simply use the
pseudocolumns CURRVAL and NEXTVAL in a PL/SQL expression.
Simplifies coding.

| | | コメント (0) | トラックバック (0)

Oracle Database 11g - PL/SQL - Hierarchical Profiler

へぇ〜。 PL/SQLのプロファイリングって、今までは、DBMS_PROFILEパッケージを使っていたけど、DBMS_PROFILERパッケージが進化したってことなのか?、それとも、新しいプロファイラーなのかなぁ。以下の概要ではそこまでは解説されていないけど、結果をHTMLで確認できるとか、深い部分までプロファイリングできそうですね。これまでのPL/SQLプロファイリングよりかなり使い勝手は向上しているようで期待してます。

Oracle Database 11g Application Development whitepaperより引用。

PL/SQL: Hierarchical Profiler

The PL/SQL hierarchical profiler reports the dynamic execution profile of your
PL/SQL program, organized by subprogram calls. It accounts for SQL and
PL/SQL execution times separately. Each subprogram-level summary in the
dynamic execution profile includes information such as number of calls to the
subprogram, time spent in the subprogram itself, time spent in the subprogram's
subtree (that is, in its descendent subprograms), and detailed parent-children
information. You can browse the generated HTML reports in any browser. The
browser's navigational capabilities, combined with well chosen links, provide a
powerful way to analyze performance of large applications, improve application
performance, and lower development costs.

| | | コメント (0) | トラックバック (0)

2007年7月 8日 (日)

Stored Procedure で host command #3

前回からの続きです。
さて、前回は、javaクラスをOracle Databaseへデプロイしたところまでだった。

ということで、デプロイしたクラスとメソッドを確認しておきましょう! (以下の例は、 user_java_methodsビューを問い合わせたもの)

このマニュアルも参考になるのでリンクしておきます。

SQL> conn scott/tiger
SQL> select name,method_name,accessibility,is_static,return_class from user_java_methods;

NAME METHOD_NAM ACCESSIBILITY IS_STA RETURN_CLA
---------------------------------------- ---------- ------------------ ------ ----------
jp/macdeoracle/CommandOutputReader $init$ NO -
jp/macdeoracle/CommandOutputReader <init> PUBLIC NO -
jp/macdeoracle/CommandOutputReader run PUBLIC NO -
jp/macdeoracle/HostCommandExecuter <clinit> YES -
jp/macdeoracle/HostCommandExecuter exec PRIVATE YES -
jp/macdeoracle/HostCommandExecuter ls PUBLIC YES -
jp/macdeoracle/HostCommandExecuter main PUBLIC YES -
jp/macdeoracle/HostCommandExecuter <init> PUBLIC NO -

8行が選択されました。

SQL>

● 次にjavaストアドプロシージャのコール仕様を作成する

language java以降がポイントですよ。実行するstatic methodを指定します。
SQL> create or replace procedure ls(directory IN VARCHAR2)
2 as language java
3 name 'jp.macdeoracle.HostCommandExecuter.ls(java.lang.String)';
4 /

プロシージャが作成されました。

では、実行してみると・・・・・。
あれれ、権限が足りないとでますね。。。。。

SQL>
SQL> exec ls('c:¥temp');
BEGIN ls; END;

*
行1でエラーが発生しました。:
ORA-29532: 不明なJava例外でJavaコールが終了しました: java.security.AccessControlException: the Permission (java.io.FilePermission <<ALL
FILES>> execute) has not been granted to SCOTT. The PL/SQL to grant this is dbms_java.grant_permission( 'SCOTT',
'SYS:java.io.FilePermission', '<<ALL FILES>>', 'execute' )
ORA-06512: "SCOTT.LS", 行1
ORA-06512: 行1


● エラーメッセージの指示通りに、 dbms_java.grant_permissionを実行してみることに。。
  user_java_policyを確認してみると権限は付与されたようですね。。。

SQL> conn / as sysdba
SQL> execute dbms_java.grant_permission('SCOTT','SYS:java.io.FilePermission','<<ALL FILES>>','execute');

PL/SQLプロシージャが正常に完了しました。

SQL>
SQL> conn scott/tiger
SQL> select action,type_name,name,enabled from user_java_policy;


ACTION TYPE_NAME NAME ENABLED
---------- ---------------------------------------- ---------------------------------------- ----------------
read java.io.FilePermission <<ALL FILES>> ENABLED
java.lang.RuntimePermission accessClassInPackage.* ENABLED
java.lang.RuntimePermission defineClassInPackage.* ENABLED
java.lang.RuntimePermission getProtectionDomain ENABLED
java.lang.RuntimePermission stopThread ENABLED
connect, java.net.SocketPermission * ENABLED
resolve
java.lang.RuntimePermission createSecurityManager ENABLED
java.lang.RuntimePermission exitVM ENABLED
java.lang.RuntimePermission loadLibrary.* ENABLED
java.lang.RuntimePermission modifyThread ENABLED
java.lang.RuntimePermission modifyThreadGroup ENABLED
java.lang.RuntimePermission preferences ENABLED
read java.util.PropertyPermission * ENABLED
write java.util.PropertyPermission user.language ENABLED
oracle.aurora.rdbms.security.PolicyTable 0:java.lang.RuntimePermission#loadLibrar ENABLED
Permission y.*
oracle.aurora.security.JServerPermission DUMMY DISABLED
oracle.aurora.security.JServerPermission LoadClassInPackage.* ENABLED
oracle.aurora.security.JServerPermission LoadClassInPackage.java.* ENABLED
oracle.aurora.security.JServerPermission LoadClassInPackage.oracle.aurora.* ENABLED
oracle.aurora.security.JServerPermission LoadClassInPackage.oracle.jdbc.* ENABLED
execute java.io.FilePermission <<ALL FILES>> ENABLED

21行が選択されました。

SQL>


再度、実行してます!

SQL> exec ls('c:¥temp');
BEGIN ls; END;

*
行1でエラーが発生しました。:
ORA-29532: 不明なJava例外でJavaコールが終了しました: java.security.AccessControlException: the Permission (java.io.FilePermission <<ALL
FILES>> execute) has not been granted to SCOTT. The PL/SQL to grant this is dbms_java.grant_permission( 'SCOTT',
'SYS:java.io.FilePermission', '<<ALL FILES>>', 'execute' )
ORA-06512: "SCOTT.LS", 行1
ORA-06512: 行1


なんなんでしょうね。よくわからないエラーメッセージですね。権限はあるのに。
わかりにくいメッセージ。やはりこうでなくちゃ、オラクルは。。なんてね。
イライラしてきたので、javasysprivロールを付与しちゃいました。(セキュリティ面からみれば好ましくないのですけど。)
各クラス毎に実行権限を細かく付与すればうまくいきそうですが、エラーメッセージからは、何のどんな権限が不足しているのか単純には判断できない(予想はつきますけど・・・)ので、この件に関しては別途調べることにして先へ進むことにします。要調査->TODO

SQL> conn / as sysdba
接続されました。
SQL> grant javasyspriv to scott;

権限付与が成功しました。


:-) やっと動いた〜。

SQL> conn scott/tiger
接続されました。

SQL>
SQL> set serveroutput on
SQL> exec dbms_java.set_output(2000);

PL/SQLプロシージャが正常に完了しました。

SQL> exec ls('c:¥temp');
apex_3.0
apex_3.0.zip
jdevstudio10131.zip
version.log

PL/SQLプロシージャが正常に完了しました。

SQL>

なんとか実行できました。。。ね。 (^^;;;;

Jdeveloper_filelist3

| | | コメント (0) | トラックバック (0)

2007年7月 4日 (水)

Stored Procedure で host command #2

さて、前回のつづきです。
javaストアドプロシージャのソースは以下の通り。

OSコマンドを実行するjavaの例は、ググればやまほどヒットするので特に解説はなし。
Oracleでjavaストアドプロシージャを作成する方法は、マニュアル「Oracle Database Java開発者ガイド - 3 Oracle DatabaseでのJavaメソッドのコール」を参照のこと。
基本は前述のマニュアルで足りると思います。(今回は、JDBCも利用していませんし。)
それに加えて、OTN-JのOracleJVM and Java Stored Procedures なども参考になるでしょうね。

以下、Oracle JDeveloper 10.1.3.1 for MacOSXを利用。

Jdev10gmacosx_hostcommand

package jp.macdeoracle;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;


public class CommandOutputReader extends Thread {
private InputStream is;
private String lines = "";

public CommandOutputReader(InputStream is) {
this.is = is;
}

public void run()
{
String outputLine;
try {
BufferedReader br = new BufferedReader(new InputStreamReader(this.is));
while ((outputLine = br.readLine()) != null) {
System.out.println(outputLine);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package jp.macdeoracle;

import java.lang.Runtime;
import java.lang.SecurityException;
import java.io.IOException;

public class HostCommandExecuter
{
public static final String OS_WINDOWS_ = "Windows";
public static final String OS_MACOSX_ = "Mac OS X";
public static final String OS_LINUX_ = "Linux";

private static void exec(String[] command)
throws IOException,
SecurityException,
InterruptedException
{
Process myproc = Runtime.getRuntime().exec(command);
new CommandOutputReader(myproc.getInputStream()).start();
new CommandOutputReader(myproc.getErrorStream()).start();
myproc.waitFor();
}

public static void ls(String directory)
throws IOException,
SecurityException,
InterruptedException
{
String osname = System.getProperty("os.name");
if (osname.indexOf(OS_WINDOWS_) >= 0) {
String[] cmd = {"CMD.EXE", "/C", "DIR", "/B", directory};
exec(cmd);
} else if (
osname.indexOf(OS_MACOSX_) >= 0
|| osname.indexOf(OS_LINUX_) >= 0
) {
String[] cmd = {"/bin/ls", directory};
exec(cmd);
} else {
throw new IOException(osname + ":このOSには対応していません。");
}
}

public static void main(String[] args)
throws IOException,
InterruptedException,
SecurityException
{
ls(".");
}
}

上記ソースをコンパイルしてOracle Databaseへデプロイ! (以下、Oracle10g R2の例)
うまくいけば以下のようなログが表示される。

loadjavaを接続'Oracle10gR2_Scott'で呼出し中。引数:
-order -resolve -definer -force -thin -schema scott -verbose
creating : class SCOTT.jp/macdeoracle/CommandOutputReader
loading : class SCOTT.jp/macdeoracle/CommandOutputReader
creating : class SCOTT.jp/macdeoracle/HostCommandExecuter
loading : class SCOTT.jp/macdeoracle/HostCommandExecuter
resolving: class SCOTT.jp/macdeoracle/CommandOutputReader
resolving: class SCOTT.jp/macdeoracle/HostCommandExecuter
Loadjavaが終了しました。
公開が終了しました。
---- ストアド・プロシージャのデプロイが終了しました ----


次のようなエラーがでたら、J2SE 1.4.xでコンパイルし直してデプロイすべし! 
オラクルにしては、わかり易いメッセージじゃ。 (^^) 

loadjavaを接続'Oracle9iR2_Scott'で呼出し中。引数:
-order -resolve -definer -force -thin -schema scott -verbose
creating : class SCOTT.jp/macdeoracle/CommandOutputReader
loading : class SCOTT.jp/macdeoracle/CommandOutputReader
created : "SCOTT".CREATE$JAVA$LOB$TABLE
Error while creating class jp/macdeoracle/CommandOutputReader
ソケットから読み込むデータはこれ以上ありません。
Error while testing for existence of dbms_java.handleMd5
Exception java.sql.SQLException: OALL8 is in an inconsistent state.
created : "SCOTT".JAVA$CLASS$MD5$TABLE
creating : class SCOTT.jp/macdeoracle/HostCommandExecuter
loading : class SCOTT.jp/macdeoracle/HostCommandExecuter
Error while creating class jp/macdeoracle/HostCommandExecuter
ソケットから読み込むデータはこれ以上ありません。
The following operations failed
class SCOTT.jp/macdeoracle/CommandOutputReader: creation (createFailed)
class SCOTT.jp/macdeoracle/HostCommandExecuter: creation (createFailed)
oracle.aurora.server.tools.loadjava.ToolsException: Failures occurred during processing
at oracle.aurora.server.tools.loadjava.LoadJava.process(LoadJava.java:863)
at oracle.jdeveloper.deploy.tools.OracleLoadjava.deploy(OracleLoadjava.java:116)
at oracle.jdeveloper.deploy.tools.OracleLoadjava.deploy(OracleLoadjava.java:46)
at oracle.jdevimpl.deploy.OracleDeployer.deploy(OracleDeployer.java:97)
at oracle.jdevimpl.deploy.StoredProcHandler.doDeploy(StoredProcHandler.java:473)
at oracle.jdevimpl.deploy.StoredProcHandler.doDeploy(StoredProcHandler.java:360)
at oracle.jdevimpl.deploy.StoredProcHandler.doDeploy(StoredProcHandler.java:284)
at oracle.jdevimpl.deploy.StoredProcProfileDt$Action$1.run(StoredProcProfileDt.java:383)
#### デプロイが不完全です ####
*** Note ***
デプロイ・エラーの可能性の1つとして、ターゲット・データベースがJDKバージョン1.5をサポートしていないことが考えられます。
お使いのプロジェクト・プロパティ・コンパイラのソースとターゲットを古いバージョンに変更することで、この問題を修復できます。
************


プロジェクトプロパティを開いて、j2se 1.4.2を設定している様子。
Change_jdk1 Change_jdk2Change_jdk3Change_jdk4

今日はここまで。次回へつづく。

| | | コメント (0) | トラックバック (0)

2007年6月30日 (土)

Stored Procedure で host command

随分前のネタ(Oracle9i R2は、TurboLinux8 Enterprise Server - Powered by United Linux 1.0と Oracle10g R1 は、MacOSX Pantherの頃なので2005年くらいに公開するタイミングを逃していたネタ)なんですが、検索キーワードやOTN-Jなどでもたまに見かけるので、昔のネタに、Oracle10g R2 for Windowsの例も付け加えて公開することにした。

どんなネタかって? まずは実行結果から。

見ての通り、ストアドプロシージャからDOSコマンドのdirや、shellコマンドのlsを実行してます。
Oracleのストアドプロシージャは、PL/SQL以外に、javaやCでを書くこともできます。
以下の例は、javaストアドプロシージャで DOSコマンドのdirや、shellコマンドのlsを実行しています。

セキュリティ上、なんでもかんでも実行できるようにしたりするのはお勧めしませんが、以前、負荷テストで使ったことがあります。
負荷テスト開始のタイミングで、sarを実行し、テスト終了時に停止させるshellスクリプトを実行してましたね。 いつ終わるかはっきりしないような負荷テストを自動起動し、システム側の状態も同時に記録するといったことを行っていました。
ただ、最近は、DBMS_SCHEDULERパッケージがあるので自前で作らなくてできるとは思いますが。。まぁ、こんなことも出来るんだよね。 っていうことを知ってても損はないんじゃないかと思います。


● Linux (United Linux 1.0 なつかしい〜。)

SCOTT> conn scott@zion
パスワードを入力してください:
接続されました。
SCOTT> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
PL/SQL Release 9.2.0.1.0 - Production
CORE 9.2.0.1.0 Production
TNS for Linux: Version 9.2.0.1.0 - Production
NLSRTL Version 9.2.0.1.0 - Production

SCOTT>
SCOTT>
SCOTT> set serveroutput on
SCOTT> exec dbms_java.set_output(20000);

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec ls('/tmp');
UnitedLinux-1.0-SP-3
i830-20030120-i386-linux.tar.gz
turbonetcfg-routing.log
turbonetcfg.log

PL/SQLプロシージャが正常に完了しました。

SCOTT>

● MacOSX 10.3 Panther

SCOTT> conn scott@panther
パスワードを入力してください:
接続されました。
SCOTT> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.1.0.3.0 - Prod
PL/SQL Release 10.1.0.3.0 - Production
CORE 10.1.0.3.0 Production
TNS for MacOS X Server: Version 10.1.0.3.0 - Production
NLSRTL Version 10.1.0.3.0 - Production

SCOTT> set serveroutput on
SCOTT> exec dbms_java.set_output(20000);

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec ls('/Users/oracle');
Desktop
Documents
Library
Movies
Music
Pictures
Public
Sites
jdevhome

PL/SQLプロシージャが正常に完了しました。

SCOTT>


● Windows XP Professional

SCOTT> conn scott@catfish
パスワードを入力してください:
接続されました。
SCOTT>
SCOTT>
SCOTT>
SCOTT> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.2.0 - Prod
PL/SQL Release 10.2.0.2.0 - Production
CORE 10.2.0.2.0 Production
TNS for 32-bit Windows: Version 10.2.0.2.0 - Production
NLSRTL Version 10.2.0.2.0 - Production

SCOTT>

SCOTT> set serveroutput on
SCOTT> exec dbms_java.set_output(20000);

PL/SQLプロシージャが正常に完了しました。

SCOTT> exec ls('c:¥temp');
apex_3.0
apex_3.0.zip
jdevstudio10131.zip
version.log

PL/SQLプロシージャが正常に完了しました。

SCOTT>

ってとこですね。ほんとにお遊びでした。(笑)

ということで、次回はどうやってOracleのPL/SQL、java、ホストコマンドで遊んでいるかお見せします。

| | | コメント (0) | トラックバック (0)

2007年6月 8日 (金)

Mac De Oracle : PL/SQL de Collection #5



Marcus Miller - Silver Rain - It'll Come Back to YouMarcus Miller - Silver Rain - It'll Come Back to You

ちょいと間が空いたが、PL/SQLのコレクションでのお遊び。その5回目。

検索ワードで結構目にするのが、"N次元 結合配列" というキーワード。

ということで、今回は、多次元のコレクションの例を。

N次元のコレクションってどうやって作るの? ってことで情報を検索しているのだと思いますが、しっかりマニュアルにも記載されているので、そちらもしっかり読んでおくことをおすすめしますよ!。

Associative Array(以下、結合配列)、VARRAYやNested Table(以下、ネスト表)どれでも、考え方は同じ。
コレクション自体を別コレクションの要素にすればできるんですよ。 コレクションを入れ子にする と言えばイメージしやすい? ですかね。


以下のコードで赤太字で示しているところが多次元コレクション定義とアクセスのポイント。
まず、empType型を要素とするNestedTableType型(ネスト表)を定義し、次にNestedTalbType型を要素とするStrAssociativeArrayType型(結合配列)を定義している。
以下の例は多少、ひねくれた例? として結合配列とネスト表という異なるコレクションを利用して多次元コレクションを定義している。その影響でコレクションの操作は多少面倒になるのだが..

尚、empType型は、事前に作成しておいたオブジェクト型。

create or replace
PROCEDURE multiLevelCollections
AS
TYPE NestedTableType IS TABLE OF empType;
TYPE StrAssociativeArrayType IS TABLE OF NestedTableType INDEX BY VARCHAR2(50);

myNestedTable NestedTableType := NestedTableType();
myStrAssociativeArray StrAssociativeArrayType;


claerMyStrAssociateArray StrAssociativeArrayType;

myEmp empType;
j dept.dname%TYPE;
vDeptName dept.dname%TYPE := NULL;

CURSOR csrEmp IS
SELECT
emp.job,
emp.deptno,
dept.dname,
emp.empno,
emp.ename,
emp.sal,
emp.hiredate
FROM
emp JOIN dept
ON emp.deptno = dept.deptno
ORDER BY
emp.deptno,
emp.ename;

PROCEDURE printArray
IS
BEGIN
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('**** コレクションに要素が存在している状態 ****');
DBMS_OUTPUT.PUT_LINE(
'myStrAssociativeArray('
|| myStrAssociativeArray.COUNT
|| ')'
);

j := myStrAssociativeArray.FIRST;
WHILE j IS NOT NULL LOOP
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE(
'myStrAssociativeArray('
|| j || ')(' || myStrAssociativeArray(j).COUNT || ')'
);

FOR k IN
myStrAssociativeArray(j).FIRST..myStrAssociativeArray(j).LAST
LOOP
DBMS_OUTPUT.PUT_LINE(
'myStrAssociativeArray(' || j || ').'
|| 'myNestedTable(' || TO_CHAR(k) || ') = '
|| myStrAssociativeArray(j)(k).TO_STRING()
);
END LOOP;
j := myStrAssociativeArray.NEXT(j);
END LOOP;
END;

BEGIN
FOR emp_rec IN csrEmp LOOP
IF csrEmp%ROWCOUNT = 1 THEN
vDeptName := emp_rec.dname;
END IF;

myEmp := empType (
emp_rec.empno,
emp_rec.ename,
emp_rec.job,
emp_rec.hiredate,
emp_rec.sal,
emp_rec.deptno
);

IF vDeptName <> emp_rec.dname THEN
myStrAssociativeArray(vDeptName) := myNestedTable;
vDeptName := emp_rec.dname;
myNestedTable := NestedTableType();
END IF;

myNestedTable.EXTEND(1);
myNestedTable(myNestedTable.COUNT) := myEmp;
END LOOP;

IF myNestedTable.COUNT > 0 THEN
myStrAssociativeArray(vDeptName) := myNestedTable;
END IF;

printArray();

-- 結合配列を空にする!
myStrAssociativeArray := claerMyStrAssociateArray;

DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('**** コレクションが空の状態 ****');
DBMS_OUTPUT.PUT_LINE(
'myStrAssociativeArray('
|| myStrAssociativeArray.COUNT
|| ')'
);


END;
/

SCOTT> l
1 select
2 emp.deptno,
3 dept.dname,
4 emp.empno,
5 emp.ename,
6 emp.sal,
7 emp.hiredate
8 from
9 emp join dept
10 on emp.deptno = dept.deptno
11 order by
12 emp.deptno,
13* emp.ename
SCOTT> /

DEPTNO DNAME EMPNO ENAME SAL HIREDATE
---------- -------------- ---------- ---------- ---------- --------
10 ACCOUNTING 7782 CLARK 2450 81-06-09
10 ACCOUNTING 7839 KING 5000 81-11-17
20 RESEARCH 7876 ADAMS 1100 87-05-23
20 RESEARCH 7566 JONES 2975 81-04-02
20 RESEARCH 7788 SCOTT 3000 87-04-19
20 RESEARCH 7369 SMITH 800 80-12-17
30 SALES 7499 ALLEN 1600 81-02-20
30 SALES 7698 BLAKE 2850 81-05-01
30 SALES 7900 JAMES 950 81-12-03
30 SALES 7654 MARTIN 1250 81-09-28
30 SALES 7844 TURNER 1500 81-09-08
30 SALES 7521 WARD 1250 81-02-22

12行が選択されました。

SCOTT>

実行結果は次のようになる。(Oracle SQL Developer 1.1 for MacOSX) 
1.2がリリースされたようなので後でダウンロードしとくか・・> TODO

Oracleplsqldev11

Mlevelcollresults


ということで、今日はここまで。

Have a good Week end!

| | | コメント (0) | トラックバック (0)

2007年6月 6日 (水)

PL/SQL de File Upload / Download #5



Kate Bush - The Whole Story - Experiment IV Kate Bush - The Whole Story - Experiment IV


PL/SQL de File Upload / Download の最終回。

さて、前回は、日本語ファイル名のファイルをダウンロードするとFirefoxやMozilla以外のブラウザでは文字化け等が発生するというところまでだった。

また、IE6では、Content-Disposition: attachment; filenameを指定する方法であれば文字化けを回避できるということも確認した。

ただ、Mac De Oracleでは一番大切な Safari。そのSafariでは常に文字化けしていた。残念!〜〜で終わらせないのがMac De Oracle.

調べた結果、文字エンコードをUTF-8で統一すれば回避できるのでは? という考えに至り、即実行!



OTN-J の Code Tipsでも公開されました。(2007/6/16更新)
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2235
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2236


下記手順で mod_plsqlのファイルアップロード/ダウンロード環境を再構築した。

  1. データベースキャラクタセットをAL32UTF8で新たなデータベースを作成する。JA16SJISTILDEからAL32UTF8へデータベースキャラクタセットを変更することはできないため、新たにデータベースを作成することになる。
  2. dads.confのPlsqlNLSLanguageをJAPANESE_JAPAN.JA16SJISTILDEからJAPANESE_JAPAN.AL32UTF8へ変更する。
  3. ストアドプロシージャで生成するHTMLのcharsetをShift_JISからUTF-8へ変更する。
  4. utl_url.escapeプロシージャでURLエンコードする際の文字コードをShift_JISからAL32UTF8へ変更する。

1.データベースの作成

DBCA (Database Configuration Assistant)を利用し、データベースキャラクタセット:AL32UTF8のデータベースをサクッと作成しちゃいます。時間のかかるのはこの部分だけですね。
既に、データベースキャラクタセットがAL32UTF8であればこのステップはスキップしてください。

2.dads.confの変更

PlsqlNLSLanguageをJAPANESE_JAPAN.JA16SJISTILDEからJAPANESE_JAPAN.AL32UTF8へ変更します。
# ============================================================================ 
# mod_plsql DAD Configuration File
# ============================================================================
# 1. Please refer to dads.README for a description of this file
# ============================================================================

# Note: This file should typically be included in your plsql.conf file with
# the "include" directive.

# Hint: You can look at some sample DADs in the dads.README file

# ============================================================================
<Location /plsql_de_fileupload>
SetHandler pls_handler
Order deny,allow
Allow from all
AllowOverride None
PlsqlDatabaseUsername scott
PlsqlDatabasePassword tiger
PlsqlDatabaseConnectString 192.168.1.19:1521:amazon
PlsqlAuthenticationMode Basic
PlsqlDefaultPage scott.fileUploadForm
PlsqlDocumentTablename scott.discusDocTable
PlsqlDocumentPath docs
PlsqlDocumentProcedure scott.fileDownloader
PlsqlNLSLanguage JAPANESE_JAPAN.AL32UTF8
</Location>

3.プロシージャの変更
(1)ファイルを最大5件同時にアップロードするフォームを表示するプロシージャの変更。

charsetをShift_JISからUTF-8へ変更するだけ。
CREATE OR REPLACE PROCEDURE fileUpLoadForm
IS
numOfFiles CONSTANT PLS_INTEGER := 5;
BEGIN
htp.htmlopen;
htp.headopen;
htp.meta('CONTENT-Type',null,'text/html; charset=UTF-8');
htp.title('PL/SQL de File Upload');
htp.headclose;

htp.bodyopen;
htp.centeropen;
htp.print('<div align="center" style="width:500; height:600">');
htp.STRONG(
cattributes=> 'style="font-size:24px; color:#ff0000; '
|| 'text-shadow:1px 2px 3px #7f7f7f"',
ctext=> 'PL/SQL de ファイルアップロード'
);
htp.prn('<br><br>');
htp.formopen(
cenctype=>'multipart/form-data',
curl=>'action',cmethod=>'POST'
);

FOR i IN 1..numOfFiles LOOP
htp.prn(
'<p><span style="text-shadow:1px 2px 3px #7f7f7f">ファイルの選択:</span>'
);
htp.formfile(cname=>'file');
htp.br;
END LOOP;
htp.prn('<br><br><p>');
htp.formsubmit(cattributes=>'align=right',cvalue=>'アップロード');

htp.formclose;
htp.print('</div>');
htp.centerclose;
htp.bodyclose;
htp.htmlclose;
END;
/


(2)<FORM>要素のaction属性に指定するプロシージャの変更

charsetをShift_JISからUTF-8へ変更するだけ。
CREATE OR REPLACE PROCEDURE action
(
file IN owa_util.vc_arr
) IS
vOpe# number(10);
BEGIN
htp.htmlopen;
htp.meta('CONTENT-TYPE',null,'text/html; charset=UTF-8');
htp.headopen;
htp.title('PL/SQL de File Upload (Action)');
htp.headclose;
htp.bodyopen;
htp.header(1,'アップロード・ステータス');
htp.print('以下のアップロードが終了しました。<br><br>');
htp.prn('<span style="color:#ff0000; text-shadow:1px 2px 3px #7f7f7f">');
FOR i IN file.FIRST..file.LAST LOOP
IF FILE(i) IS NOT NULL THEN
htp.print('-&gt; ' || file(i) || '<br>');
END IF;
END LOOP;
htp.print('</span>');

-- 同一操作でアップロードしたファイルへ、同一操作番号を設定する。
SELECT upload_operation#.nextval INTO vOpe# FROM dual;
FOR i IN file.FIRST..file.LAST LOOP
UPDATE DISCUSDOCTABLE SET operation# = vOpe# WHERE name=file(i);
END LOOP;

htp.bodyclose;
htp.htmlclose;
END;
/


(3)ダウンロード可能なファイルリストを表示するプロシージャの変更。

charsetをShift_JISからUTF-8へ変更、utl_url.escapeでURLエンコードする際の文字コードをShift_JISからAL32UTF8へ変更。
CREATE OR REPLACE PROCEDURE fileList
IS
BEGIN
htp.htmlopen;
htp.headopen;
htp.meta('CONTENT-Type',null,'text/html; charset=UTF-8');
htp.title('PL/SQL de File upload - ダウンロード可能なファイル一覧');
htp.headclose;
htp.bodyopen;
htp.print(
'<span style="text-shadow:1px 2px 3px #7f7f7f; font-size: 22px">'
|| 'ダウンロードファイル一覧</span><br><br>'
);
FOR doc IN (SELECT name FROM discusDocTable) LOOP
htp.anchor('docs/' || utl_url.escape(doc.name, false, 'AL32UTF8'), 'docs/' || doc.name);
htp.br;
END LOOP;
htp.bodyclose;
htp.htmlclose;
END;
/

(4)ファイルのダウンロード時にmod_plsqlから呼び出されるプロシージャの変更。

charsetをShift_JISからUTF-8へ変更。
CREATE OR REPLACE PROCEDURE fileDownloader
IS
vFilePath VARCHAR2(256);
BEGIN
vFilePath := SUBSTR(owa_util.get_cgi_env('PATH_INFO'), 2);
wpg_docload.download_file(vFilePath);
EXCEPTION
WHEN OTHERS THEN
htp.htmlopen;
htp.headopen;
htp.meta('Ccntent-Type',null,'text/html; charset=UTF-8');
htp.headclose;
htp.bodyopen;
htp.print('ダウンロードファイル:' || vFilePath);
htp.print('<strong style="color:#ff0000">' || sqlerrm() || '</strong>');
htp.bodyclose;
htp.htmlclose;
END fileDownloader;
/


最後に、各プラットフォーム、各ブラウザでダウンロードしているスクリーンショットを。
MacOSX
Safari:
Macosx_safari

Firefox:
Macosx_firefox

Opera9.2:
Macosx_opera92

Linux(TurboLinux8 , CentOS4.4)
Firefox:
Centos44_firefox

SeaMonkey:
Centos44_seamonkey103

Mozilla1.4.2(TurboLinux8):
Turbolinux_mozilla142

Windows XP Professional
IE6:
Windows_ie6

FIrefox:
Windows_firefox


結果として、データベースからHTMLまでの文字エンコードを UTF-8 に統一することで、Firefox以外のブラウザで発生していた文字化けを回避することができるということになる。   (^▽^)v

追記、Oracle10gでは、(おそらく Oracle11g以降も)、新規に作成するデータベースのキャラクタセットは、Unicodeが推奨されているので、データベース作成時のキャラクタセットは、AL32UTF8にしておいたほうがいいだろうね。

以下、マニュアルより引用


データベース・キャラクタ・セットとしてのUnicodeの選択

すべての新規システム配置にUnicodeを使用することをお薦めします。レガシー・システムも最終的にはUnicodeに移行することをお薦めします。現在システムをUnicodeで配置すると、利便性、互換性および拡張性の面で多くの利点があります。Oracle Databaseの包括的なサポートにより、Unicodeの利点を活かしながら高パフォーマンス・システムを高速かつ容易に配置できます。現時点で多言語データをサポートする必要がない場合、またはUnicodeが必要ない場合でも、長期的には新規システムに最適な選択となる可能性が高く、結局は時間を短縮してコストを節減し、競争上の優位性を得ることになります。Unicodeの詳細は、第6章「Unicodeを使用した多言語データベースのサポート」を参照してください。


ついでに、グローバリゼージョンガイドの6 Unicodeを使用した多言語データベースのサポートあたりも熟読して最終的にどの文字コードやエンコーディングにするか判断するべし。

| | | コメント (0) | トラックバック (0)

2007年6月 5日 (火)

PL/SQL de File Upload / Download #4



Howard Jones - The Peaceful Tour - Things Can Only Get BetterHoward Jones - The Peaceful Tour - Things Can Only Get Better

PL/SQL de File Upload / Download のつづきです。


前回は、MacOSXのSafariで発生する問題。日本語ファイル名のファイルをダウンロードした際、ファイル名が、 hostname.拡張子 のような形式で保存されてしまうというところまでだった
Firefoxでは全く問題が発生していないので、ブラウザ依存の問題ということなのだろうか。


ということで、今回は、MacOSX以外のプラットフォームで試した結果+αも。



OTN-J の Code Tipsでも公開されました。(2007/6/16更新)
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2235
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2236


ファイル名が日本語であるファイルをダウンロードした場合のファイル名の文字化けについて

・Firefox 2 (Mozilla含む) : MacOSX/Linux/Windows XP すべてのプラットフォームで
 文字化け等の問題は発生しない。

Firefox2macosxok Firefox2windowsok Mozillalinuxok


・IE6 : 文字化け。

Ieng_1


・Safari : 前述の通り。 hostname.拡張子 という形式で保存されてしまう。

・Opera 9.2 (MacOSX版): URLエンコードされた文字列.拡張子 という形式で保存されてしまう


Operamacosxng



文字エンコードが統べてUTF-8だったらどうなんだろう。。。解決できたりして。。。という思いつきは置いといて、今日は、Shift_JISのままでIE6での文字化けを(無理矢理?)解決する方法があったので載せておくことにする。

参考:
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19260-01/concept.htm#i1011220


※2007/6/8一部修正
以下のようにContent-Disposition: attachment; filename=UTF-8でURLエンコードしたファイル名を指定する方式であれば、IE6でも正しいファイル名で保存できるようになる。
MacOSXのSafariでは依然として改善されず、下記方法では文字化けしてしまう。 

以下の方法もマニュアルに記載されている方法だが、あくまで英語圏で作成されたマニュアルの翻訳なので、ファイル名が日本語である場合の文字化け回避方法までは記載されていない。
文字化けの回避はブラウザにも依存する部分なのでなかなか面倒ですね。文字コードやエンコーディングがいろいろとありすぎてうれしくないな〜。

create or replace PROCEDURE fileDownloader
IS
vFilePath VARCHAR2(256);
vUserAgent VARCHAR2(512);
vContentType discusdoctable.BLOB_CONTENT%TYPE;
vMimeType discusdoctable.MIME_TYPE%TYPE;
vDocSize discusdoctable.DOC_SIZE%TYPE;
vFileName discusdoctable.NAME%TYPE;

BEGIN
vFilePath := SUBSTR(owa_util.get_cgi_env('PATH_INFO'), 2);
vUserAgent := owa_util.get_cgi_env('HTTP_USER_AGENT');
IF INSTR(vUserAgent,'MSIE') != 0 THEN
-- IE (かなり無理矢理な対応)
-- IE6 - Version 6.0.290000.2180.xpsp_sp2_dgr.070227-2254 で動作確認!
-- 他のバージョンのIEではどうなるか未確認。。。
SELECT
SUBSTR(name, INSTR(name, '/')+1),
blob_content,
mime_type,
doc_size
INTO
vFileName,
vContentType,
vMimeType,
vDocSize
FROM
discusDocTable
WHERE
name = vFilePath;
OWA_UTIL.mime_header(vMimeType, FALSE);
HTP.p('Content-Length: ' || TO_CHAR(vDocSize));
HTP.p(
'Content-Disposition: attachment; filename=' || utl_url.escape(vFileName,false, 'AL32UTF8')
);
OWA_UTIL.http_header_close;
wpg_docload.download_file(vContentType);
ELSE
-- その他のブラウザ(MacOSX Safariでは日本語ファイル名のダウンロードは文字化けする)
wpg_docload.download_file(vFilePath);
END IF;
EXCEPTION
WHEN OTHERS THEN
htp.htmlopen;
htp.headopen;
htp.meta('Ccntent-Type',null,'text/html; charset=Shift_JIS');
htp.headclose;
htp.bodyopen;
htp.print('ダウンロードファイル:' || vFilePath);
htp.print('<strong style="color:#ff0000">' || sqlerrm() || '</strong>');
htp.bodyclose;
htp.htmlclose;
END fileDownloader;
/

IE6で日本語ファイル名のファイルをダウンロードしたスクリーンショット。
Ieok

| | | コメント (0) | トラックバック (0)

2007年6月 4日 (月)

PL/SQL de File Upload / Download #3



Foundation - The Best of House, Vol 2 - Somebody's Watching Me Foundation - The Best of House, Vol 2 - Somebody's Watching Me



PL/SQL de File Upload / Download のつづきです。

前回は、プロシージャの実装まで行ったので実際にアップロードとダウンロードを行った結果を載せておく。


以下、MacOSXのSafariとFirefox2でファイルアップロードを行ったスクリーンショット

Modplsql3_1
Modplsql3_2
Modplsql3_3





OTN-J の Code Tipsでも公開されました。(2007/6/16更新)
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2235
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2236


5.ファイルアップロード後のドキュメント表の内容の例

(1)〜(4)のようなプロシージャを実装するだけで簡単にファイルアップロード/ダウンロードを行うWebアプリケーションを作成できる。
ただし、多少の癖はあるようですが。。。

尚、OPERATION#が等しいファイルは同一操作でアップロードされたことを意味する。今回、拡張した部分です!。想定通りうまくいきました。 (^^。

SCOTT> col name for a46
SCOTT> col content_type for a10
SCOTT> set linesize 132
SCOTT> set pagesize 1000
SCOTT> select name,content_type,doc_size,operation# from discusdoctable;

NAME CONTENT_TY DOC_SIZE OPERATION#
---------------------------------------------- ---------- ---------- ----------
F1416265333/DSC00002.JPG BLOB 32970 1
F462708383/CocoaAdvanced.zip BLOB 6189625 1
F701507294/Cocoaセミナー上級編.pdf BLOB 2673700 2
F622467197/MyQuickTimeMovieSoundTrack#3.m4a BLOB 663868 2
F1413335442/PLSQL_COLLECTION_sample.txt BLOB 15865 2
F612154227/ユーザの等価化.txt BLOB 1706 2
F1814846582/secondLife.jpg BLOB 231581 2
F621341128/ピクチャ 1.png BLOB 904551 3

8行が選択されました。

SCOTT>


まず、MacOSXの SafariとFirefoxで同じファイルをダウンロードしてみたのだが、ファイル名が日本語のファイルについては、Safariでダウンロードしたファイルのファイル名称は元のファイル名称とは異なりダウンロード元サーバのIPアドレスになっていた。(ホスト名を指定していればホスト名になっていただろう)。
また、Firefox(Mozillaでも)であればWindows/Linux上であっても文字化け等は発生しなかった。という結果であった。

Modplsql3_4

SafariとFirefoxで ピクチャ 1.pngというファイルを開いてたところなのだが、どちらもファイルは開けるものの、Safariではファイル名が192.168.1.19.pngとなっている。。
Modplsql3_5


Safariでも Cocoaセミナー上級編スライド.pdfをダウンロードしたのだが、ダウンロードされたファイル名は、192.168.1.19.pdfとなってしまった。FirefoxやMozillaでは問題ないんですが。。。

Modplsql3_6 Modplsql3_7

Modplsql3_8

初回にも書いたのだが、今回の環境は全てのレイヤーで文字コードはシフトJISにしてある。それがSafariの挙動に影響しているような・・・・・。
ということで、今日はここまで。 次回へつづく。

| | | コメント (0) | トラックバック (0)

2007年6月 3日 (日)

PL/SQL de File Upload / Download #2



Ultravox - Vienna - ViennaUltravox - Vienna - Vienna


さて、環境が整ったのでプロシージャを作成する。
(今回はマニュアルに記載されているサンプルのようにそれぞれ独立したプロシージャとしたが、これらを纏めてパッケージ化しても問題ない。 私が仕事で依頼されたものならパッケージとして実装するでしょうね。)



OTN-J の Code Tipsでも公開されました。(2007/6/16更新)
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2235
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2236

4.ファイルアップロード/ダウンロードプロシージャの例

注)style属性のtext-shadowを利用していますが、MacOSXのSafari以外のブラウザではドロップシャドウ効果はありません。


URLの例)- hostnameが discus 、 portが 7780 である場合、以下のようなURLでアクセスする。
http://discus:7780/plsql_de_fileupload/fileuploadform


(1)ファイルを最大5件同時にアップロードするフォームを表示するプロシージャ。

参考:
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19260-01/concept.htm#i1005985
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19260-01/concept.htm#i1011204

CREATE OR REPLACE PROCEDURE fileUpLoadForm
IS
numOfFiles CONSTANT PLS_INTEGER := 5;
BEGIN
htp.htmlopen;
htp.headopen;
htp.meta('CONTENT-TYpe',null,'text/html; charset=Shift_JIS');
htp.title('PL/SQL de File Upload');
htp.headclose;

htp.bodyopen;
htp.centeropen;
htp.print('<div align="center" style="width:500; height:600">');
htp.STRONG(
cattributes=> 'style="font-size:24px; color:#ff0000; '
|| 'text-shadow:1px 2px 3px #7f7f7f"',
ctext=> 'PL/SQL de ファイルアップロード'
);
htp.prn('<br><br>');
htp.formopen(
cenctype=>'multipart/form-data',
curl=>'action',cmethod=>'POST'
);

FOR i IN 1..numOfFiles LOOP
htp.prn(
'<p><span style="text-shadow:1px 2px 3px #7f7f7f">ファイルの選択:</span>'
);
htp.formfile(cname=>'file');
htp.br;
END LOOP;
htp.prn('<br><br><p>');
htp.formsubmit(cattributes=>'align=right',cvalue=>'アップロード');

htp.formclose;
htp.print('</div>');
htp.centerclose;
htp.bodyclose;
htp.htmlclose;
END;
/


(2)<FORM>要素のaction属性に指定するプロシージャの例

下記サンプルコードでは、マニュアルに記載されているサンプルの他に、同一操作でアップロードされたファイルに対してシーケンスから採番した操作番号を設定する処理を追加している。

CREATE OR REPLACE PROCEDURE action
(
file IN owa_util.vc_arr
) IS
vOpe# number(10);
BEGIN
htp.htmlopen;
htp.meta('CONTENT-TYPE',null,'text/html; charset=Shift_JIS');
htp.headopen;
htp.title('PL/SQL de File Upload (Action)');
htp.headclose;
htp.bodyopen;
htp.header(1,'アップロード・ステータス');
htp.print('以下のアップロードが終了しました。<br><br>');
htp.prn('<span style="color:#ff0000; text-shadow:1px 2px 3px #7f7f7f">');
FOR i IN file.FIRST..file.LAST LOOP
IF FILE(i) IS NOT NULL THEN
htp.print('-&gt; ' || file(i) || '<br>');
END IF;
END LOOP;
htp.print('</span>');

-- 同一操作でアップロードしたファイルへ、同一操作番号を設定する。
SELECT upload_operation#.nextval INTO vOpe# FROM dual;
FOR i IN file.FIRST..file.LAST LOOP
UPDATE DISCUSDOCTABLE SET operation# = vOpe# WHERE name=file(i);
END LOOP;

htp.bodyclose;
htp.htmlclose;
END;
/


(3)ダウンロード可能なファイルリストを表示するプロシージャの例。

URLの例) - hostname = discus, port = 7780である場合。
http://discus:7780/plsql_de_fileupload/filelist

マニュアルに記載されているファイルダウンロードのサンプルは、同マニュアルに記載されている他のサンプルコードに比べ、少々不親切(端折り過ぎ)なサンプルコードになっている。(CGIなどのWebアプリケーションに慣れている方なら想像できなくもないのだが・・・・。)
下記サンプルコードを見れば容易に理解できると思うのだが、アップロードしたファイルをダウンロードするには、dads.confのPlsqlDocumentPathパラメータで指定したドキュメントルートのパスと、ドキュメント表のname列の値を連結したパスでダウンロードするファイル指すURLを生成できる。

CREATE OR REPLACE PROCEDURE fileList
IS
BEGIN
htp.htmlopen;
htp.headopen;
htp.meta('CONTENT-Type',null,'text/html; charset=Shift_JIS');
htp.title('PL/SQL de File upload - ダウンロード可能なファイル一覧');
htp.headclose;
htp.bodyopen;
htp.print(
'<span style="text-shadow:1px 2px 3px #7f7f7f; font-size: 22px">'
|| 'ダウンロードファイル一覧</span><br><br>'
);
FOR doc IN (SELECT name FROM discusDocTable) LOOP
htp.anchor('docs/' || utl_url.escape(doc.name, false, 'Shift_JIS'), 'docs/' || doc.name);
htp.br;
END LOOP;
htp.bodyclose;
htp.htmlclose;
END;
/

(4)ファイルのダウンロード時にmod_plsqlから呼び出されるプロシージャの例。

このプロシージャは、(3)の方法でダウンロードする場合には必須であり、dads.confのPlsqlDocumentProcedureパラメータで指定する。
このプロシージャのサンプルコードはマニュアルにも記載されているのだが少々難解ですね。
実際には、以下のように wpg_docload.download_file()プロシージャを実行するだけの処理なのだがマニュアルのサンプルコードを見ると少々混乱してしまう! 私も少々考えましたもん!!!

以下コードを見てもらうと実は非常にシンプルな操作であることに気付くと思う。 解説するまでも無いと思うが念のために解説しておくと、wpg_docload.download_file(環境変数PATH_INFOの値)を実行しているだけなのである。

CREATE OR REPLACE PROCEDURE fileDownloader
IS
vFilePath VARCHAR2(256);
BEGIN
vFilePath := SUBSTR(owa_util.get_cgi_env('PATH_INFO'), 2);
wpg_docload.download_file(vFilePath);
EXCEPTION
WHEN OTHERS THEN
htp.htmlopen;
htp.headopen;
htp.meta('Ccntent-Type',null,'text/html; charset=Shift_JIS');
htp.headclose;
htp.bodyopen;
htp.print('ダウンロードファイル:' || vFilePath);
htp.print('<strong style="color:#ff0000">' || sqlerrm() || '</strong>');
htp.bodyclose;
htp.htmlclose;
END fileDownloader;
/


以上でプロシージャの作成は終わり。 簡単ですよね!。 

次回へつづく。

| | | コメント (0) | トラックバック (0)

2007年6月 1日 (金)

PL/SQL de File Upload / Download #1

先日、OTN-Jでmod_plsqlが提供するファイルアップロード/ダウンロードフレームワークに関連するスレッドがあり、その中でのやり取りと、作成したサンプルコードに多少手を加えて公開しておく。
ファイルダウンロード及び、ドキュメント表の拡張方法など明確な解説が無いこともあり、一部マニュアルの行間を読みつつ試した結果を含む。
(OTN-JのCodeTipsへも投稿する予定)
http://otn.oracle.co.jp/forum/message.jspa?messageID=35011699&tstart=0


mod_plsqlのファイルアップロード/ダウンロードに関する詳細は、下記ドキュメント参照のこと。
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19260-01/concept.htm#i1009257
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19260-01/concept.htm#i1005866


※ファイルアップロード/ダウンロードサンプルの概要

・複数のファイル(この例では最大5件)を1操作でアップロードする。
・同一操作でアップロードされた複数のファイルは、同一の操作番号(OPERATION#)を
 設定する。

※環境

HTTP Servetとデータベースは同ーサーバ上に構築。(尚、$ORACLE_HOMEは別にしてインストールする必要がある。)
尚、今回はWindows XP Professional上に構築し、Oracle SQL Developer for MacOSXを利用してプロシージャを作成した。

・サーバ

・Oracle10g HTTP Server (Apache Standalone 10.1.2.0.0)
  - Oracle Database 10g Compaion CD Release 1 (10.1.0.3.0)
・Oracle10g Database R1 EE
  - Oracle Database 10g Release 1 (10.1.0.3.0)
  - データベースキャラクタセット: JA16SJISTILDE
・OWA - 10.1.2.0.6

・クライアント(ブラウザ)

・Safari 2 - MacOSX 10.4.9
・Opera 9.2 - MacOSX 10.4.9
・FireFox 2 - MacOSX 10.4.9
・Firefox 2 - Linux (TurboLinux 8 - powered by United Linux1.0)
・FireFox 2 - Linux (CentOS 4.4)
・IE6 - Windows XP Professional
・FireFox 2 - Windows XP Professional



OTN-J の Code Tipsでも公開されました。(2007/6/16更新)
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2235
http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2236


1.ドキュメント表の定義

ドキュメント表で定義する必要のある列に加て、OPERATION#という列を追加した。
尚、アップロードするコンテンツはすべて BLOB列へ格納する。
(同一操作でアップロードされたファイルに同一操作番号を設定する為に利用する。)
SCOTT> desc discusdoctable
名前 NULL? 型
----------------------------------------- -------- ----------------------------
NAME NOT NULL VARCHAR2(128)
MIME_TYPE VARCHAR2(128)
DOC_SIZE NUMBER
DAD_CHARSET VARCHAR2(128)
LAST_UPDATED DATE
CONTENT_TYPE VARCHAR2(128)
BLOB_CONTENT BLOB
OPERATION# NUMBER(10)

SCOTT>

2.操作番号を取得するシーケンスの定義

CREATE SEQUENCE UPLOAD_OPERATION#  
MINVALUE 1 MAXVALUE 9999999999
INCREMENT BY 1 START WITH 1
NOCACHE NOORDER NOCYCLE;

3.dads.confの定義

dads.confに関連するマニュアルは以下の通り。
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19260-01/concept.htm#i1012038
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19260-01/concept.htm#i1011190
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19260-01/concept.htm#i1010512

# ============================================================================ 
# mod_plsql DAD Configuration File
# ============================================================================
# 1. Please refer to dads.README for a description of this file
# ============================================================================

# Note: This file should typically be included in your plsql.conf file with
# the "include" directive.

# Hint: You can look at some sample DADs in the dads.README file

# ============================================================================
<Location /plsql_de_fileupload>
SetHandler pls_handler
Order deny,allow
Allow from all
AllowOverride None
PlsqlDatabaseUsername scott
PlsqlDatabasePassword tiger
PlsqlDatabaseConnectString 192.168.1.19:1521:amazon
PlsqlAuthenticationMode Basic
PlsqlDefaultPage scott.fileUploadForm
PlsqlDocumentTablename scott.discusDocTable
PlsqlDocumentPath docs
PlsqlDocumentProcedure scott.fileDownloader
PlsqlNLSLanguage JAPANESE_JAPAN.JA16SJISTILDE
</Location>


環境、構成ファイル、ドキュメント表及び、その拡張部分の定義はこれで終了。次回へつづく。
Making1

| | | コメント (0) | トラックバック (0)

2007年5月29日 (火)

Mac De Oracle : PL/SQL de Collection #4

さて、PL/SQLのコレクションでのお遊び。その4回目。

今回は、既に要素を持っているコレクションを空にしてみましょう。

第二回目にも似たようなことをやっているので、VarrayとNested Tableについては想像できると思いますが、Associative Arrayにいてはどうすればよいでしょう?

ということで、答えは以下のコードを読めば分かりますよ!

create or replace
PROCEDURE COLLECTIONS AS
TYPE VarrayType IS VARRAY(100) OF empType;
TYPE NestedTableType IS TABLE OF empType;
TYPE StrAssociativeArrayType IS TABLE OF empType INDEX BY VARCHAR2(50);
TYPE NumAssociativeArrayType IS TABLE OF empType INDEX BY BINARY_INTEGER;

myVarray VarrayType := VarrayType();
myNestedTable NestedTableType := NestedTableType();
myStrAssociativeArray StrAssociativeArrayType;
myNumAssociativeArray NumAssociativeArrayType;

claerMyStrAssociateArray StrAssociativeArrayType;
clearMyNumAssociateArray NumAssociativeArrayType;

myEmp empType;

CURSOR csrEmp IS
SELECT
job,
empno,
ename,
hiredate,
sal,
deptno
FROM
emp
ORDER BY
empno;

PROCEDURE printCount
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(
'Varray : '
|| myVarray.COUNT
|| ' 件の要素が存在します。'
);
DBMS_OUTPUT.PUT_LINE(
'Nested Table : '
|| myNestedTable.COUNT
|| ' 件の要素が存在します。'
);
DBMS_OUTPUT.PUT_LINE(
'Associative Array(String) :'
|| myStrAssociativeArray.COUNT
|| ' 件の要素が存在します。'
);
DBMS_OUTPUT.PUT_LINE(
'Associative Array(Number) :'
|| myNumAssociativeArray.COUNT
|| ' 件の要素が存在します。'
);
END;

BEGIN
FOR emp_rec IN csrEmp LOOP
myEmp := empType (
emp_rec.empno,
emp_rec.ename,
emp_rec.job,
emp_rec.hiredate,
emp_rec.sal,
emp_rec.deptno
);
myVarray.EXTEND(1);
myVarray(myVarray.COUNT) := myEmp;
myNestedTable.EXTEND(1);
myNestedTable(myNestedTable.COUNT) := myEmp;
myStrAssociativeArray(TO_CHAR(myEmp.empno) || ':' || myEmp.ename) := myEmp;
myNumAssociativeArray(myEmp.empno) := myEmp;
END LOOP;


DBMS_OUTPUT.PUT_LINE('**** コレクションに要素が存在している状態 ****');
printCount;


myVarray := VarrayType();
myNestedTable := NestedTableType();
myStrAssociativeArray := claerMyStrAssociateArray;
myNumAssociativeArray := clearMyNumAssociateArray;

DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('**** コレクションが空の状態 ****');
printCount;


END;
/


上記のコードの通り、Assciative Array(結合配列)を空にする方法は、同じ型の”空”結合配列をセットしてやればいいわけです。
今回の例では1次元の結合配列ですが、N次元の結合配列でも考え方は同じです。(VarrayやNested Tableとは異なります。)
ちなみに、第二回目にも書きましたが、VarrayやNexted Tableは、NULLにすることができます。その場合、VarrayやNexted Tableは、atomic nullであるためコンストラクタによる初期化が行われるまで一切の操作は行えないということもお忘れなく。

最後に、上記プロシージャの実行結果。

SCOTT> set serveroutput on size 10000 format wrapped
SCOTT> exec collections;
**** コレクションに要素が存在している状態 ****
Varray : 29 件の要素が存在します。
Nested Table : 29 件の要素が存在します。
Associative Array(String) :29 件の要素が存在します。
Associative Array(Number) :29 件の要素が存在します。

**** コレクションが空の状態 ****
Varray : 0 件の要素が存在します。
Nested Table : 0 件の要素が存在します。
Associative Array(String) :0 件の要素が存在します。
Associative Array(Number) :0 件の要素が存在します。

PL/SQLプロシージャが正常に完了しました。

SCOTT>

Collection4

今日はこのへんで。

| | | コメント (0) | トラックバック (0)

2007年5月27日 (日)

Mac De Oracle : PL/SQL de Collection #3

前回の続き

各コレクションの初期化の方法は見えたと思うので、今回は実際に要素をセットしたり取り出したりして遊んでみる。
最初にも書いたが今回セットするのはオブジェクト型。emp表をempno順に問い合せ、empType型として各コレクションの要素としてある。)

マニュアルなどを見ながらいろいろ試してみたほうが面白いと思うので特に解説はしません。。Enjoy!!

CREATE OR REPLACE
PROCEDURE COLLECTIONS AS
TYPE VarrayType IS VARRAY(100) OF empType;
TYPE NestedTableType IS TABLE OF empType;
TYPE StrAssociativeArrayType IS TABLE OF empType INDEX BY VARCHAR2(50);
TYPE NumAssociativeArrayType IS TABLE OF empType INDEX BY BINARY_INTEGER;

myVarray VarrayType := VarrayType();
myNestedTable NestedTableType := NestedTableType();
myStrAssociativeArray StrAssociativeArrayType;
myNumAssociativeArray NumAssociativeArrayType;

myEmp empType;
strIdx VARCHAR2(50);
numIdx PLS_INTEGER;

CURSOR csrEmp IS
SELECT
job,
empno,
ename,
hiredate,
sal,
deptno
FROM
emp
ORDER BY
empno;


BEGIN
FOR emp_rec IN csrEmp LOOP
myEmp := empType (
emp_rec.empno,
emp_rec.ename,
emp_rec.job,
emp_rec.hiredate,
emp_rec.sal,
emp_rec.deptno
);
myVarray.EXTEND(1);
myVarray(myVarray.COUNT) := myEmp;
myNestedTable.EXTEND(1);
myNestedTable(myNestedTable.COUNT) := myEmp;
myStrAssociativeArray(TO_CHAR(myEmp.empno) || ':' || myEmp.ename) := myEmp;
myNumAssociativeArray(myEmp.empno) := myEmp;
END LOOP;

DBMS_OUTPUT.PUT_LINE('**** VARRAY ****');
FOR j IN myVarray.FIRST..myVarray.LAST LOOP
DBMS_OUTPUT.PUT_LINE(
'myVarray(' || TO_CHAR(j) || ')=' || myVarray(j).TO_STRING()
);
END LOOP;

DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.PUT_LINE('**** NESTED TABLE ****');
FOR j IN myNestedTable.FIRST..myNestedTable.LAST LOOP
DBMS_OUTPUT.PUT_LINE(
'myNestedTable(' || TO_CHAR(j) || ')=' || myNestedTable(j).TO_STRING()
);
END LOOP;

DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.PUT_LINE('**** Associative Array (String) ****');
strIdx := myStrAssociativeArray.FIRST();
WHILE strIdx IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(
'myStrAssociativeArray(' || strIdx || ')=' || myStrAssociativeArray(strIdx).TO_STRING()
);
strIdx := myStrAssociativeArray.NEXT(strIdx);
END LOOP;

DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.PUT_LINE('**** Associative Array (Number) ****');
numIdx := myNumAssociativeArray.FIRST();
WHILE numIdx IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(
'myNumAssociativeArray(' || TO_CHAR(numIdx) || ')=' || myNumAssociativeArray(numIdx).TO_STRING()
);
numIdx := myNumAssociativeArray.NEXT(numIdx);
END LOOP;
END;
/

以下、SQL*Plusでの実行結果。
尚、SQL*Plusのset serveroutputコマンドのオプション format wrappedは覚えておくといいですよ。
このオプションの意味も特に解説はしません。 format wrappedがある場合と無い場合の違いはご自分で確認してみてください


SCOTT> set serveroutput on size 10000 format wrapped
SCOTT> exec collections;

**** VARRAY ****
myVarray(1)=7369,SMITH,CLERK,1980/12/17, 800.00,20
myVarray(2)=7499,ALLEN,SALESMAN,1981/02/20, 1600.00,30
myVarray(3)=7521,WARD,SALESMAN,1981/02/22, 1250.00,30
myVarray(4)=7566,JONES,MANAGER,1981/04/02, 2975.00,20
myVarray(5)=7654,MARTIN,SALESMAN,1981/09/28, 1250.00,30
myVarray(6)=7698,BLAKE,MANAGER,1981/05/01, 2850.00,30
myVarray(7)=7782,CLARK,MANAGER,1981/06/09, 2450.00,10
myVarray(8)=7788,SCOTT,ANALYST,1987/04/19, 3000.00,20
myVarray(9)=7839,KING,PRESIDENT,1981/11/17, 5000.00,10
myVarray(10)=7844,TURNER,SALESMAN,1981/09/08, 1500.00,30
myVarray(11)=7876,ADAMS,CLERK,1987/05/23, 1100.00,20
myVarray(12)=7900,JAMES,CLERK,1981/12/03, 950.00,30

**** NESTED TABLE ****
myNestedTable(1)=7369,SMITH,CLERK,1980/12/17, 800.00,20
myNestedTable(2)=7499,ALLEN,SALESMAN,1981/02/20, 1600.00,30
myNestedTable(3)=7521,WARD,SALESMAN,1981/02/22, 1250.00,30
myNestedTable(4)=7566,JONES,MANAGER,1981/04/02, 2975.00,20
myNestedTable(5)=7654,MARTIN,SALESMAN,1981/09/28, 1250.00,30
myNestedTable(6)=7698,BLAKE,MANAGER,1981/05/01, 2850.00,30
myNestedTable(7)=7782,CLARK,MANAGER,1981/06/09, 2450.00,10
myNestedTable(8)=7788,SCOTT,ANALYST,1987/04/19, 3000.00,20
myNestedTable(9)=7839,KING,PRESIDENT,1981/11/17, 5000.00,10
myNestedTable(10)=7844,TURNER,SALESMAN,1981/09/08, 1500.00,30
myNestedTable(11)=7876,ADAMS,CLERK,1987/05/23, 1100.00,20
myNestedTable(12)=7900,JAMES,CLERK,1981/12/03, 950.00,30

**** Associative Array (String) ****
myStrAssociativeArray(7369:SMITH)=7369,SMITH,CLERK,1980/12/17, 800.00,20
myStrAssociativeArray(7499:ALLEN)=7499,ALLEN,SALESMAN,1981/02/20, 1600.00,30
myStrAssociativeArray(7521:WARD)=7521,WARD,SALESMAN,1981/02/22, 1250.00,30
myStrAssociativeArray(7566:JONES)=7566,JONES,MANAGER,1981/04/02, 2975.00,20
myStrAssociativeArray(7654:MARTIN)=7654,MARTIN,SALESMAN,1981/09/28, 1250.00,30
myStrAssociativeArray(7698:BLAKE)=7698,BLAKE,MANAGER,1981/05/01, 2850.00,30
myStrAssociativeArray(7782:CLARK)=7782,CLARK,MANAGER,1981/06/09, 2450.00,10
myStrAssociativeArray(7788:SCOTT)=7788,SCOTT,ANALYST,1987/04/19, 3000.00,20
myStrAssociativeArray(7839:KING)=7839,KING,PRESIDENT,1981/11/17, 5000.00,10
myStrAssociativeArray(7844:TURNER)=7844,TURNER,SALESMAN,1981/09/08, 1500.00,30
myStrAssociativeArray(7876:ADAMS)=7876,ADAMS,CLERK,1987/05/23, 1100.00,20
myStrAssociativeArray(7900:JAMES)=7900,JAMES,CLERK,1981/12/03, 950.00,30

**** Associative Array (Number) ****
myNumAssociativeArray(7369)=7369,SMITH,CLERK,1980/12/17, 800.00,20
myNumAssociativeArray(7499)=7499,ALLEN,SALESMAN,1981/02/20, 1600.00,30
myNumAssociativeArray(7521)=7521,WARD,SALESMAN,1981/02/22, 1250.00,30
myNumAssociativeArray(7566)=7566,JONES,MANAGER,1981/04/02, 2975.00,20
myNumAssociativeArray(7654)=7654,MARTIN,SALESMAN,1981/09/28, 1250.00,30
myNumAssociativeArray(7698)=7698,BLAKE,MANAGER,1981/05/01, 2850.00,30
myNumAssociativeArray(7782)=7782,CLARK,MANAGER,1981/06/09, 2450.00,10
myNumAssociativeArray(7788)=7788,SCOTT,ANALYST,1987/04/19, 3000.00,20
myNumAssociativeArray(7839)=7839,KING,PRESIDENT,1981/11/17, 5000.00,10
myNumAssociativeArray(7844)=7844,TURNER,SALESMAN,1981/09/08, 1500.00,30
myNumAssociativeArray(7876)=7876,ADAMS,CLERK,1987/05/23, 1100.00,20
myNumAssociativeArray(7900)=7900,JAMES,CLERK,1981/12/03, 950.00,30

PL/SQLプロシージャが正常に完了しました。

SCOTT>

以下のスクリーンショットは、Oracle SQL Developerで上記プロシージャを作成、テストしているところ。
Collection3_sdev

今日はここまで。

| | | コメント (0) | トラックバック (0)

2007年5月25日 (金)

Mac De Oracle : PL/SQL de Collection #2

PL/SQL de Collectionのつづきです。

VarrayとNested Tableを利用するには、まず最初に初期化する必要がある。(Associated Arrayでは、その必要はない。)
オブジェクト指向的だとインスタンスの作成と言ったほうが分かりやすいですかね。

以下、VarrayType型とNestedTableType型では、インスタンスを作成しないと、Javaで言うところのNull Pointer Exceptionが発生する。という例。
PL/SQLなのでNull Pointer Exceptionではなく、ORA-6531が発生するのですがね。
(私個人としては、ORA-6531より、ORA-2309: atomic NULL violation の方がストレートで分かりやすいんでない? なんて思う事しばしば。初めてNested Tableを使おうとして、そのメッセージの意味が分からずハマったことも。。。)

SCOTT> l
1 DECLARE
2 -- VARRAY
3 TYPE VarrayType IS VARRAY(100) OF empType;
4 -- NESTED TABLE
5 TYPE NestedTableType IS TABLE OF empType;
6 -- ASSOCIATIVE ARRAY (formaly called PL/SQL TABLE)
7 TYPE StrAssociativeArrayType IS TABLE OF empType INDEX BY VARCHAR2(50);
8 TYPE NumAssociativeArrayType IS TABLE OF empType INDEX BY BINARY_INTEGER;
9
10 myVarray VarrayType;
11 myNestedTable NestedTableType;
12 myStrAssociativeArray StrAssociativeArrayType;
13 myNumAssociativeArray NumAssociativeArrayType;
14 BEGIN
15 DBMS_OUTPUT.PUT_LINE('Associative Array:' || myStrAssociativeArray.COUNT());
16
17 BEGIN
18 DBMS_OUTPUT.PUT_LINE('Varray:' || myVarray.COUNT());
19 EXCEPTION
20 WHEN OTHERS THEN
21 DBMS_OUTPUT.PUT_LINE('Varray: ' || SQLERRM());
22 END;
23
24 BEGIN
25 DBMS_OUTPUT.PUT_LINE('Nested Table:' || myNestedTable.COUNT());
26 EXCEPTION
27 WHEN OTHERS THEN
28 DBMS_OUTPUT.PUT_LINE('Nested Table:' || SQLERRM());
29 END;
30* END;
SCOTT> /
Associative Array:0
Varray: ORA-06531: 参照しているコレクションは初期化されていません。
Nested Table:ORA-06531: 参照しているコレクションは初期化されていません。

PL/SQLプロシージャが正常に完了しました。

SCOTT>


ちなみに、Associative Arrayは、atomic nullには出来ない。やってみれば分かると思いますが、コンパイルエラーになります。
なのでこの点については、コンパイラから注意されるので特に問題はないでしょうね。

SCOTT> l
1 DECLARE
2 -- VARRAY
3 TYPE VarrayType IS VARRAY(100) OF empType;
4 -- NESTED TABLE
5 TYPE NestedTableType IS TABLE OF empType;
6 -- ASSOCIATIVE ARRAY (formaly called PL/SQL TABLE)
7 TYPE StrAssociativeArrayType IS TABLE OF empType INDEX BY VARCHAR2(50);
8 TYPE NumAssociativeArrayType IS TABLE OF empType INDEX BY BINARY_INTEGER;
9
10 myVarray VarrayType;
11 myNestedTable NestedTableType;
12 myStrAssociativeArray StrAssociativeArrayType;
13 myNumAssociativeArray NumAssociativeArrayType;
14 BEGIN
15 myStrAssociativeArray := NULL;
16 DBMS_OUTPUT.PUT_LINE('Associative Array:' || myStrAssociativeArray.COUNT());
17
18 myVarray := NULL;
19 BEGIN
20 DBMS_OUTPUT.PUT_LINE('Varray:' || myVarray.COUNT());
21 EXCEPTION
22 WHEN OTHERS THEN
23 DBMS_OUTPUT.PUT_LINE('Varray: ' || SQLERRM());
24 END;
25
26 myNestedTable := NULL;
27 BEGIN
28 DBMS_OUTPUT.PUT_LINE('Nested Table:' || myNestedTable.COUNT());
29 EXCEPTION
30 WHEN OTHERS THEN
31 DBMS_OUTPUT.PUT_LINE('Nested Table:' || SQLERRM());
32 END;
33* END;
SCOTT> /
myStrAssociativeArray := NULL;
*
行14でエラーが発生しました。:
ORA-06550: 行15、列28:
PLS-00382: 式の型が正しくありません。
ORA-06550: 行15、列3:
PL/SQL: Statement ignored


SCOTT>


VarrayやNested Tableを利用する際には、事前にコンストラクタを実行して初期化しておく必要がありますが、実行時エラーの危険を避けるため、定義時に初期化しておく方が無難でしょう。
以下の例では、各変数の宣言時にコンストラクタであるVarrayType()や、NestedTableType()を実行し初期化しています。

SCOTT> l
1 DECLARE
2 -- VARRAY
3 TYPE VarrayType IS VARRAY(100) OF empType;
4 -- NESTED TABLE
5 TYPE NestedTableType IS TABLE OF empType;
6 -- ASSOCIATIVE ARRAY (formaly called PL/SQL TABLE)
7 TYPE StrAssociativeArrayType IS TABLE OF empType INDEX BY VARCHAR2(50);
8 TYPE NumAssociativeArrayType IS TABLE OF empType INDEX BY BINARY_INTEGER;
9
10 myVarray VarrayType := VarrayType();
11 myNestedTable NestedTableType := NestedTableType();
12 myStrAssociativeArray StrAssociativeArrayType;
13 myNumAssociativeArray NumAssociativeArrayType;
14 BEGIN
15 DBMS_OUTPUT.PUT_LINE('Associative Array:' || myStrAssociativeArray.COUNT());
16 DBMS_OUTPUT.PUT_LINE('Varray:' || myVarray.COUNT());
17 DBMS_OUTPUT.PUT_LINE('Nested Table:' || myNestedTable.COUNT());
18* END;
SCOTT> /
Associative Array:0
Varray:0
Nested Table:0

PL/SQLプロシージャが正常に完了しました。

SCOTT>

こんどは問題なく要素数を表示できましたね。 この時点で、Associative Array、Varray、Nested Tableの各コレクションは空の状態。

尚、上記の例では、いきなりコレクションのメソッド (COUNT)を使っているが、コレクションで利用できるメソッドは以下のマニュアルで確認できる。
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/appdev.102/B19257-01/langelems.html#6420

ということで、今日はここまで。

Oracleのオブジェクトに関しては、
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/appdev.102/B19256-01/adobjint.htm#sthref39を中心に読めばそれなりに理解できるかと。

| | | コメント (0) | トラックバック (0)

2007年5月22日 (火)

Mac De Oracle : PL/SQL de Collection #1

最近のOracle Master試験は、PL/SQLは試験範囲に含まれていないわけですが、Oracleの開発や運用管理でもPL/SQLを利用する場面は少なく無いと思います。
(Mac De OracleでもPL/SQL関連ネタは多いです。)

Oracleデータベースありきですけが、Webアプリケーションからバッチ処理、さらに、運用管理、移行時など、PL/SQLは利用範囲が意外と広いのです。

ところで、
ブログの検索キーワードで、ORA-20000〜ORA-20999に次いで多いのが、実は、PL/SQLネタ。
Oracle Master試験の対象外になったことが影響しているんでしょうかねぇ?
Oracle Masterだからといっても、PL/SQLを使いこなせるとは限らない? という状況になりつつあるなんて話もちらほら。 少々心配です。

ということで、PL/SQLの Collectionについて少し書いてみるか! と思った次第。


では、早速。。。

PL/SQLで扱えるコレクションは、

・Associative Array
・Nested Table
・Varray

の3種。


・Associative Array


結合配列(索引付き配列と呼ばれたり、以前はPL/SQL表とも呼ばれていました。)これだけ日本語で書いてもイメージできないですよね。おそらく。
Javaをやっている方には、HashTableみたいな〜〜。とか、Perlをやっている方には連想配列みたいな〜。とか説明すること、「あ〜〜〜! なるほど。」とイメージできるようですね。(〜みたいな〜〜、なんですけどね。)

・Nested Table

Oracleが、Object Relational Database (ORDB)へと進化した際(Oracle8だったか?!)に登場したのコレクションの一つが、このNested Table(ネスト表)。このNested Tableは、Associate Arrayとは異なり、データベースへ格納することも可能。
また、PL/SQLで利用する場合、Associative Arrayとは異なり、事前に初期化しておく必要がある。
初期化って何? ということになりますが、要するにインスタンスを作成しておいてね。ってことになりますね。(詳細は簡単な例も含めて別エントリにて。)
#マニュアルには「初期化」と書いてあるので、そのまま使っています。

・Varray

Oracleが、Object Relational Database (ORDB)へと進化した際に登場したもう一つのコレクションがこのVarray。
事前に要素数が分かっている場合に利用することが多い。データベースに格納することができるが、更新する際にはコレクション全体を更新する必要がある。
PL/SQLで利用するには、Nested Table同様、事前に初期化しておく必要がある。

メモリを食らいつくすくらい巨大なコレクションを作る方も居たりして。。。。
限度はあるよ何事も。。。。。 (^^;;;

PL/SQLのコレクションに関するマニュアルは以下
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/appdev.102/B19257-01/collections.html#740

ということで、今回は準備だけ。

Object Relationalな部分にもちょっとだけ足を踏み込む次いでに、Collectionに格納する要素もオブジェクト型にしておきましょう。

以下のようにempType型を定義し、TO_STRING() メンバーファンクション(Java等では、メソッド相当)を定義しておく。
(見ての通り、 empTypeの値をCSV形式の文字列にするファンクション。)

ちなみに、デフォルトコンストラクタをオーバーライドしたり、コンストラクタを追加することもできますよ。(簡単な例は別エントリにて)

create or replace TYPE empType AS OBJECT
(
empno NUMBER(4),
ename VARCHAR2(10, BYTE),
job VARCHAR2(9, BYTE),
hiredate DATE,
sal NUMBER(7,2),
deptno NUMBER(2),
MEMBER FUNCTION TO_STRING RETURN VARCHAR2
);
/
create or replace TYPE BODY empType AS
MEMBER FUNCTION TO_STRING
RETURN VARCHAR2 IS
BEGIN
RETURN
TO_CHAR(SELF.empno) ||','||
SELF.ename ||','||
SELF.job ||','||
TO_CHAR(SELF.hiredate, 'YYYY/MM/DD') ||','||
TO_CHAR(SELF.sal, '99999.99') ||','||
TO_CHAR(SELF.deptno);
END;
END;
/


Prepare_collection_sample
今日はここまで。

| | | コメント (0) | トラックバック (0)

2007年4月19日 (木)

DBマガジン2007年6月号

OTN-Jのこのスレッドでアナウンスされていますが、私がOTN-Jに投稿したCodeTips#1531(OTN-JのCodeTipsを参照するにはOTNにログインする必要があります。)が、月刊DBマガジン 6月号 (4/24発売)の OTN傑作選 Oracleデータベース Tipsで紹介されるとのこと。。

ちなみに、投稿したコードと Oracle SQL Developerを絡めたネタは、こちら。

思いつきで書いたコード及び方法ですが、どなたかのお役に立っているようですね。
選んでいただき、ありがとうございます。 --> OTN-Jシスオペ様。  m(_ _)m

| | | コメント (0) | トラックバック (0)

2007年4月10日 (火)

Oracle SQL Developer #12

Oracle SQL Developer 1.1 Patch 1 (1.1.1.25.14) for MacOSX でと思ったが、Oracle SQL Developer 1.1 Patch 2 (1.1.2.25.79) for MacOSX が既にリリースされていたので最新版で、Oracle SQL Developerのデバッグ機能を試してみた。

http://www.oracle.com/technology/software/products/sql/index.html
尚、ダウンロードにはユーザ登録(無料)が必要。

リリースノートは以下。
http://www.oracle.com/technology/products/database/sql_developer/files/relnotes_v1.1.html

SQL Developer 1.1 Path 1(1.1.1.25.14) for MacOSXの時点で確認できていたので、ネタとしては少々鮮度が落ちるが、PL/SQLのデバッガ機能でハングが発生しなくなった。 
1.0か、1.1のころデバッグ機能を試した時は、デバッグモードで実行するとSQL Developerがハングしてしまい強制終了するしか無かったが、前回リリースされた Oracle SQL Developer 1.1 Patch 1 (1.1.1.25.14) for MacOSXで解決されていた。
これができるようになれば MacOSX上でもPL/SQLの開発が楽になりますね。

以下、Oracle10g R2 EE 10.2.0.2.0に接続してdebugしている画面のスナップショット。

Oraclesqldeveloper_debug

ちなみに、Oracle SQL Developerでデバッグ機能を利用するには、DEBUG CONNECT SESSION及び、DEBUG ANY PROCEDUREシステム権限が必要。もし実行するユーザが該当システム権限を付与されていない場合、以下のようなメッセージが返される。

Connecting to the database pleco.
Executing PL/SQL: ALTER SESSION SET PLSQL_DEBUG=TRUE
Executing PL/SQL: CALL DBMS_DEBUG_JDWP.CONNECT_TCP( '192.168.1.2', '53865' )
ORA-01031: 権限が不足しています。
ORA-06512: "SYS.DBMS_DEBUG_JDWP", 行68
ORA-06512: 行1
This session requires DEBUG CONNECT SESSION and DEBUG ANY PROCEDURE user privileges.
Process exited.
Disconnecting from the database pleco.

| | | コメント (0) | トラックバック (0)

2007年4月 9日 (月)

ORA-20001

マニュアルにもしっかり記載されているが、意外に多いなぁと感じた検索ワードの ”ORA-20001”を取り上げておこうと思います。

ORA-20001 を エラーメッセージマニュアルで確認してみると、

ORA-20000: %s
原因:
このメッセージが発生する原因となったストアド・プロシージャraise_application_errorが呼び出されました。
処置:
エラー・メッセージの記述どおりに問題を修正するか、詳細をアプリケーション管理者またはDBAに問い合せてください。

と、少々、不親切な書き方になっています。

上記、エラーメッセージの解説からは、ORA-20001が、ORA-20000と同じ、ユーザ定義エラーとして予約されているエラーコードであるとは読み取れません。
(Oracleさん、もうちょっとだけ、親切なドキュメントに修正してくれるとありがたいのですが・・・・・)


PL/SQLに慣れた方なら、最初に、冒頭でリンクしているこのマニュアルを見る確立は高いでしょうが、そうで無い方は、ORA-20001というエラーコードを見て、最初に参照するマニュアルはエラーメッセージマニュアルであることの方が多いはず!。 

で、「マニュアルに記載されていない!」 ということになり、ハマるわけです。。。。ね。

前置きはこれくらいにして、本題。

Oracle Database PL/SQLユーザーズ・ガイドおよびリファレンス 10g リリース2(10.2)に書かれているように、ORA-20000からORA-20999は、ユーザが自由にメッセージを割り当てる事ができるように予約されているエラーコードだ。

つまり、ORA-20000 というエラーコードには、Oracle側では対応するエラーメッセージが割り当てられていない。
ユーザが自由にエラーメッセージを割当て意味を持たせることができるエラーコードなんですよ。

たとえば、プロシージャAでは、ORA-20000は、「URLが指定されていません。」というエラーメッセージを割り当てたり、ファンクションBでは、ORA-20001には「指定された予約がありません。」というメッセージを割り当てることができるわけです。


尚、ORA-20000からORA-20999までのエラーコードはユーザが自由に設定できるエラーコードであるため、ほとんどの場合、プロジェクト毎に標準化されることが多いと思います。理由は、ORA-20000からORA-20999のエラーコードに対して一意にエラーメッセージを割り当て、1つのエラーコードが複数の意味を持たないようにするためです。
私が関わったプロジェクトでは、台帳で一元管理し、リリース後は該当システム独自のエラーメッセージマニュアルとしても利用させました。

サンプルコードは、Oracle Database PL/SQLユーザーズ・ガイドおよびリファレンス 10g リリース2(10.2)にもあるのでそちらも参照してください。

以下、簡単なサンプル。。。と行きたいところだがここまで。
(後日、このエントリを更新してサンプルを載せるかも。。。マニュアルに記載されているから追記しないかも。。。。気分次第です。)

| | | コメント (0) | トラックバック (0)

2007年4月 2日 (月)

Mac De PL/SQL RSS Reader #33 (XSLT編 #5)

さて、前回からのつづき。

●プロシージャのソースです。

HTMLで表示しているため以下のソースをそのままコピー/ペーストするとコンパイルエラーなど不都合が発生することがあるかもしれませんので、ソースを利用される場合は、XSLTスタイルシートも含め、こちらからダウンロードしたソースをお使いください。 source.zip (3.9K)

CREATE OR REPLACE 
PROCEDURE RSS_READER_XSLT
(
i_feedXmlUrl IN VARCHAR2,
i_feedXmlCharset IN VARCHAR2,
i_styleSheetDir IN VARCHAR2,
i_styleSheetName IN VARCHAR2,
DEBUG_ IN BOOLEAN DEFAULT FALSE
)
AS
--===========================================================
rss_reader_xslt.sql : 2007/3/26 - Mac De Oracle
https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/
--============ ===============================================

--============ TYPEs/VARIABLEs ===============================================
XSL_CHARSET_ CONSTANT VARCHAR(30) := 'UTF8';
v_feedXmlUrl VARCHAR2(32767);
v_feedXmlCharset VARCHAR2(30);
v_styleSheetDir VARCHAR2(30);
v_styleSheetName VARCHAR2(100);

myParser DBMS_XMLPARSER.Parser;
v_feedDoc DBMS_XMLDOM.DomDocument;
v_feedSourceClob CLOB;

myProcessor DBMS_XSLPROCESSOR.Processor;
v_xsl DBMS_XSLPROCESSOR.StyleSheet;
v_xslDoc DBMS_XMLDOM.DomDocument;
v_xslSourceClob CLOB;
v_xslDocFragment DBMS_XMLDOM.DomdocumentFragment;
v_transformedDoc DBMS_XMLDOM.DomNode;

v_clobbuffer CLOB;

--========== Internal PROCEDUREs/FUNCTIONs ===================================
PROCEDURE getXMLSource(
i_url IN VARCHAR2,
i_charset IN VARCHAR2 DEFAULT 'UTF-8',
io_sourceClob IN OUT NOCOPY CLOB
)
IS
v_httpReq UTL_HTTP.REQ;
v_httpResp UTL_HTTP.RESP;
v_xmlSource VARCHAR2(1024);
BEGIN
v_httpReq := UTL_HTTP.BEGIN_REQUEST(i_url);
UTL_HTTP.SET_HEADER(
v_httpReq,
'User-Agent',
'Oracle UTL_HTTP/Oracle10g R1;'
);

UTL_HTTP.SET_HEADER(
v_httpReq,
'Content-Type',
'text/xml;charset='||i_charset
);

v_httpResp := UTL_HTTP.GET_RESPONSE(v_httpReq);
DBMS_LOB.CREATETEMPORARY(io_sourceClob, FALSE);
BEGIN
LOOP
UTL_HTTP.READ_LINE(v_httpResp, v_xmlSource, true);
v_xmlSource := v_xmlSource || UTL_TCP.CRLF;
DBMS_LOB.WRITEAPPEND(
io_sourceClob,
LENGTH(v_xmlSource),
v_xmlSource
);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(v_httpResp);
END;
END getXMLSource;

PROCEDURE drawFeedEntries(
i_buffer IN CLOB
)
IS
chunkSize_ CONSTANT PLS_INTEGER := 10922;
v_numOfChunk PLS_INTEGER;
v_src CLOB;
v_tempStr VARCHAR2(32767);
BEGIN
v_src := i_buffer;
FOR chunk# IN 0..CEIL(DBMS_LOB.GETLENGTH(v_src) / chunkSize_) LOOP
v_tempStr := SUBSTR(i_buffer, (chunk#*chunkSize_)+1, chunkSize_);
HTP.PRN(v_tempStr);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(
'Error Occured : rss_reader_xslt.drawFeedEntries(CLOB).'
);
DBMS_OUTPUT.PUT_LINE(sqlerrm());
RAISE;

END drawFeedEntries;

-- for DEBUG
PROCEDURE pl(text IN CLOB)
IS
BEGIN
IF length(text)> 80 THEN
FOR i IN 1..(trunc(length(text)/80)+1) LOOP
dbms_output.put_line(substr(text,1+ 80*(i-1),80));
END LOOP;
ELSE
dbms_output.put_line(text);
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error occured : pl(VARCHAR2).');
RAISE;
END pl;

--
--
--****************************************************************************
-- Main
--****************************************************************************
BEGIN

v_feedXmlUrl := i_feedXmlUrl;
v_feedXmlCharset := i_feedXmlCharset;
v_styleSheetDir := UPPER(i_styleSheetDir);
v_styleSheetName := i_styleSheetName;

IF v_feedXmlUrl IS NULL THEN
RAISE_APPLICATION_ERROR(-20000, 'FEEDのURLを指定してください。');
END IF;
IF v_styleSheetDir IS NULL THEN
RAISE_APPLICATION_ERROR(
-20001,
'XSLTスタイルシートが保存されているディレクトリを指定してください。'
);
END IF;
IF v_styleSheetName IS NULL THEN
RAISE_APPLICATION_ERROR(-20002, 'XSLTスタイルシート名を指定してください。');
END IF;

-- RSS FEEDの取得する
getXMLSource(
i_url => v_feedXmlUrl,
i_charset => v_feedXmlCharset,
io_sourceClob => v_feedSourceClob
);
myParser := DBMS_XMLPARSER.NEWPARSER();
DBMS_XMLPARSER.PARSECLOB(myParser, v_feedSourceClob);
v_feedDoc := DBMS_XMLPARSER.GETDOCUMENT(myParser);

-- XSLTスタイルシートを読み込む
v_xslSourceClob := DBMS_XSLPROCESSOR.READ2CLOB(
v_styleSheetDir,
v_styleSheetName,
NLS_CHARSET_ID(XSL_CHARSET_)
);
DBMS_XMLPARSER.PARSECLOB(myParser, v_xslSourceClob);
v_xslDoc := DBMS_XMLPARSER.GETDOCUMENT(myParser);
v_xsl := DBMS_XSLPROCESSOR.NEWSTYLESHEET(v_xslDoc, NULL);

-- DOMにXSLTスタイルシートを適用する
myProcessor := DBMS_XSLPROCESSOR.NEWPROCESSOR();
v_xslDocFragment := DBMS_XSLPROCESSOR.PROCESSXSL(
myProcessor,
v_xsl,
v_feedDoc
);
v_transformedDoc := DBMS_XMLDOM.MAKENODE(v_xslDocFragment);
DBMS_LOB.CREATETEMPORARY(v_clobbuffer, FALSE);
DBMS_XMLDOM.WRITETOCLOB(v_transformedDoc, v_clobbuffer);

-- 変換後のHTMLを出力する
IF DEBUG_ THEN
pl(v_clobbuffer);
ELSE
drawFeedEntries(v_clobbuffer);
END IF;

DBMS_LOB.FREETEMPORARY(v_feedSourceClob);
DBMS_LOB.FREETEMPORARY(v_xslSourceClob);
DBMS_LOB.FREETEMPORARY(v_clobbuffer);
DBMS_XSLPROCESSOR.FREESTYLESHEET(v_xsl);
DBMS_XMLDOM.FREEDOCUMENT(v_feedDoc);
DBMS_XMLDOM.FREEDOCUMENT(v_xslDoc);
DBMS_XMLPARSER.FREEPARSER(myParser);
DBMS_XSLPROCESSOR.FREEPROCESSOR(myProcessor);

EXCEPTION
WHEN OTHERS THEN
IF v_feedSourceClob IS NOT NULL THEN
DBMS_LOB.FREETEMPORARY(v_feedSourceClob);
END IF;
IF v_xslSourceClob IS NOT NULL THEN
DBMS_LOB.FREETEMPORARY(v_xslSourceClob);
END IF;
IF v_clobbuffer IS NOT NULL THEN
DBMS_LOB.FREETEMPORARY(v_clobbuffer);
END IF;
DBMS_XSLPROCESSOR.FREESTYLESHEET(v_xsl);
DBMS_XMLDOM.FREEDOCUMENT(v_feedDoc);
DBMS_XMLDOM.FREEDOCUMENT(v_xslDoc);
DBMS_XMLPARSER.FREEPARSER(myParser);
DBMS_XSLPROCESSOR.FREEPROCESSOR(myProcessor);

IF DEBUG_ THEN
DBMS_OUTPUT.PUT_LINE('RSS Reader error:'||SQLERRM());
ELSE
HTP.TABLEOPEN();
HTP.TABLEROWOPEN();
HTP.TABLEHEADER('RSS Reader error:'||SQLERRM());
HTP.TABLEROWCLOSE();
HTP.TABLECLOSE();
END IF;
END RSS_READER_XSLT;
/



このサンプルは OTN-Jの CodeTipsでも公開しています。

(1/3) http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2135
(2/3) http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2137
(3/3) http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2138

| | | コメント (0) | トラックバック (0)

2007年4月 1日 (日)

Mac De PL/SQL RSS Reader #32 (XSLT編 #4)

つづきです。

XSLTスタイルシートに関して細かく解説するつもりはありませんが、少しだけ解説しておくと。

RSS 0.91/RSS 0.92/ RDF(RSS)1.0ATOM 0.3 / ATOM 1.0の FEEDからタイトルと元ネタへのリンク、各エントリ毎の要約とリンク、そして、存在してれば、[エントリの作成日]を抜き出してHTMLへ変換している。
尚、OTN-JRSS2.0で配信されているが 各エントリには、<pubDate>要素や、<dc:date>要素は存在せず、拡張された<jf:creationDate>要素に作成日を持っているようなので、<jf:creationDate>要素を処理している。

XSLTについては、@ITの記事などや、検索すれば沢山ヒットするので理解できる範囲かと。。。
(難しいことはやってないですから。)
http://www.atmarkit.co.jp/fxml/tanpatsu/xslt/xslt04.html
http://www.atmarkit.co.jp/fxml/tanpatsu/xslt/xslt10.html

尚、DBMS_XSLPROCESSORパッケージなどは、
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/appdev.102/B19245-01/d_xslpro.htmを参照のこと。

簡単な解説はこれぐらいにして、以下、XSLTスタイルシートのソース。
#1時間くらいで作ったものなので、バグが無いとは言えません。あしからず。
#ちなみに、私が読んでいるFEEDでは特に問題は発生していません。

但し、こんな問題もあり2月に入ってからDTDが開けないエラーが発生している。大抵、他の形式でも配布しているからRSS0.91使わなきゃいいだけかも。。

尚、今回作成したXSLTスタイルシートとPL/SQLのソースコードは、こちらからダウンロードできます。 source.zip (3.9K)

<?xml version="1.0" encoding="utf-8"?>
<!-- ===================================================================
rss2html.xsl : 2007/3/26 - Mac De Oracle
https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/
==================================================================== -->
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rss10="http://purl.org/rss/1.0/"
xmlns:atom03="http://purl.org/atom/ns#"
xmlns:atom10="http://www.w3.org/2005/Atom"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:jf="http://www.jivesoftware.com/xmlns/jiveforums/rss"
>


<xsl:output method="html" encoding="utf-8" />
<xsl:template match="/">
<table border="0" cellpadding="0" cellspacing="2">
<tr>
<th>
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:apply-templates select="//rdf:RDF/rss10:channel/rss10:link"/>
<xsl:apply-templates select="//atom03:feed/atom03:link/@href"/>
<xsl:apply-templates select="//atom10:feed/atom10:link/@href"/>
<xsl:apply-templates select="//rss/channel/link"/>
</xsl:attribute>
<xsl:apply-templates select="//rdf:RDF/rss10:channel/rss10:title"/>
<xsl:apply-templates select="//atom03:feed/atom03:title"/>
<xsl:apply-templates select="//atom10:feed/atom10:title"/>
<xsl:apply-templates select="//rss/channel/title"/>
</xsl:element>
</th>
</tr>
<xsl:apply-templates select="//rdf:RDF/rss10:item" mode="normal"/>
<xsl:apply-templates select="//atom03:feed/atom03:entry" mode="normal"/>
<xsl:apply-templates select="//atom10:feed/atom10:entry" mode="normal"/>
<xsl:apply-templates select="//rss/channel/item" mode="normal"/>
</table>
</xsl:template>


<!-- RDF/RSS1.0 template -->
<xsl:template match="rss10:item" mode="normal">
<tr>
<td valign="top">
<xsl:element name="a">
<xsl:attribute name="href"><xsl:value-of select="rss10:link"/></xsl:attribute>
<xsl:attribute name="title"><xsl:value-of select="rss10:link"/></xsl:attribute>
<xsl:attribute name="style">color:#FF0000</xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
<xsl:value-of select="concat(rss10:title, ' - 続きを読む...')" />
</xsl:element>
<xsl:choose>
<xsl:when test="dc:date">
<xsl:value-of select="concat(' - ', substring-before(dc:date,'T'))"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</td>
</tr>
</xsl:template>

<xsl:template match="//rdf:RDF/rss10:channel/rss10:title">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="//rdf:RDF/rss10:channel/rss10:link">
<xsl:value-of select="."/>
</xsl:template>


<!-- ATOM 1.0 template -->
<xsl:template match="//atom10:feed/atom10:entry" mode="normal">
<tr>
<td valign="top">
<xsl:element name="a">
<xsl:attribute name="href"><xsl:value-of select="atom10:link/@href"/></xsl:attribute>
<xsl:attribute name="title"><xsl:value-of select="atom10:summary"/></xsl:attribute>
<xsl:attribute name="style">color:#FF0000</xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
<xsl:value-of select="concat(atom10:title,' - 続きを読む...')"/>
</xsl:element>
<xsl:choose>
<xsl:when test="atom10:published">
<xsl:value-of select="concat(' - ',substring-before(atom10:published,'T'))"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</td>
</tr>
</xsl:template>

<xsl:template match="//atom10:feed/atom10:title">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="//atom10:feed/atom10:link/@href">
<xsl:if test="../@rel = 'alternate'">
<xsl:if test="../@type = 'text/html'">
<xsl:value-of select="."/>
</xsl:if>
</xsl:if>
</xsl:template>


<!-- ATOM 0.3 template -->
<xsl:template match="//atom03:feed/atom03:entry" mode="normal">
<tr>
<td valign="top">
<xsl:element name="a">
<xsl:attribute name="href"><xsl:value-of select="atom03:link/@href"/></xsl:attribute>
<xsl:attribute name="title"><xsl:value-of select="atom03:summary"/></xsl:attribute>
<xsl:attribute name="style">color:#FF0000</xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
<xsl:value-of select="concat(atom03:title, ' - 続きを読む...')"/>
</xsl:element>
<xsl:choose>
<xsl:when test="atom03:issued">
<xsl:value-of select="concat(' - ', substring-before(atom03:issued, 'T'))"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</td>
</tr>
</xsl:template>

<xsl:template match="//atom03:feed/atom03:title">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="//atom03:feed/atom03:link/@href">
<xsl:if test="../@rel = 'alternate'">
<xsl:if test="../@type = 'text/html'">
<xsl:value-of select="."/>
</xsl:if>
</xsl:if>
</xsl:template>


<!-- RSS 0.91/0.92/2.0 template -->
<xsl:template match="//rss/channel/item" mode="normal">
<tr>
<td valign="top">
<xsl:element name="a">
<xsl:attribute name="href"><xsl:value-of select="link"/></xsl:attribute>
<xsl:attribute name="title"><xsl:value-of select="link"/></xsl:attribute>
<xsl:attribute name="style">color:#FF0000</xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
<xsl:value-of select="concat(title, ' - 続きを読む...')"/>
</xsl:element>
<xsl:choose>
<xsl:when test="pubDate">
<xsl:value-of
select="concat(
' - ',
substring(
substring-before(jf:creationDate, ':'),
1,
string-length(substring-before(jf:creationDate, ':'))-3
)
)"
/>
</xsl:when>
<xsl:when test="dc:date">
<xsl:value-of select="concat(' - ', substring-before(dc:date, 'T'))"/>
</xsl:when>
<xsl:otherwise>
<!-- for OTN rss -->
<xsl:if test="jf:creationDate">
<xsl:value-of
select="concat(
' - ',
substring(
substring-before(jf:creationDate, ':'),
1,
string-length(substring-before(jf:creationDate, ':'))-3
)
)"
/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>

<xsl:template match="//rss/channel/title">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="//rss/channel/link">
<xsl:value-of select="."/>
</xsl:template>

</xsl:stylesheet>

長くなるので、PL/SQLのソースは次回。




このサンプルは OTN-Jの CodeTipsでも公開しています。

(1/3) http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2135
(2/3) http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2137
(3/3) http://otn.oracle.co.jp/otn_pl/otn_tool/code_detail?n_code_id=2138

| | | コメント (0) | トラックバック (0)

2007年3月30日 (金)

Mac De PL/SQL RSS Reader #31 (XSLT編 #3)

前回前々回と、いきなりデバッグの後半部分とApex3.0 での動作確認を載せていたので、今回は、少しだけ解説を。

XSLTスタイルシートは、Oracleのディレクトリオブジェクトから読み込んだと書いたが、まずはその点から解説しておこう。
ディレクトリオブジェクトを作成し、Apex3.0でアプリケーションの格納に利用する「作業領域」に割り当てられたスキーマへディレクトリオブジェクトの読み込み権限を付与しておく。

SYS> create directory plsql_de_xslt as '/Volumes/Discus/4oracle/oracle/plsql_de_xslt/';

ディレクトリが作成されました。

SYS> grant read on directory plsql_de_xslt to aquarium;

権限付与が成功しました。

尚、「作業領域」に割り当てられているスキーマの確認方法は、
Application Express管理サービスにログイン->「作業領域の管理」->「作業領域とスキーマの割当の管理」

31 32
33 34
上記のスクリーンショットを見れば一目瞭然だが、作業領域:aquariumにaquariumスキーマが割り当てられていることが確認できる。


ディレクトリオブジェクトの準備が済んだら、事前に作成しておいたXSLTスタイルシート(文字エンコーディングは、UTF-8とした)をディレクトリオブジェクトに対応付けされたディレクトリへ配置する。(以下は配置後の状態)

discus:˜/plsql_de_xslt oracle$ pwd
/Volumes/Discus/4oracle/oracle/plsql_de_xslt
discus:˜/plsql_de_xslt oracle$ ls
RssToXhtml.xsl
discus:˜/plsql_de_xslt oracle$

PL/SQLでは、DBMS_XSLPROCESSOR.READ2CLOB()ファンクションを使って読み込むことができる。


例えば、
●ディレクトリオブジェクト名:"PLSQL_DE_XSLT"
●XSLTスタイルシート名:"RssToXhtml.xsl"
●文字エンコーディング: "UTF-8"

である場合は以下のように指定する。
注意する点は、キャラクタセットIDだろう。NLS_CHARSET_ID関数を利用しキャラクタセット名からキャラクタセットIDに変換する必要がある。また、指定するキャラクタセット名は、Oracle Database グローバリゼーション・サポート・ガイドに記載されている名称を指定する。 この例では、UTF8

==> 私もハマりました。つい、"UTF-8"って指定してしまって。。。(^^;;;; しばらく悩みました。。はい。

  tempClob :=  DBMS_XSLPROCESSOR.READ2CLOB(
'PLSQL_DE_XSLT',
'RssToXhtml.xsl',
NLS_CHARSET_ID('UTF8')
);

また、以前にも書いたが、 DBMS_XMLPARSER.PARSER(url)を使ってRSS FEEDをパースできればいいのだが、今のところ、マルチバイトを利用している場合には文字化けの影響があるので、面倒だがUTL_HTTPパッケージを利用し自前でCLOBに取込んでからパースするようにしている。 

| | | コメント (0) | トラックバック (0)

2007年3月29日 (木)

Mac De PL/SQL RSS Reader #30 (XSLT編 #2)

さて、SQL Developerでの確認も終わり、さっそく、Apex3.0で試してみた。

いつものように、「Hide and Show Region」を選択し、RSS 0.91/0.92/1.0/2.0 ATOM 0.3/1.0で配信されているFEEDを読み込むよう設定した。

Ap1_1 Ap2 


アプリケーションを実行した結果は以下。思い通りに変換されている。(XSLTスタイルシートを利用しているので、見栄えを変更する場合はXSLTスタイルシートを修正するだけ。。)

Ap3 Ap4

まあ、なんとか、以前のものとおなじような見栄えにはなりますね。(XSLT2.0ならもっといろいろとできるでしょうけども)
以下マニュアル引用


PL/SQL XSLT Processor for XMLType: Features

PL/SQL XSLT Processor for XMLType (DBMS_XSLPROCESSOR) is the Oracle XML DB implementation of the XSL processor. This follows the W3C XSLT final recommendation (REC-xslt-19991116).

W3C XSL Transformations(XSLT) バージョン 1.0の和訳はこちら。
http://www.infoteria.com/jp/contents/xml-data/REC-xslt-19991116-jpn.htm

次回へつづく。




植木等さんの名作といえば、無責任シリーズかもしれません。(今見ても、笑えますもん)
ただ、私などはリアルタイムで無責任シリーズを見た世代ではないので、「オヨビでない奴!」というドラマの印象が強烈かもしれない。
所ジョージと高橋良明が植木等にからみ、植木等の3乗くらいの面白さのあったドラマに仕上がっていたと思います。
ところで、所ジョージに植木等のまねをやらせたら似てるよ、きっと。。。

植木等さんのご冥福を心よりお祈りいたします。

| | | コメント (0) | トラックバック (0)

Mac De PL/SQL RSS Reader #29 (XSLT編#1)

ノドが痛いと思っていたら、熱。インフルエンザではないが、風邪には違いない。(油断禁物ですね。。)1日休んだら、回復しましたけど、本調子に戻っていないので今日はおとなしくして明日は完全復活する予定。(週末は花見もしないとならんし。。な)


さて、PL/SQLでRSS Readerを作ってきたが、XSLTスタイルシートを利用した場合はどうなるか?
ということで、以前のプロシージャをベースに、XSLT版に書き換えてみることにする。

利環境は前回からの続きなので変更はないのだが、念のために、データベース、Web Toolkit、そして、Apexのバージョンを載せておく。

SYS> select version from v$instance;

VERSION
-----------------
10.1.0.3.0


SYS> select owa_util.get_version from dual;

GET_VERSION
--------------------------------------------------------
10.1.2.0.6

SYS> select flows_030000.wwv_flows_release from dual;

WWV_FLOWS_RELEASE
--------------------------------------------------------
3.0.0.00.20

SYS>

尚、開発には、 Oracle SQL Developer 1.1.2.25 Build Main 25.79 を使用した。

SQL Developerでは、OWAの出力もできるようなので試したかったということもあったのだが。。。

試してみるとOWAが書き出す量が多いとエラーになるようだ、具体的にどの程度ということまでは確認していないのだが。。HTP.PRNで4000バイト以上は書き出している。
但し、エラー時にもHTP.PRINTでエラー内容を出力しているのだが、こちらは、画面のスナップショットからも判るように問題なく表示されている
(前述の問題は、dbms_outputパッケージで代用できるので、特に困るということもないので深入りしないでスルー。(^^;;;)


X1 X2

前回までとは異なり、PL/SQL内部でDOMをトラバースしたりすることはせず、XSLTスタイルシートを利用してRSS/RDF/ATOMをHTMLに変換する。
変換後のHTMLには、FEEDのタイトル及びリンク、各記事の要約とリンク、それに記事の作成日があれば表示することにした。
尚、記事の作成日については、該当要素値から日付(RFC3339形式)部分を表示することにした。(RFC882形式の場合は、曜日も含める)
また、XSLスタイルシートの読み込み方法は幾つかあるが、今回はディレクトリオブジェクト以下にあるスタイルシートを読み込むことにした。。

XSLTスタイルシートで、xsl:choose要素や、xsl:if要素。xsl:variable要素なんて多用してしまうと、もの凄く読みにくいJSPのようになってしまいそうな気がする。。。
ところで、しっかりと内容を確認していないのだが、XML Master professional Database を取るべきか、Applicationを取るべきか悩みなすね。。


DBMS_OUTPU.PUT_LINEを利用してデバック変換後のHTMLを表示しているところ。

X3 X4
X5 X6


適当に作ったXSLTスタイルシートもなんとか思い通りのもにに仕上がったので、Apex3.0でお試し。。

次回へつづく。

| | | コメント (0) | トラックバック (0)

2007年3月23日 (金)

Mac De Apex3.0 #5

さて、以前にも少々触れたが、Apexのupgradeの失敗、又は、upgrade後に以前のリリースに戻す方法について、備忘録程度に記述しておく。

手順については、マニュアルにも記載されているが、imageファイルについては、移行前に別名で保存して必要があるのでお忘れなく。
http://download-west.oracle.com/docs/cd/B32472_01/doc/install.300/b32468/trouble.htm#BABDJIFE



HTMLDB1.5からはじまり、HTMLDB2.0、APEX2.2そして、APEX3.0へと移行したのだが、前リリースのimagesフォルダや、marvel.conf(利用しているHTTP Serverによっては、dads.confを使用している場合あり)は、それぞれ別名で退避、また移行後、旧リリースのスキーマを削除していないことを前提としている。

以下はApex3.0移行後にApex2.2へ戻す例である。尚、apexは停止しておくこと!。


  1. Apex3.0用imagesディレクトリを別名で退避(以下例では、images_30として改名)又は、削除する。

  2. 事前に退避しておいた、Apex2.2用imagesディレクトリ(以下の例では、images_22)をimagesディレクトリとして改名又はコピーする。

  3. Apex3.0用に書き換えたmarvel.confを別名で退避(以下例では、marvel.conf.30と改名)又は、削除する。

  4. 事前に退避しておいた、Apex2.2用marvel.confファイル(以下例では、marvel.conf.22)をmarvel.confに改名又はコピーする。

  5. SQL*Plusを起動し、SYSユーザ接続後、スキーマを FLOWS_020200(Apex2.2用スキーマ)に変更する。

  6. flows_020200.wwv_flow_upgrade.switch_schemas()プロシージャにて、APEXのスキーマをFLOWS_030000(Apex3.0用スキーマ)からFLOWS_020200(Apex2.2スキーマ)に切り替える。

  7. flows_030000スキーマを削除する。


以上のような手順でApex3.0からApex2.2に戻す事ができる。以下はApex3.0からApex2.2に戻した際のログである。
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ ll | grep images
drw-r----- 881 oracle oinstall 29954 Mar 18 17:21 images
drw-r----- 998 oracle oinstall 33932 Mar 18 14:33 images_20
drw-r----- 876 oracle oinstall 29784 Mar 18 13:00 images_22
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ mv images images_30
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ cp -rf images_22 images
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ cd ../apache/modplsql/conf
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/apache/modplsql/conf oracle$ ls marvel*
marvel.conf marvel.conf.20 marvel.conf.22
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/apache/modplsql/conf oracle$ mv marvel.conf marvel.conf.30
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/apache/modplsql/conf oracle$ mv marvel.conf.22 marvel.conf
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ sqlplus /nolog
> conn sys/xxxx@tiger as sysdba
SYS> l
1 select
2 username,
3 account_status
4 from
5 dba_users
6 where
7 username like '%¥_PUBLIC¥_%' escape '¥'
8 or username like 'FLOWS¥_%' escape '¥'
9 order by
10 account_status,
11* username
SYS> /

USERNAME ACCOUNT_STATUS
------------------------------ --------------------------------
FLOWS_020000 LOCKED
FLOWS_020200 LOCKED
FLOWS_030000 LOCKED
FLOWS_FILES LOCKED
APEX_PUBLIC_USER OPEN
HTMLDB_PUBLIC_USER OPEN

6行が選択されました。

SYS>
SYS> alter session set current_schema = FLOWS_020200;

セッションが変更されました。

SYS> exec flows_020200.wwv_flow_upgrade.switch_schemas('FLOWS_030000','FLOWS_020200');

PL/SQLプロシージャが正常に完了しました。

SYS> drop user flows_030000 cascade;

ユーザーが削除されました。

SYS>

これでApex2.2への戻しは終了である。

注意)HTMLDB_PUBLIC_USERというスキーマは、HTMLDB2.0までの接続スキーマである。HTMLDB2.0以前に戻す可能性がある場合にはこれも残しておく(lockしておいたほうがより安全だが)か、退避しておく必要がある。また、FLOWS_020000というスキーマはHTMLDB2.0のスキーマである。

| | | コメント (0) | トラックバック (0)

2007年3月22日 (木)

Mac De APEX3.0 #4

さて、HTMLDB2.0からAPEX3.0への移行は簡単だったが、HTMLDB2.0や、APEX2.2でいろいろな問題のあったSVGチャートはどうなったのか?
SVGViewerの開発元であるアドビ社は、SVGViewerの開発を中止したようなので、APEXのチャートのサポートはどうなるのか少々気になっていたのだが、先日APEX3.0がリリースされ、SVGではなく、Flashもサポートされていた。お〜〜〜っ。これで、日本語も表示できるか??? 

まずは、はやり、AppleのCMからどうぞ。

Apple_pie_chart



ということで、マニュアルに示されている手順で、SVGチャート扱うアプリケーションをFLASHチャートを扱えるように移行する。実はこの移行手順も簡単なのだ。クリック、クリック、クリック。とクリックするだけで移行作業を行える。
(尚、MacOSXは、最新パッチを適用した 10.4.9 である。)

最後に、前回、日本語が化けるので凡例部分をローマ字にしていたバリバリ鎌倉円グラフを。。。おおお〜〜表示されました〜〜。

Fin

移行手順は、SVGチャートを配置しているページの該当リージョン開き、右側の枠に表示される、"SVGチャートのFLASHチャートへの移行"というリンクをクリックだけ!
その模様はQuickTImeムービーでどうぞ。


(再生にはQuickTimeが必要です。)


svg_2_flash

| | | コメント (0) | トラックバック (0)

2007年3月21日 (水)

Mac De APEX3.0 #3

● APEX3.0 日本語リソースのインストール

apex_3.0.zipを解凍して作成されるディレクトリのbuilder/jaに移動し、load_ja.sqlスクリプトをsysユーザで実行する。
NLS_LANGは、AL32UTF8へ一時的に変更し、Terminalの文字エンコーディングもUTF-8変更しておく!

Apex30_terminal_utf8

G5Server:/Volumes/DiscusWork/temp/apex/builder/ja oracle$ ls -l load_ja.sql
-r--r--r-- 1 oracle oinstall 2319 Feb 28 04:35 unload_ja.sql
G5Server:/Volumes/DiscusWork/temp/apex/builder/ja oracle$
G5Server:/Volumes/DiscusWork/temp/apex/builder/ja oracle$ echo $NLS_LANG
Japanese_Japan.JA16SJISTILDE
G5Server:/Volumes/DiscusWork/temp/apex/builder/ja oracle$ export NLS_LANG=japanese_japan.AL32UTF8
G5Server:/Volumes/DiscusWork/temp/apex/builder/ja oracle$ sqlplus /nolog

SQL*Plus: Release 10.1.0.3.0 - Production on 月 3月 17 23:17:47 2007

Copyright (c) 1982, 2004, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS> alter session set current_schema = FLOWS_030000;

セッションが変更されました。

SYS> @load_ja
. ____ ____ ____ ____
. / \ | \ /\ / | /
.| || / / \ | | |
.| ||--- ---- | | |--
.| || \ / \ | | |
. \____/ | \/ \ \____ |____ \____
.
. Application Express Hosted Development Service Installation.
.........................................................

PL/SQLプロシージャが正常に完了しました。

Install Application Express applications

PL/SQLプロシージャが正常に完了しました。

APPLICATION 4420 - APEX - System Messages

・・・・中略・・・・

APPLICATION 4009 - APEX - Application Builder
Set Credentials...
Check Compatibility...
API Last Extended:20070108
Your Current Version:20070108
This import is compatible with version: 20070108
COMPATIBLE (You should be able to run this import without issues.)
Set Application ID...
...authorization schemes
...navigation bar entries
...application processes
...application items
...application level computations
...Application Tabs
...Application Parent Tabs
...Shared Lists of values
...Application Trees
...page groups
...PAGE .4009: ページ0(ゼロ)
...PAGE 1.4009: アプリケーション・ビルダー
...PAGE 2.4009: サポートするオブジェクト
...PAGE 3.4009: ブレッドクラム・エントリ
...PAGE 4.4009: マスター表の定義
...PAGE 5.4009: ディテール表を定義
...PAGE 6.4009: アイテム・ラベルのグリッド編集

・・・・中略・・・・

...PAGE 1.4709: 作業領域のリクエスト - 最初のページ
...PAGE 3.4709: 作業領域のリクエスト - 確認
...PAGE 10.4709: 作業領域のリクエスト - 管理者
...PAGE 20.4709: 作業領域のリクエスト - 作業領域
...PAGE 25.4709: 作業領域のリクエスト - スキーマの指定
...PAGE 26.4709: 作業領域のリクエスト - スキーマに関する警告
...PAGE 27.4709: 作業領域のリクエスト - プロジェクトの説明
...PAGE 30.4709: 作業領域のリクエスト - 確認
...PAGE 40.4709: 作業領域のリクエスト - 新規または既存のスキーマ

・・・・中略・・・・

...authentication schemes
......scheme 108165525079033088.4709
...done
Adjust instance settings

PL/SQLプロシージャが正常に完了しました。

SYS> exit
Oracle Database 10g Enterprise Edition Release 10.1.0.3.0 - Production
With the Partitioning, OLAP and Data Mining optionsとの接続が切断されました。
G5Server:/Volumes/DiscusWork/temp/apex/builder/ja oracle$

● 確認

APEX3.0への移行は特に問題もなく終了した。作業が簡単なのは、本当にうれしいねぇ。 (^^;;;。
(9iASや、10g Application Serverのupgradeも、もっと簡単になれば、もっとうれしいと思うのだが。。) 


Start_apex30 Apex30_test

今日はここまで。
次回は、チャートの表示がSVGViewerからFlashに変更されたので、以前SVGチャートを試したdemoアプリケーションをFlashに移行し、「パイチャート」を表示してみることにする。

| | | コメント (0) | トラックバック (0)

2007年3月20日 (火)

Mac De Apex3.0 #2

HTMLDB2.0からAPEX3.0への移行作業。その2。

事前準備は整ったので、HTMLDB2.0からAPEX3.0へupgradeする。
尚、新規インストールなのか移行なのかを意識することはない楽で簡単ですね。
apex_3.0.zipを解凍した際に作成されるapexディレクトリ直下にあるapexins.sqlを実行するだけでよいので、非常に簡単。とは言っても心配ならはバックアップを取得しておいたほうがよいだろう。


尚、APEXのインストールでは、リリース毎に新たなスキーマが作成され、旧スキーマが残されている。
APEX3.0に移行したとしても、容易に以前のリリースに戻せるようになっている。 したがって、移行後すぐに旧スキーマを削除せず、しばらくの間は移行前のスキーマを残しておくという方法も取れる。
HTMLDB2.0からAPEX3.0の例でいえば、 FLOWS_020000が、HTMLDB2.0のスキーマで、新たにFLOWS_030000というスキーマが作成される。FLOWS_020000と関連するイメージファイル、marvel.conf(dads.confの場合もあり)を別途退避しておけば、その必要があればHTMLDB2.0環境へ戻すこともできる。

前置きが長くなったが作業再開。

尚、apexins.sqlでは、英語版がインストールされるので注意が必要、日本語リソースを別途インストールしないと、APEXのメニューなどは英語表記のままだ(online helpには日本語ヘルプは用意されていないようだ。)

ただし、日本語リソースをインストールしなくても、日本語を扱うアプリケーションは作成できる(ようだ)。
簡単に確認しただけなので、全く問題とは言い切れないが・・・・。


SYS> @apexins apex_master SYSAUX SYSAUX TEMP /i/
. ____ ____ ____ ____
. / ¥ | ¥ /¥ / | /
.| || / / ¥ | | |
.| ||--- ---- | | |--
.| || ¥ / ¥ | | |
. ¥____/ | ¥/ ¥ ¥____ |____ ¥____
.
. Application Express Installation.
...................................

F
-
2


FOO_USR
------------------------------
FLOWS_020000


PL/SQLプロシージャが正常に完了しました。


PL/SQLプロシージャが正常に完了しました。


レコードが選択されませんでした。


PL/SQLプロシージャが正常に完了しました。

I. O R A C L E S Y S I N S T A L L P R O C E S S
...create flows user

ユーザーが作成されました。

・・・・中略・・・・

......Message master_detail
......Message master_detail_edit
......Message master_detail_detail
......Message sw_empty_script
......Message sw_greater_than_30k
......Message confirm_create_master_detail
......Message confirm_3_page_master_detail
......Message confirm_2_page_master_detail
......Message confirm_master_table
......Message confirm_master_display_columns
......Message confirm_detail_table
......Message confirm_detail_display_columns
......Message confirm_master_sort
......Message confirm_include_master_report

・・・・中略・・・・

...PAGE 0: Page Zero
...PAGE 1: Application Builder
...PAGE 2: Supporting Objects
...PAGE 3: Breadcrumb Entries
...PAGE 4: Define Master Table
...PAGE 5: Define Detail Table
...PAGE 6: Item Label Grid Edit
...PAGE 7: Delete Multiple Items
...PAGE 8: Referenced Components
...PAGE 9: Shared Components

・・・・中略・・・・

...done load verification images
Verification Images Loadのタイミング。
経過: 00:00:01.13
VII. L O A D E N G L I S H D I C T I O N A R Y

トリガーが変更されました。

...10000 rows
...20000 rows
...30000 rows
...40000 rows
...50000 rows
...60000 rows
...70000 rows
English Dictionaryのタイミング。
経過: 00:04:56.56



Thank you for installing Oracle Application Express.

Oracle Application Express is installed in the FLOWS_030000 schema.

The structure of the link to the Application Express administration services is as follows:
http://host:port/pls/apex/apex_admin

The structure of the link to the Application Express development interface is as follows:
http://host:port/pls/apex

-- Now beginning upgrade. This will take several minutes.-------
-- Ensuring template names are unique -------
-- Migrating metadata to new schema -------
-- Switching builder to new schema -------
-- Migrating SQL Workshop metadata -------
-- Upgrading new schema. -------
-- Copying preferences to new schema. -------
-- Updating user account expiration. -------
Upgrade completed successfully no errors encountered.
-- Upgrade is complete -----------------------------------------
Upgradeのタイミング。
経過: 00:01:15.78
JOB_QUEUE_PROCESSES: 10

Performing Application Express component validation - please wait...

Completing registration process.
Validating installation.
Validate Installationのタイミング。
経過: 00:05:56.91
Complete Installationのタイミング。
経過: 00:31:02.26

● お約束、imagesフォルダの入れ替え。


このディレクトリは、前述のapexins.sqlで指定した /i/ という仮想ディレクトリにマッピングされている。
仮想ディレクトリ /i/ にマップされている実ディレクトリはmarvel.confで確認できる。
尚、利用しているHTTPサーバー毎に構成ファイルが異なるので要注意。詳細は、マニュアルの4.3.1.1 Copying the Images Directory When Upgradingを参照のこと。

G5Server:˜/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ ls -l images*
drw-rw---- 998 oracle oinstall 33932 Mar 17 20:19 images
drw-rw---- 1334 oracle oinstall 45356 Mar 10 22:10 images_1_5
G5Server:˜/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ mv images images_2_0
G5Server:˜/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ cp -rf /Volumes/DiscusWork/temp/apex/images .
G5Server:˜/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ ls -l images*
drw-rw---- 881 oracle oinstall 29954 Mar 17 22:45 images
drw-rw---- 1334 oracle oinstall 45356 Mar 10 22:10 images_1_5
drw-rw---- 998 oracle oinstall 33932 Mar 17 20:19 images_2_0


● HTTPサーバーの構成ファイルの書き換え


利用しているHTTPサーバーに対応した構成ファイルをapex30向けに書き換える。
当環境では、marvel.confである。変更内容は以下の通り。

(余談だが、marvel.confを書き換えずに試したところ、特に問題もなくAPEX3.0が利用できた。URLのパスを変更したくない場合には、他のパラメータを変更するだけでも問題ないかもしれない。詳細未確認。)

G5Server:˜/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ diff marvel.conf.org marvel.conf
2c2
< <Location /pls/htmldb>
---
> <Location /pls/apex>
7c7
< PlsqlDatabaseUsername HTMLDB_PUBLIC_USER
---
> PlsqlDatabaseUsername APEX_PUBLIC_USER
10c10
< PlsqlDefaultPage htmldb
---
> PlsqlDefaultPage apex
13c13
< PlsqlDocumentProcedure wwv_flow_file_manager.process_download
---
> PlsqlDocumentProcedure wwv_flow_file_mgr.process_download
15c15
< PlsqlNLSLanguage JAPANESE_JAPAN.JA16SJISTILDE
---
> PlsqlNLSLanguage JAPANESE_JAPAN.AL32UTF8
G5Server:˜/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$

今日はここまで、日本語リソースのインストールへつづく。



聞いている曲:

Howard Jones - The Peaceful Tour - Things Can Only Get Better Howard Jones - The Peaceful Tour - Things Can Only Get Better

| | | コメント (0) | トラックバック (0)

2007年2月22日 (木)

Mac De PL/SQL RSS Reader #28 - メモ

OTN-JフォーラムのFEEDはUS OTNと同じく、RSS2.0となっているようだ。(下記XMLソース抜粋参照のこと。)


item要素にはネームスペースで拡張された、jf:creationDate要素や、jf:author要素は存在するが、author要素や、pubDate要素が存在しない。

そのため、FirefoxやSarafi、Google RSS readerなどでは、投稿者や日時が表示されない。
US OTNの場合と同じなのだが、投稿日があると便利でしょと思うのは私だけ?

Safariでは投稿日関連する要素が存在しない場合には、FEEDを取込んだ日時を表示してくれるので、多少は助かる。
Safari


Firefox2では、日時表示は全くない(pubDate要素がないので当然だが。。)
Firefox2


Google ReaderでもFirefox2に同じ。
Googlerssreader

リニューアルついでなので、author要素や、pubDate要素くらい追加してもらえるといいのだが・・・OTN-Jさん。。。と思いつつ。。





ということで、Apex2.2に組み込んだPL/SQLのRSS FEED Readerのコードを変更するのもいいかと思い始めた。


例えば、OTNのRSS2.0 FEEDのように、item要素には、pubDate要素が含まれず、名前空間で拡張されたjf:creationDate要素がある場合、
RSS FEED Reader側で、どのネームスペースのどの要素を使うかなんて設定できるようにすれば、便利かな〜〜と思った次第。。
(あと、jf:createDate要素値は、rfc822形式のようだが、曜日が日本語表記なので、英語表記へ変換する必要あり。(現行のコードのままだと例外が飛ぶはずなので、それも含め、コードの見直しを考えてみますか。。。

<rss version="2.0" xmlns:jf="http://www.jivesoftware.com/xmlns/jiveforums/rss">
<channel>
・・・・中略・・・・
<item>
<title>Re: 追記: 名前空間を使用したXqueryの使用方法に関して</title>
<link>http://otn.oracle.co.jp/forum/thread.jspa?messageID=35001142&tstart=0#35001142</link>

<description><![CDATA[名前空間のURLが間違っているのでは? (.xsd が抜けているとか。)]]></description>

<jf:creationDate>金, 09 2 2007 18:55:56 +0900</jf:creationDate>
<jf:modificationDate>金, 09 2 2007 18:55:56 +0900</jf:modificationDate>
<jf:author>xxxxxx</jf:author>
<jf:replyCount>0</jf:replyCount>
</item>
・・・・以下略・・・・

| | | コメント (2) | トラックバック (0)

2007年2月21日 (水)

Mac De PL/SQL RSS Reader番外編-OTN-J Forum RSS FEED配信開始記念?!

2月1日からOTN-JのForumがリニューアルされ、US OTNのForumのように新規投稿がRSS FEEDで配信されるようになった。(やっとと言った感じ。US OTNに遅れること。。。?年という感じなので待ちくたびれていたところだった。。。)

さっそく、Safariで、会議室のFEEDをブックマークしてみるとUS OTNのように新着記事が取込まれている。。
クリックして各記事を眺めてみても問題なさそうだなぁ。
と思い、折角だから、Apex2.2に組み込んだ RSS READERで試しはじめたころから、リニューアルしたフォーラムの問題に気づき始める。。。。

まず、以前にも試していた、PL/SQLでRSS FEED ReaderをApex2.2に組み込み、OTN-JのFEEDを取込むと、、、なんと、パースエラー!
ん? このエラーは、以前にも見たような気がする。。が、ちゃんとUTL_HTTPパッケージを利用し、UTF-8で取込むことで回避したはずだ。。おかしい。。Safariでは文字化けなど発生していない!!

Xml_parse_error_plsql


そこで、MacOSX版 Firefox2でプレビューモードにしてOTN-JのFEEDを取込んでみると。。。
お〜〜〜。見事にXMLパースエラー!!

Xml_parse_error


ところが、再度行うと、今度は、文字化け!
さらに不思議なことに、、、ブックマークしたFEEDのエントリは文字化けしていないではないか??(なんだこりゃ!)

Xml_no_parse_error_but


その後、何度か試していると、上手く取込める場合、XMLパースエラーになる場合、パースエラーにはならないが文字化けする状態がランダムに発生することが分かった。
UTF-8以外の文字コードで送信されてしまう状態がランダムに発生しているようにみえた。(・・・経路が違うのかもしれない。例えばキャッシュを経由した場合とそれ以外のように、。。と勝ってに想像したりしている。)

そして、OTN-Jがリニューアルされてから、9日目のこと、Firefox2上でのFeedの文字化けも収まった?模様?で、文字化けやXMLパースエラーにはお目にかからなくなった。


ということで、再度、Apex2.2に組み込んだPL/SQLで作成したFEED READERで、OTN-JのXMLの部屋のFEEDを取込んでみた。


(再生にはQuickTimeが必要です。)

[an error occurred while processing this directive]apex2.2

なんとか、読めるようになりましたね。。。。。OTN-J様。ご苦労さまです。m(_ _)m。

| | | コメント (0) | トラックバック (0)

2006年11月20日 (月)

Mac De PL/SQL RSS Reader #27 (+)

Mac De PL/SQL RSS Reader #27で公開したコードがOTN-JapanのCocd Tipsで公開されました。

尚、記事が大きく3分割してあります。

Code Tips PL/SQLで RSS Feed Reader (1/3)
Code Tips PL/SQLで RSS Feed Reader (2/3)
Code Tips PL/SQLで RSS Feed Reader (3/3)

尚、OTN-JapanのCode Tipsを見るには、OTN-Japanへのユーザ登録が必要です。

※オラクルライセンスセンターのアフィリエイトも追加しました。



いいねぇ、Saigenji

聞いている曲:Saigenji - SAIGENJI - 孤動 saigenji - SAIGENJI - 孤動

| | | コメント (0) | トラックバック (0)

2006年11月 4日 (土)

Mac De PL/SQL RSS Reader #27

RSS Readerの続き。

週末、美味しいワインのつまみのレシピを多摩美の芸術祭りに模擬店を出しているmiwaさん教えてもらいニッコリ!!

さて、APEX2.2に組み込んだPL/SQLで作成したRSS Readerストアドプロシージャのソースを公開。
ひとまず、これで、RSS Readerネタは終了。
Oracle Portalのデータベースポートレット向けにするとか、XSLT版というのも面白いと思っているが、それらはまた、気が向いたときにでも。。 

以下は、APEXに組み込んだデモをQiuckTime Movieにしたもの。(再生にはQuickTimeが必要です。)


Apex_de_rss_reader_demo4


そして、RSS Readerストアドプロシージャのソースは以下。
ただし、ブラウザに表示するためソースコードの一部の文字を
文字参照に置換してある。( < > " ¥ など) 

オリジナルのスクリプトrss_reader.sqlはこちら。(エンコーディングは、shift_jis。)

(RFC822や、RFC3339のDate / Time関連関数は、ストアドプロシージャの内部関数化してある。これらをパッケージ化するほうが分かりやすくなるかもしれないが、それらの内部関数を単独で利用することもないのでストアドプロシージャの内部関数としてある。)

CREATE OR REPLACE PROCEDURE RSS_READER
(
i_feedXmlUrl IN VARCHAR2
)
AS
--============ TYPEs/VARIABLEs ===============================================
C_RSS CONSTANT VARCHAR2(7) := 'rss';
C_RDF CONSTANT VARCHAR2(7) := 'rdf:RDF';
C_ATOM CONSTANT VARCHAR2(7) := 'feed';
-- C_ATTR_NAME_VERSION CONSTANT VARCHAR2(7) := 'version';

v_feedXmlUrl VARCHAR2(32767);

TYPE FeedEntryType IS RECORD
(
title VARCHAR2(2000),
link VARCHAR2(200),
published TIMESTAMP WITH LOCAL TIME ZONE
);
v_feedEntryList DBMS_XMLDOM.DomNodeList;
v_feedTitle VARCHAR2(2000);
v_feedUrl VARCHAR2(200);

v_myParser DBMS_XMLPARSER.Parser;
v_feedDoc DBMS_XMLDOM.DomDocument;

v_rootElementTagName VARCHAR2(7);
-- v_feedVersion VARCHAR2(40);
v_feedSourceClob CLOB;
v_nsAttrXmlNameSpaces VARCHAR2(32767);

--========== Internal PROCEDUREs/FUNCTIONs ===================================
FUNCTION RFC3339_DateStrToTimeStampLTZ(
i_dateTimeString IN VARCHAR2
)
RETURN TIMESTAMP WITH LOCAL TIME ZONE
AS
C_NLS_DATE_LANGUAGE CONSTANT VARCHAR2(40) := 'NLS_DATE_LANGUAGE=AMERICAN';
v_tempTimeStampTz TIMESTAMP WITH LOCAL TIME ZONE := NULL;
v_dateTimeString VARCHAR2(50) := NULL;
v_timeStampTzFormat VARCHAR2(40) := NULL;
BEGIN
v_dateTimeString := REGEXP_REPLACE(
REGEXP_REPLACE(
UPPER(i_dateTimeString),
'T', NULL, 1, 1
),
'Z$', 'UTC'
);

IF v_dateTimeString IS NULL THEN
RETURN NULL;
END IF;


v_timeStampTzFormat := 'RRRR-MM-DD';

IF LENGTH(v_dateTimeString) >= 10 THEN

IF REGEXP_INSTR(v_dateTimeString , '([0-9]{2}:){2}[0-9]{2}¥.?') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI:SSXFF';
END IF;

IF REGEXP_INSTR(v_dateTimeString, '(¥+|¥-)[0-9]{2}:[0-9]{2}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZH:TZM';
ELSIF REGEXP_INSTR(v_dateTimeString, 'UTC$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZR';
END IF;
END IF;

v_tempTimeStampTz := TO_TIMESTAMP_TZ(
v_dateTimeString,
v_timeStampTzFormat,
c_nls_date_language
);

RETURN v_tempTimeStampTz;

EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(
-20002,
'RFC3339_DateStrToTimeStampLTZ():' || SQLERRM()
);
RETURN NULL;
END RFC3339_DateStrToTimeStampLTZ;


FUNCTION RFC822_DateStrToTimeStampLTZ
(
i_dateTimeString IN VARCHAR2
)
RETURN TIMESTAMP WITH LOCAL TIME ZONE
AS
C_NLS_DATE_LANGUAGE CONSTANT VARCHAR2(40) := 'NLS_DATE_LANGUAGE=AMERICAN';
v_tempTimeStampTz TIMESTAMP WITH LOCAL TIME ZONE := NULL;
v_dateTimeString VARCHAR2(50) := NULL;
v_timeStampTzFormat VARCHAR2(40) := NULL;
BEGIN
v_dateTimeString := UPPER(i_dateTimeString);
IF v_dateTimeString IS NULL THEN
RETURN NULL;
END IF;

IF REGEXP_INSTR(v_dateTimeString, '^[A-Z]{3}¥, ') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'DY, ';
END IF;

v_timeStampTzFormat := v_timeStampTzFormat || 'DD MON RRRR';
IF REGEXP_INSTR(v_dateTimeString, '[0-9]{2}:[0-9]{2}:[0-9]{2}') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI:SS ';
ELSIF REGEXP_INSTR(v_dateTimeString, '[0-9]{2}:[0-9]{2}') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI ';
END IF;

IF REGEXP_INSTR(v_dateTimeString, ' (¥+|¥-)[0-9]{4}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZHTZM';
ELSIF REGEXP_INSTR(v_dateTimeString, ':[0-9]{2}$') = 0
AND REGEXP_INSTR(v_dateTImeString, '[0-9]{2} [A-Z]{3} [0-9]{2,4}$') = 0
AND REGEXP_INSTR(v_dateTImeString, ' [A-Z]{1,3}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZR';
END IF;

v_tempTimeStampTz := TO_TIMESTAMP_TZ(
v_dateTimeString,
v_timeStampTzFormat,
c_nls_date_language
);

RETURN v_tempTimeStampTz;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(
-20001,
'RFC822_DateStrToTimeStampLTZ():' || SQLERRM()
);
RETURN NULL;
END RFC822_DateStrToTimeStampLTZ;


PROCEDURE getFeedSource(
i_feedXmlUrl IN VARCHAR2,
i_feedXmlCharset IN VARCHAR2 DEFAULT 'UTF-8',
io_feedSourceClob IN OUT NOCOPY CLOB
)
IS
v_httpReq UTL_HTTP.REQ;
v_httpResp UTL_HTTP.RESP;
v_feedSource VARCHAR2(1024);
BEGIN
v_httpReq := UTL_HTTP.BEGIN_REQUEST(i_feedXmlUrl);
UTL_HTTP.SET_HEADER(
v_httpReq,
'User-Agent',
'Oracle UTL_HTTP/Oracle10g R1;'
);

UTL_HTTP.SET_HEADER(
v_httpReq,
'Content-Type',
'text/xml;charset='||i_feedXmlCharset
);

v_httpResp := UTL_HTTP.GET_RESPONSE(v_httpReq);
DBMS_LOB.CREATETEMPORARY(io_feedSourceClob, FALSE);
BEGIN
LOOP
UTL_HTTP.READ_LINE(v_httpResp, v_feedSource, true);
v_feedSource := v_feedSource || UTL_TCP.CRLF;
DBMS_LOB.WRITEAPPEND(
io_feedSourceClob,
LENGTH(v_feedSource),
v_feedSource
);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(v_httpResp);
END;
END getFeedSource;


PROCEDURE getNameSpaces(
i_feedType IN VARCHAR2,
i_feedDoc IN DBMS_XMLDOM.DomDocument,
io_feedXmlNameSpaces IN OUT NOCOPY VARCHAR2
)
IS
v_attrTagName VARCHAR2(32767);
v_feedNodelist DBMS_XMLDOM.DomNodeList;
v_nsAttrNamedNodeMap DBMS_XMLDOM.DomNamedNodeMap;
BEGIN

CASE i_feedType
WHEN C_RSS THEN
v_feedNodeList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, C_RSS);

WHEN C_RDF THEN
-- name spaceが影響しているのだと思われるが上手い解決方法が
-- 見つからなかったので、全要素を取得している。
v_feedNodeList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, '*');

WHEN C_ATOM THEN
v_feedNodeList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, C_ATOM);

END CASE;

v_nsAttrNamedNodeMap
:= DBMS_XMLDOM.GETATTRIBUTES(DBMS_XMLDOM.ITEM(v_feedNodeList, 0));

FOR i IN 0..DBMS_XMLDOM.GETLENGTH(v_nsAttrNamedNodeMap)-1 LOOP
v_attrTagName
:= DBMS_XMLDOM.GETNODENAME(DBMS_XMLDOM.ITEM(v_nsAttrNamedNodeMap, i));

IF REGEXP_INSTR(v_attrTagName, '^xmlns') = 1 THEN
io_feedXmlNameSpaces
:= io_feedXmlNameSpaces
|| v_attrTagName || '="'
|| DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.ITEM(v_nsAttrNamedNodeMap, i))
|| '" ';
END IF;
END LOOP;
END getNameSpaces;


PROCEDURE getFeedEntries(
i_feedType IN VARCHAR2,
i_feedDoc IN DBMS_XMLDOM.DomDocument,
i_feedXmlNameSpaces IN VARCHAR2,
o_feedTitle OUT NOCOPY VARCHAR2,
o_feedUrl OUT NOCOPY VARCHAR2,
o_feedEntryList OUT NOCOPY DBMS_XMLDOM.DomNodeList
)
IS
v_feedSourceXml XMLTYPE;
BEGIN
v_feedSourceXml := DBMS_XMLDOM.GETXMLTYPE(i_feedDoc);
CASE i_feedType
WHEN C_RSS THEN
o_feedEntryList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, 'item');
o_feedTitle := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/rss/channel/title/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();
o_feedUrl := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/rss/channel/link/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();

WHEN C_RDF THEN
o_feedEntryList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, 'item');
o_feedTitle := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/rdf:RDF/channel/title/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();
o_feedUrl := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/rdf:RDF/channel/link/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();

WHEN C_ATOM THEN
o_feedEntryList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(i_feedDoc, 'entry');
o_feedTitle := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/feed/title/text()',
i_feedXmlNameSpaces
).GETSTRINGVAL();
o_feedUrl := XMLTYPE.EXTRACT(
v_feedSourceXml,
'/feed/link[@type="text/html"]/@href',
i_feedXmlNameSpaces
).GETSTRINGVAL();

END CASE;
END getFeedEntries;


PROCEDURE drawFeedEntry(
i_feedEntry IN FeedEntryType
)
IS
BEGIN
HTP.TABLEROWOPEN();
HTP.TABLEDATA(
'<a href="'
|| i_feedEntry.link
|| '" style="color:#FF0000" target="_blank">'
|| i_feedEntry.title || ' -- 続きを読む... '
|| '</a>' || TO_CHAR(i_feedEntry.published,'yyyy-mm-dd hh24:mi')

);
HTP.TABLEROWCLOSE();
END drawFeedEntry;


PROCEDURE drawFeedEntries(
i_feedType IN VARCHAR2,
i_feedTitle IN VARCHAR2,
i_feedUrl IN VARCHAR2,
i_feedEntryList IN DBMS_XMLDOM.DomNodeList
)
IS
v_feedEntry FeedEntryType;
v_emptyFeedEntry FeedEntryType;
v_itemNode DBMS_XMLDOM.DomNode;
v_childnode DBMS_XMLDOM.DomNode;
v_feedItemFields DBMS_XMLDOM.DomNodeList;
v_attrNamedNodeMap DBMS_XMLDOM.DomNamedNodeMap;
v_childNodeText VARCHAR2(32767);
BEGIN
HTP.TABLEOPEN();
HTP.TABLEROWOPEN();
HTP.TABLEHEADER(
'<a href="'
|| i_feedUrl
|| '" style="color:#000000" target="_blank">'
|| i_feedTitle
|| '</a>'
|| ' - ' || dbms_xmldom.getlength(i_feedEntryList)|| ' articles.'
);
HTP.TABLEROWCLOSE();

v_feedEntry := v_emptyFeedEntry;
FOR i IN 1..DBMS_XMLDOM.GETLENGTH(i_feedEntryList) LOOP
v_itemNode := DBMS_XMLDOM.ITEM(i_feedEntryList, i-1);
v_feedItemFields := DBMS_XMLDOM.GETCHILDNODES(v_itemNode);

FOR j IN 1..DBMS_XMLDOM.GETLENGTH(v_feedItemFields) LOOP
v_childnode := DBMS_XMLDOM.ITEM(v_feedItemFields, j-1);
v_childNodeText := DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.GETFIRSTCHILD(v_childnode)
);

CASE DBMS_XMLDOM.GETNODENAME(v_childnode)

WHEN 'title' THEN
v_feedEntry.title := v_childNodeText;


WHEN 'link' THEN
IF i_feedType = C_ATOM THEN
-- ATOM
v_attrNamedNodeMap := DBMS_XMLDOM.GETATTRIBUTES(v_childnode);
IF DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.GETNAMEDITEM(v_attrNamedNodeMap, 'type')
) = 'text/html'
THEN
v_feedEntry.link :=
DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.GETNAMEDITEM(v_attrNamedNodeMap, 'href')
);
END IF;

ELSE
-- RSS/RDF
v_feedEntry.link := v_childNodeText;
END IF;


-- RSS 0.9x and RSS 2.0
WHEN 'pubDate' THEN
v_feedEntry.published
:= RFC822_DateStrToTimestampLTZ(v_childNodeText);

-- ATOM 1.0
WHEN 'published' THEN
v_feedEntry.published
:= RFC3339_DateStrToTimestampLTZ(v_childNodeText);

-- RSS/RDF 1.0 , RSS 2.0
WHEN 'dc:date' THEN
v_feedEntry.published
:= RFC3339_DateStrToTimestampLTZ(v_childNodeText);

-- ATOM 0.3
WHEN 'issued' THEN
v_feedEntry.published
:= RFC3339_DateStrToTimestampLTZ(v_childNodeText);

ELSE
NULL; -- nop
END CASE;
END LOOP;

drawFeedEntry(v_feedEntry);
END LOOP;
HTP.TABLECLOSE();
END drawFeedEntries;
--
--
--****************************************************************************
-- Main
--****************************************************************************
BEGIN

v_feedXmlUrl := i_feedXmlUrl;
IF v_feedXmlUrl IS NULL THEN
RAISE_APPLICATION_ERROR(-20003, 'FEEDのURLを指定してください。');
END IF;


getFeedSource(
i_feedXmlUrl => v_feedXmlUrl,
io_feedSourceClob => v_feedSourceClob
);

v_myParser := DBMS_XMLPARSER.NEWPARSER();
DBMS_XMLPARSER.PARSECLOB(v_myParser, v_feedSourceClob);
v_feedDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);

v_rootElementTagName
:= DBMS_XMLDOM.GETTAGNAME(DBMS_XMLDOM.GETDOCUMENTELEMENT(v_feedDoc));

-- v_feedVersion
-- := DBMS_XMLDOM.GETVALUE(
-- DBMS_XMLDOM.GETATTRIBUTENODE(
-- DBMS_XMLDOM.GETDOCUMENTELEMENT(v_feedDoc),
-- C_ATTR_NAME_VERSION
-- )
-- );

getNameSpaces(
i_feedType => v_rootElementTagname,
i_feedDoc => v_feedDoc,
io_feedXmlNameSpaces => v_nsAttrXmlNameSpaces
);

getFeedEntries(
i_feedType => v_rootElementTagname,
i_feedDoc => v_feedDoc,
i_feedXmlNameSpaces => v_nsAttrXmlNameSpaces,
o_feedTitle => v_feedTitle,
o_feedUrl => v_feedUrl,
o_feedEntryList => v_feedEntryList
);

drawFeedEntries(
i_feedType => v_rootElementTagName,
i_feedTitle => v_feedTitle,
i_feedUrl => v_feedUrl,
i_feedEntryList => v_feedEntryList
);

DBMS_LOB.FREETEMPORARY(v_feedSourceClob);
DBMS_XMLDOM.FREEDOCUMENT(v_feedDoc);
DBMS_XMLPARSER.FREEPARSER(v_myParser);

EXCEPTION
WHEN OTHERS THEN
IF v_feedSourceClob IS NOT NULL THEN
DBMS_LOB.FREETEMPORARY(v_feedSourceClob);
END IF;
DBMS_XMLDOM.FREEDOCUMENT(v_feedDoc);
DBMS_XMLPARSER.FREEPARSER(v_myParser);
HTP.TABLEOPEN();
HTP.TABLEROWOPEN();
HTP.TABLEHEADER('RSS Reader error:'||SQLERRM());
HTP.TABLEROWCLOSE();
HTP.TABLECLOSE();
END RSS_READER;
/
show errors
/




まだまだ、早いか?! などと思っていると、あっという間に、クリスマスモードに突入しちゃうんですよね!。
聞いている曲:Take 6 - We wish you a merry christmas

| | | コメント (0) | トラックバック (0)

2006年10月29日 (日)

Mac De PL/SQL RSS Reader #26

RSS Readerの続き。APEX 2.0に組み込んだRss Readerのレイアウトを変えてみた 

Hide and Show リージョンを3行2列にレイアウトしてみた。設定はこんな感じ。
Img7 Img6 

リージョンに埋め込んだコードはこんな感じ。PL/SQLブロックしか実行できないので、beginブロック内でrss_readerプロシージャを呼び出している。
Img8


実行するとこうなる。Hide and Showリージョンなのでリージョンを折り畳む事ができる。
Img2_1 Img3_1 Img4 Img5

ひとまず、こんなところで。。

| | | コメント (0) | トラックバック (0)

2006年10月23日 (月)

Mac De PL/SQL RSS Reader #25

RSS Readerの続き。適当にAPEX 2.0に組み込み、一覧にしてみた。

以下の通り、大雑把に作ってみたが、それなりね。APEX (HTML DB)に慣れる必要あり。。。。
R1 R2jpg R3

| | | コメント (0) | トラックバック (0)

2006年10月21日 (土)

Mac De PL/SQL RSS Reader #24

RSS Readerの続き。ドラフト版でのテストログの続き。(その6/6)

ATOM 1.0のFEEDをテストした結果。

---------------------- PROMPT ATOM 1.0
begin...
====================
Radical Imagination -from 鎌倉 with Love - 15 articles:
http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/
====================
TITLE: 由比ヶ浜秋景-3
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/3.html
Published on: 2006-10-11 19:48
—————————————-
TITLE: 老母のテラコッタ人形新作
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/post_58.html
Published on: 2006-10-10 00:23
—————————————-
TITLE: 地方のロードサイド風景
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/post_57.html
Published on: 2006-10-09 00:55
—————————————-
TITLE: 由比ヶ浜3頭会談
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/post_56.html
Published on: 2006-10-08 18:30
—————————————-
TITLE: 由比ヶ浜秋景-2
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/2_9.html
Published on: 2006-10-07 19:36
—————————————-
TITLE: 由比ヶ浜秋景-1
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/post_55.html
Published on: 2006-10-07 15:10
—————————————-
TITLE: 『ドリームボックス-殺されていくペットたち』
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/post_54.html
Published on: 2006-10-06 13:35
—————————————-
TITLE: 『Maria Full of Grace』(『そして、ひと粒のひかり』)
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/maria_full_of_grace.html
Published on: 2006-10-05 03:06
—————————————-
TITLE: ラ・ジュルネ(鎌倉由比ガ浜)-20 いぶりがっこ
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/20_1.html
Published on: 2006-10-04 05:03
—————————————-
TITLE: 恩師・尾崎康先生
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/post_53.html
Published on: 2006-10-03 13:52
—————————————-
TITLE: 朝の光
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/10/post_52.html
Published on: 2006-10-01 22:34
—————————————-
TITLE: 長谷秋色-2 曼珠沙華
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/09/2.html
Published on: 2006-09-30 00:19
—————————————-
TITLE: 今朝のおはよう-40 由比ガ浜文様
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/09/post_51.html
Published on: 2006-09-29 22:53
—————————————-
TITLE: なかむら庵(鎌倉小町)
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/09/post_50.html
Published on: 2006-09-29 21:10
—————————————-
TITLE: まっさらMac
URL : http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/2006/09/mac.html
Published on: 2006-09-29 19:46
—————————————-
...End

PL/SQLプロシージャが正常に完了しました。

GLASSCATFISH> spo off


特に問題もないので、OracleのAPEXに組み込みテストしてみる予定である。
そういえば、最近、米salesforce が、新プログラミング言語 「Apex」を発表というニュースもあり、APEX だけで検索してしまうと、いろいろとヒットしてしまう・・・・・。(^^;;

尚、コードを少々変更すれば、Oracle Portalの DBポートレット化することも難しくないと思うのだが、その環境がいまは無い。。。



クラシックも聞くんです(ただし、ピアノだけ、しかも、大映テレビのドラマで使われた曲だけ)

赤い激流
少女に何が起こったか


フジ子・へミング - 練習曲 第12番 ハ短調 作品10の12《革命》 フジ子・へミング - ラ・カンパネラ 1973 - 練習曲 第12番 ハ短調 作品10の12《革命》


フジ子・ヘミング - ラ・カンパネラ フジ子・ヘミング - 奇蹟のカンパネラ - ラ・カンパネラ (パガニーニによる大練習曲 S.141-3) [リスト]

フジ子・ヘミング - ポロネーズ第6番《英雄》 イングリット・フジコ・ヘミング - ピアノ名曲集 - ポロネーズ第6番《英雄》



今日は、美味しい空気とワインを楽しみに。。。。。どこに行ったか? 詳しくは、以下。

メガ放談
今日のゴハン

| | | コメント (0) | トラックバック (0)

2006年10月20日 (金)

Mac De PL/SQL RSS Reader #23

RSS Readerの続き。ドラフト版でのテストログの続き。(その5/6)

ATOM 0.3のFEEDをテストした結果。

---------------------- PROMPT ATOM 0.3
begin...
====================
今日のゴハン - 10 articles:
http://atsu-log.way-nifty.com/today_s_menu/
====================
TITLE: 長崎堂カステラ
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/post_7031.html
Published on: 2006-10-11 23:46
—————————————-
TITLE: 豚汁
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/post_3239.html
Published on: 2006-10-11 23:36
—————————————-
TITLE: ポテトサラダ
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/post_e144.html
Published on: 2006-10-11 23:31
—————————————-
TITLE: TAKAGIのプリン、、、
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/takagi_71f8.html
Published on: 2006-10-10 22:24
—————————————-
TITLE: フンギビアンコ Part2
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/part2_21d3.html
Published on: 2006-10-10 22:20
—————————————-
TITLE: きのこ & アーティチョークのホットサンドウィッチ
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/__bbee.html
Published on: 2006-10-10 22:12
—————————————-
TITLE: 茶豆梅ご飯
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/post_cb33.html
Published on: 2006-10-10 22:04
—————————————-
TITLE: はっぴーばーすでー・ははちゃん!
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/post_f02c.html
Published on: 2006-10-09 23:07
—————————————-
TITLE: La vie Equiseのお総菜パン
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/la_vie_equise_0de3.html
Published on: 2006-10-09 20:36
—————————————-
TITLE: 納豆の揚げ包み焼き
URL : http://atsu-log.way-nifty.com/today_s_menu/2006/10/post_66d1.html
Published on: 2006-10-09 09:56
—————————————-
...End

PL/SQLプロシージャが正常に完了しました。


テストログ、ATOM 1.0へ続く。



iTMS-J、いろんなアーティストの曲がまだまだないと思っていると、The Theなんかがあったり。。。

聞いている曲:The The - NakedSelf - Swine Fever The The - NakedSelf - Swine Fever

| | | コメント (0) | トラックバック (0)

2006年10月19日 (木)

Mac De PL/SQL RSS Reader #22

RSS Readerの続き。ドラフト版でのテストログの続き。(その4/6)

RSS 2.0のFEEDをテストした結果。

---------------------- PROMPT RSS 2.0
begin...
====================
Life is a trip of looking for myself ! - 15 articles:
http://miwa2006.exblog.jp
====================
TITLE: Aji & Mic -3
URL : http://miwa2006.exblog.jp/4694707
Published on: 2006-10-08 18:51
—————————————-
TITLE: 手作りごはん
URL : http://miwa2006.exblog.jp/4694041
Published on: 2006-10-08 16:17
—————————————-
TITLE: 造形の美
URL : http://miwa2006.exblog.jp/4686673
Published on: 2006-10-07 01:24
—————————————-
TITLE: 街づくりー2
URL : http://miwa2006.exblog.jp/4672126
Published on: 2006-10-04 15:48
—————————————-
TITLE: Aji & Mic -2
URL : http://miwa2006.exblog.jp/4657697
Published on: 2006-10-01 23:32
—————————————-
TITLE: 野菜収穫
URL : http://miwa2006.exblog.jp/4647062
Published on: 2006-09-30 01:29
—————————————-
TITLE: 街づくり−1
URL : http://miwa2006.exblog.jp/4641071
Published on: 2006-09-29 00:04
—————————————-
TITLE: Aji & Mic -1
URL : http://miwa2006.exblog.jp/4626283
Published on: 2006-09-26 14:16
—————————————
TITLE: 自由が丘
URL : http://miwa2006.exblog.jp/4625821
Published on: 2006-09-26 11:55
—————————————-
TITLE: 増山理人「中南米ニカラグア・スラムの子どもたちと過ごした2年間の記憶」講演会
URL : http://miwa2006.exblog.jp/4601697
Published on: 2006-09-22 11:48
—————————————-
TITLE: Love! dessert
URL : http://miwa2006.exblog.jp/4600028
Published on: 2006-09-22 00:39
—————————————-
TITLE: 愛すべきタマゴサンド
URL : http://miwa2006.exblog.jp/4568432
Published on: 2006-09-16 23:45
—————————————-
TITLE: 料理してますよ。
URL : http://miwa2006.exblog.jp/4559138
Published on: 2006-09-15 11:25
—————————————-
TITLE: レンバイ
URL : http://miwa2006.exblog.jp/4395219
Published on: 2006-08-23 10:28
—————————————-
TITLE: Mic夏バテ?
URL : http://miwa2006.exblog.jp/4387154
Published on: 2006-08-22 10:47
—————————————-
...End

PL/SQLプロシージャが正常に完了しました。

テストログ、ATOM 0.3へ続く。



日本のバンドだけど、いいねぇ。ちょいとラテン入ってる?!


聞いている曲:ピラミッド − ピラミッド ピラミッド - ピラミッド

| | | コメント (0) | トラックバック (0)

2006年10月18日 (水)

Mac De PL/SQL RSS Reader #21

RSS Readerの続き。ドラフト版でのテストログの続き。(その3/6)

RDF (RSS 1.0) のFEEDをテストした結果。
RSS 0.91で登場したmegawattさんのブログなのだが、RDFでは記事の公開日が入っている。

---------------------- PROMPT RDF
begin...
====================
メガ放談 - 16 articles:
http://megawatt.blogdns.net/blog
====================
TITLE: アレルギーフリーな猫
URL : http://megawatt.blogdns.net/blog/304
Published on: 2006-10-11 16:42
—————————————-
TITLE: 猫は段ボールがお好き
URL : http://megawatt.blogdns.net/blog/303
Published on: 2006-10-10 11:16
—————————————-
TITLE: オクトーバーフェストに行ってきた
URL : http://megawatt.blogdns.net/blog/302
Published on: 2006-10-08 22:37
—————————————-
TITLE: オクトーバーフェストに行ってくる
URL : http://megawatt.blogdns.net/blog/301
Published on: 2006-10-07 22:30
—————————————-
TITLE: Old People
URL : http://megawatt.blogdns.net/blog/300
Published on: 2006-10-07 00:08
—————————————-
TITLE: ラップ音≠rap sounds
URL : http://megawatt.blogdns.net/blog/299
Published on: 2006-10-03 09:51
—————————————-
TITLE: 猫のための写真講座/2006年度前期・6
URL : http://megawatt.blogdns.net/blog/298
Published on: 2006-10-01 12:41
—————————————-
TITLE: 傷つきたくないからコミュニケーションしないという意見がある
URL : http://megawatt.blogdns.net/blog/297
Published on: 2006-09-28 13:23
—————————————-
TITLE: 英会話の日々
URL : http://megawatt.blogdns.net/blog/296
Published on: 2006-09-26 11:55
—————————————-
TITLE: コミュニケーション能力の減衰を食い止めるには
URL : http://megawatt.blogdns.net/blog/295
Published on: 2006-09-21 19:23
—————————————-
TITLE: くどかれました!?
URL : http://megawatt.blogdns.net/blog/294
Published on: 2006-09-19 21:43
—————————————-
TITLE: 大手町・日本工業倶楽部
URL : http://megawatt.blogdns.net/blog/293
Published on: 2006-09-18 12:59
—————————————-
TITLE: 役目を終える日
URL : http://megawatt.blogdns.net/blog/292
Published on: 2006-09-16 22:13
—————————————-
TITLE: 左向け〜
URL : http://megawatt.blogdns.net/blog/291
Published on: 2006-09-15 09:07
—————————————-
TITLE: 新しいiPod shuffleは買い、かなあ
URL : http://megawatt.blogdns.net/blog/290
Published on: 2006-09-14 16:15
—————————————-
TITLE: びーの枕
URL : http://megawatt.blogdns.net/blog/289
Published on: 2006-09-11 22:39
—————————————-
...End

PL/SQLプロシージャが正常に完了しました。

テストログ、RSS 2.0へ続く。



そう言えば、小曽根真さんのBluenote Tokyoでのライブは、話も楽しい。

聞いている曲:小曽根真トリオ − Real 小曽根 真トリオ - Real - ダンス・オン・ザ・ビーチ

| | | コメント (0) | トラックバック (0)

2006年10月17日 (火)

Mac De PL/SQL RSS Reader #20

RSS Readerの続き。ドラフト版でのテストログの続き。 (その2/6)。

RSS 0.92のFEEDをテストした結果(これも公開日が省略されている)

---------------------- PROMPT RSS 0.92
begin...
====================
AMIS Technology blog - 10 articles:
http://technology.amis.nl/blog
====================
TITLE: Rule based in Oracle 10g
URL : http://technology.amis.nl/blog/?p=1354
—————————————-
TITLE: Putting Analytical Functions to good use - find tables with multiple
foreign keys to the same referenced table
URL : http://technology.amis.nl/blog/?p=1353
—————————————-
TITLE: Extract Domain Definitions from Oracle Designer for use in JHeadstart
URL : http://technology.amis.nl/blog/?p=1352
—————————————-
TITLE: JHeadstart 10.1.3 Evaluation Copy available! - Everybody can now try out
JHeadstart for ADF Faces
URL : http://technology.amis.nl/blog/?p=1351
—————————————-
TITLE: SUSE support for next release of Oracle
URL : http://technology.amis.nl/blog/?p=1350
—————————————-
TITLE: JHeadstart 10.1.3 - Generating InputTextHyperlink items -
a generic custom generator template
URL : http://technology.amis.nl/blog/?p=1349
—————————————-
TITLE: ADF Faces: The InputTextHyperlink Component
URL : http://technology.amis.nl/blog/?p=1348
—————————————-
TITLE: Oracle BPEL PM 10.1.3 - Sending an Email to multiple recipients
URL : http://technology.amis.nl/blog/?p=1347
—————————————-
TITLE: Steve Muench publishes tutorial for JHeadstart 10.1.3 for ADF Faces
URL : http://technology.amis.nl/blog/?p=1346
—————————————-
TITLE: Unit testing with XFire ? How to test your SOAP server with a WSDL file
URL : http://technology.amis.nl/blog/?p=1345
—————————————-
...End

PL/SQLプロシージャが正常に完了しました。


テストログ、RDF (RSS 1.0)へ続く。



ボコーダーで「ド・モ・ア・リ・ガ・ト・ミ・ス・タ・ー・ロ・ボ・ッ・ト」というところ、懐かしいですね。
思い切って、ボコーダーを使うという今時の勇気あるミュージシャン居ないかねぇ。(意外と、ウケたりして・・・)

聞いている曲:
Styx - Styx Greatest Hits - Mr. RobotStyx - Styx: Greatest Hits - Mr. Roboto

| | | コメント (0) | トラックバック (0)

2006年10月16日 (月)

Mac De PL/SQL RSS Reader #19

RSS Readerの続き。課題をクリアしたドラフト版でのテスト結果。
尚、ログが長いので、今回から、6回に分けて公開していく。(そんなに引っ張るなよ〜という声が遠くでするが・・・マイペース。)

テスト用のスクリプトは以下。

GLASSCATFISH> set serveroutput on
GLASSCATFISH> !cat rss_reader.sql
PROMPT -----------------------
PROMPT RSS 0.91
execute rss_reader('http://megawatt.blogdns.net/blog/rdf91_xml');
PROMPT -----------------------
PROMPT RSS 0.92
execute rss_reader('http://technology.amis.nl/blog/?feed=rss');
PROMPT -----------------------
PROMPT RDF
execute rss_reader('http://megawatt.blogdns.net/blog/rdf10_xml');
PROMPT -----------------------
PROMPT RSS 2.0
execute rss_reader('http://miwa2006.exblog.jp/index.xml');
PROMPT -----------------------
PROMPT ATOM 0.3
execute rss_reader('http://atsu-log.way-nifty.com/today_s_menu/atom.xml');
PROMPT -----------------------
PROMPT ATOM 1.0
execute rss_reader('http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/atom.xml');

RSS 0.91のFEEDをテストした結果。(公開日が省略されている)

GLASSCATFISH> @rss_reader
---------------------- PROMPT RSS 0.91
begin...
====================
メガ放談 - 16 articles:
http://megawatt.blogdns.net/blog
====================
TITLE: アレルギーフリーな猫
URL : http://megawatt.blogdns.net/blog/304
—————————————-
TITLE: 猫は段ボールがお好き
URL : http://megawatt.blogdns.net/blog/303
—————————————-
TITLE: オクトーバーフェストに行ってきた
URL : http://megawatt.blogdns.net/blog/302
—————————————-
TITLE: オクトーバーフェストに行ってくる
URL : http://megawatt.blogdns.net/blog/301
—————————————-
TITLE: Old People
URL : http://megawatt.blogdns.net/blog/300
—————————————-
TITLE: ラップ音≠rap sounds
URL : http://megawatt.blogdns.net/blog/299
—————————————-
TITLE: 猫のための写真講座/2006年度前期・6
URL : http://megawatt.blogdns.net/blog/298
—————————————-
TITLE: 傷つきたくないからコミュニケーションしないという意見がある
URL : http://megawatt.blogdns.net/blog/297
—————————————-
TITLE: 英会話の日々
URL : http://megawatt.blogdns.net/blog/296
—————————————-
TITLE: コミュニケーション能力の減衰を食い止めるには
URL : http://megawatt.blogdns.net/blog/295
—————————————-
TITLE: くどかれました!?
URL : http://megawatt.blogdns.net/blog/294
—————————————-
TITLE: 大手町・日本工業倶楽部
URL : http://megawatt.blogdns.net/blog/293
—————————————-
TITLE: 役目を終える日
URL : http://megawatt.blogdns.net/blog/292
—————————————-
TITLE: 左向け〜
URL : http://megawatt.blogdns.net/blog/291
—————————————-
TITLE: 新しいiPod shuffleは買い、かなあ
URL : http://megawatt.blogdns.net/blog/290
—————————————-
TITLE: びーの枕
URL : http://megawatt.blogdns.net/blog/289
—————————————-
...End

PL/SQLプロシージャが正常に完了しました。

テストログ、RSS 0.92へつづく。



今週から、Bluenote Tokyoで Candy Dulferのライブですね。ファンキーなサックスを楽しむのもいいかも。(そういえば、Candy Dulferのライブに行ったのはもう2年前くらいだったか? 昨年のMarcus Miller、今年のDave Koz Dave Koz - The Dance
のライブの次に面白しいライブでした。)今年も楽しませてくれることでしょう。

| | | コメント (0) | トラックバック (0)

2006年10月14日 (土)

Mac De PL/SQL RSS Reader #18

RSS Readerの続き。問題点の回避方法。

以下は、各FEEDのルート要素を示したものだが、違いがお分かりだろうか?

<rss version="0.92">

<rss version="0.91">

<rss version="2.0"
 xmlns:dc="http://purl.org/dc/elements/1.1/"
 xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
 xmlns:admin="http://webns.net/mvcb/"
 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">

<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:admin="http://webns.net/mvcb/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:cc="http://web.resource.org/cc/"
xmlns="http://purl.org/rss/1.0/">

<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:admin="http://webns.net/mvcb/"
xmlns="http://purl.org/rss/1.0/">

<feed version="0.3"
 xmlns="http://purl.org/atom/ns#"
 xmlns:dc="http://purl.org/dc/elements/1.1/">

<feed xmlns="http://www.w3.org/2005/Atom">

もう、気付きましたよね?! そう!。 違いは、Namespaceが宣言されている点なのである。
しかも、同じ RDFでも微妙に異なる場合もある。ということで、ORA-30625というエラーの原因は、Namespaceを指定すれば解決できそうである。

残りのORA-31011については、解析に失敗していことから、おそらく、文字化けが影響しているものと想像できる。(以前にも似たようなことがあったが文字化けを回避することで解決した。

それらを踏まえてテスト用のコードを修正(前回のコードに比べると可成りコード量が増えます)し再度、確認してみると、

RSS 2.0ーー(前回はこんなエラーであった。

GLASSCATFISH> l
1 declare
2 v_req UTL_HTTP.REQ;
3 v_resp UTL_HTTP.RESP;
4 v_source VARCHAR2(1024);
5 v_tempSourceClob CLOB;
6 v_myParser DBMS_XMLPARSER.Parser;
7 v_rssDoc DBMS_XMLDOM.DomDocument;
8 v_uri httpUriType;
9 v_xml xmlType;
10 c_namespace CONSTANT VARCHAR2(500) :=
11 'xmlns:dc="http://purl.org/dc/elements/1.1/" '
12 ||'xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" '
13 ||'xmlns:admin="http://webns.net/mvcb/" '
14 ||'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"';
15 begin
16 v_req := UTL_HTTP.BEGIN_REQUEST('http://miwa2006.exblog.jp/index.xml');
17 UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1;');
18 UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
19 v_resp := UTL_HTTP.GET_RESPONSE(v_req);
20 DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
21 BEGIN
22 LOOP
23 UTL_HTTP.READ_LINE(v_resp, v_source, true);
24 v_source := v_source || UTL_TCP.CRLF;
25 DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);
26 END LOOP;
27 EXCEPTION
28 WHEN UTL_HTTP.END_OF_BODY THEN
29 UTL_HTTP.END_RESPONSE(v_resp);
30 END;
31 v_myParser := DBMS_XMLPARSER.NEWPARSER();
32 DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);
33 v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
34 v_xml := DBMS_XMLDOM.GETXMLTYPE(v_rssDoc);
35 dbms_output.put_line(
36 XmlType.extract(
37 v_xml, '/rss/channel/title/text()', c_namespace
38 ).getStringVal()
39 );
40 dbms_output.put_line(
41 XmlType.extract(
42 v_xml, '/rss/channel/link/text()', c_namespace
43 ).getStringVal()
44 );
45* end;
GLASSCATFISH> /
Life is a trip of looking for myself !
http://miwa2006.exblog.jp

PL/SQLプロシージャが正常に完了しました。

RDFーー(前回はこのようなエラーであった。

GLASSCATFISH> l
1 declare
2 v_req UTL_HTTP.REQ;
3 v_resp UTL_HTTP.RESP;
4 v_source VARCHAR2(1024);
5 v_tempSourceClob CLOB;
6 v_myParser DBMS_XMLPARSER.Parser;
7 v_rssDoc DBMS_XMLDOM.DomDocument;
8 v_uri httpUriType;
9 v_xml xmlType;
10 c_namespace CONSTANT VARCHAR2(500) :=
11 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" '
12 ||'xmlns:dc="http://purl.org/dc/elements/1.1/" '
13 ||'xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" '
14 ||'xmlns:admin="http://webns.net/mvcb/" '
15 ||'xmlns:content="http://purl.org/rss/1.0/modules/content/" '
16 ||'xmlns:cc="http://web.resource.org/cc/" '
17 ||'xmlns="http://purl.org/rss/1.0/"';
18 begin
19 v_req := UTL_HTTP.BEGIN_REQUEST(
20 'https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/index.rdf'
21 );
22 UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1;');
23 UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
24 v_resp := UTL_HTTP.GET_RESPONSE(v_req);
25 DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
26 BEGIN
27 LOOP
28 UTL_HTTP.READ_LINE(v_resp, v_source, true);
29 v_source := v_source || UTL_TCP.CRLF;
30 DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);
31 END LOOP;
32 EXCEPTION
33 WHEN UTL_HTTP.END_OF_BODY THEN
34 UTL_HTTP.END_RESPONSE(v_resp);
35 END;
36 v_myParser := DBMS_XMLPARSER.NEWPARSER();
37 DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);
38 v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
39 v_xml := DBMS_XMLDOM.GETXMLTYPE(v_rssDoc);
40 dbms_output.put_line(
41 XmlType.extract(
42 v_xml, '/rdf:RDF/channel/title/text()', c_namespace
43 ).getStringVal()
44 );
45 dbms_output.put_line(
46 XmlType.extract(
47 v_xml, '/rdf:RDF/channel/link/text()', c_namespace
48 ).getStringVal()
49 );
50* end;
GLASSCATFISH> /
Mac De Oracle
https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/

PL/SQLプロシージャが正常に完了しました。

GLASSCATFISH> l
1 declare
2 v_req UTL_HTTP.REQ;
3 v_resp UTL_HTTP.RESP;
4 v_source VARCHAR2(1024);
5 v_tempSourceClob CLOB;
6 v_myParser DBMS_XMLPARSER.Parser;
7 v_rssDoc DBMS_XMLDOM.DomDocument;
8 v_uri httpUriType;
9 v_xml xmlType;
10 c_namespace CONSTANT VARCHAR2(500) :=
11 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" '
12 ||'xmlns:dc="http://purl.org/dc/elements/1.1/" '
13 ||'xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" '
14 ||'xmlns:admin="http://webns.net/mvcb/" '
15 ||'xmlns="http://purl.org/rss/1.0/"';
16 begin
17 v_req := UTL_HTTP.BEGIN_REQUEST(
18 'http://megawatt.blogdns.net/blog/rdf10_xml'
19 );
20 UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1;');
21 UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
22 v_resp := UTL_HTTP.GET_RESPONSE(v_req);
23 DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
24 BEGIN
25 LOOP
26 UTL_HTTP.READ_LINE(v_resp, v_source, true);
27 v_source := v_source || UTL_TCP.CRLF;
28 DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);
29 END LOOP;
30 EXCEPTION
31 WHEN UTL_HTTP.END_OF_BODY THEN
32 UTL_HTTP.END_RESPONSE(v_resp);
33 END;
34 v_myParser := DBMS_XMLPARSER.NEWPARSER();
35 DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);
36 v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
37 v_xml := DBMS_XMLDOM.GETXMLTYPE(v_rssDoc);
38 dbms_output.put_line(
39 XmlType.extract(
40 v_xml, '/rdf:RDF/channel/title/text()', c_namespace
41 ).getStringVal()
42 );
43 dbms_output.put_line(
44 XmlType.extract(
45 v_xml, '/rdf:RDF/channel/link/text()', c_namespace
46 ).getStringVal()
47 );
48* end;
GLASSCATFISH> /
メガ放談
http://megawatt.blogdns.net/blog

PL/SQLプロシージャが正常に完了しました。

最後にATOMーー(前回はこのようなエラーであった

GLASSCATFISH> l
1 declare
2 v_req UTL_HTTP.REQ;
3 v_resp UTL_HTTP.RESP;
4 v_source VARCHAR2(1024);
5 v_tempSourceClob CLOB;
6 v_myParser DBMS_XMLPARSER.Parser;
7 v_rssDoc DBMS_XMLDOM.DomDocument;
8 v_uri httpUriType;
9 v_xml xmlType;
10 c_namespace CONSTANT VARCHAR2(500) :=
11 'xmlns="http://purl.org/atom/ns#" '
12 ||'xmlns:dc="http://purl.org/dc/elements/1.1/"';
13 begin
14 v_req := UTL_HTTP.BEGIN_REQUEST(
15 'http://atsu-log.way-nifty.com/today_s_menu/atom.xml'
16 );
17 UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1;');
18 UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
19 v_resp := UTL_HTTP.GET_RESPONSE(v_req);
20 DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
21 BEGIN
22 LOOP
23 UTL_HTTP.READ_LINE(v_resp, v_source, true);
24 v_source := v_source || UTL_TCP.CRLF;
25 DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);
26 END LOOP;
27 EXCEPTION
28 WHEN UTL_HTTP.END_OF_BODY THEN
29 UTL_HTTP.END_RESPONSE(v_resp);
30 END;
31 v_myParser := DBMS_XMLPARSER.NEWPARSER();
32 DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);
33 v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
34 v_xml := DBMS_XMLDOM.GETXMLTYPE(v_rssDoc);
35 dbms_output.put_line(
36 XmlType.extract(
37 v_xml, '/feed/title/text()', c_namespace
38 ).getStringVal()
39 );
40 dbms_output.put_line(
41 XmlType.extract(
42 v_xml, '/feed/link[@type="text/html"]/@href', c_namespace
43 ).getStringVal()
44 );
45* end;
GLASSCATFISH> /
今日のゴハン
http://atsu-log.way-nifty.com/today_s_menu/

PL/SQLプロシージャが正常に完了しました。

GLASSCATFISH>
GLASSCATFISH> l
1 declare
2 v_req UTL_HTTP.REQ;
3 v_resp UTL_HTTP.RESP;
4 v_source VARCHAR2(1024);
5 v_tempSourceClob CLOB;
6 v_myParser DBMS_XMLPARSER.Parser;
7 v_rssDoc DBMS_XMLDOM.DomDocument;
8 v_uri httpUriType;
9 v_xml xmlType;
10 c_namespace CONSTANT VARCHAR2(500) :=
11 'xmlns="http://www.w3.org/2005/Atom"';
12 begin
13 v_req := UTL_HTTP.BEGIN_REQUEST(
14 'http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/atom.xml'
15 );
16 UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1;');
17 UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
18 v_resp := UTL_HTTP.GET_RESPONSE(v_req);
19 DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
20 BEGIN
21 LOOP
22 UTL_HTTP.READ_LINE(v_resp, v_source, true);
23 v_source := v_source || UTL_TCP.CRLF;
24 DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);
25 END LOOP;
26 EXCEPTION
27 WHEN UTL_HTTP.END_OF_BODY THEN
28 UTL_HTTP.END_RESPONSE(v_resp);
29 END;
30 v_myParser := DBMS_XMLPARSER.NEWPARSER();
31 DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);
32 v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
33 v_xml := DBMS_XMLDOM.GETXMLTYPE(v_rssDoc);
34 dbms_output.put_line(
35 XmlType.extract(
36 v_xml, '/feed/title/text()', c_namespace
37 ).getStringVal()
38 );
39 dbms_output.put_line(
40 XmlType.extract(
41 v_xml, '/feed/link[@type="text/html"]/@href', c_namespace
42 ).getStringVal()
43 );
44* end;
GLASSCATFISH> /
Radical Imagination -from 鎌倉 with Love
http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/

PL/SQLプロシージャが正常に完了しました。

GLASSCATFISH>

ということで、全て解決!!。




ほんとうに曲数が少ないのは iTMSの抱える課題だ。ということで、今回もAmazonへ

聞いている曲:Candy Dulfer - ベスト・オブ・キャンディ・ダルファー

| | | コメント (0) | トラックバック (0)

2006年10月13日 (金)

Mac De PL/SQL RSS Reader #17

RSS Readerの続きです。
さて、RSSリーダーを作る上でもう一つクリアしておきたい課題がある。
それは、各記事のタイトルとURL、公開日以外に、各FEEDのタイトルとURLを取得すること。

BLOGに関して、それぞれの記事のタイトルとURL以外に、BLOGのタイトルとURLも表示したいと考えている。
作成中のRSSリーダーでは、DOMを利用していので、そのBLOGのタイトルとURLもDOMをトラバースして取得することも可能なのだが、ロジックが複雑になるため、BLOGのタイトルとURLに関しては、XPATHを利用して取り出すことにした。

当初、DBMS_XMLPARSER.PARSE(URL)を利用しようとしていたが、URL指定でパースした場合にマルチバイト文字で文字化けが発生したため、UTL_HTTPパッケージDBMS_XMLPARSER.PARSECLOB(CLOB)を使用して回避している。XPATHを利用しBLOGタイトルとURIの取得テストでは、DBMS_XMLPARSER.PARSE(URL)同様に文字化けするのだが、HttpUriType型を利用してXML(XmlType型)を取り出し、XPATHでBLOGタイトルとURLを取得することにした。
HttpUriType型と、XmlType型の2つはどちらもオブジェクト型である。詳細は、マニュアル「PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス」のXmlTypeHttpUriType参照のこと。

以下は、RSS 0.92 の FEEDである「AMIS Technorogy blog」のタイトルとURLをXPATHを利用して取り出したもの。シングルバイトなので文字化けは起こらない。楽でいいね〜。マルチバイト文字が含まれていても文字化けしなくなれば、この方法が利用できるようになるので楽なのだが、Oracleさんなんとかして。。。

GLASSCATFISH> set serveroutput on
GLASSCATFISH> l
1 declare
2 v_uri httpUriType;
3 v_xml xmlType;
4 begin
5 v_uri := HttpUriType.createUri('http://technology.amis.nl/blog/?feed=rss');
6 v_xml := v_uri.getXML();
7 dbms_output.put_line(v_xml.extract('/rss/channel/title/text()').getStringVal());
8 dbms_output.put_line(v_xml.extract('/rss/channel/link/text()').getStringVal());
9* end;
GLASSCATFISH> /
AMIS Technology blog
http://technology.amis.nl/blog

PL/SQLプロシージャが正常に完了しました。

GLASSCATFISH>

ところが、友人のmegawattさんの「メガ放談」(RSS 0.91) のタイトルとURLをXPATHを利用して取り出すと、タイトルは見事に文字化けする。DBMS_XMLPARSER.PARSE(URL)を利用した場合と同じく文字化けするが、取り出せることは取り出せる。文字化けの回避方法はあるのでとりあえずこのままテスト続行!。

GLASSCATFISH> l
1 declare
2 v_uri httpUriType;
3 v_xml xmlType;
4 begin
5 v_uri := HttpUriType.createUri('http://megawatt.blogdns.net/blog/rdf91_xml');
6 v_xml := v_uri.getXML();
7 dbms_output.put_line(v_xml.extract('/rss/channel/title/text()').getStringVal());
8 dbms_output.put_line(v_xml.extract('/rss/channel/link/text()').getStringVal());
9* end;
GLASSCATFISH> /
a?!a?¬a??e≪?
http://megawatt.blogdns.net/blog

PL/SQLプロシージャが正常に完了しました。

3つ目は、RSS 2.0なのだが、RSS 0.9xとは異なりエラーとなる。何故?
幸か不幸か、今回は、RSS 0.9x以外のFEEDフォーマットにも対応しなければならない。

GLASSCATFISH> l
1 declare
2 v_uri httpUriType;
3 v_xml xmlType;
4 begin
5 v_uri := HttpUriType.createUri('http://miwa2006.exblog.jp/index.xml');
6 v_xml := v_uri.getXML();
7 dbms_output.put_line(v_xml.extract('/rss/channel/title/text()').getStringVal());
8 dbms_output.put_line(v_xml.extract('/rss/channel/link/text()').getStringVal());
9* end;
GLASSCATFISH> /
declare
*
行1でエラーが発生しました。:
ORA-31011: XML解析に失敗しました
ORA-19202: XML処理
LPX-00225: 要素の終了タグ"item"が開始タグ"title"と一致しません
Error at line 94
中にエラーが発生しました
ORA-06512: "SYS.XMLTYPE", 行5
ORA-06512: "SYS.HTTPURITYPE", 行97
ORA-06512: 行6


GLASSCATFISH>

つづいて、RDF (RSS 1.0)を試す。どちらも、RDFなのだが上手くいかない。しかも、エラーメッセージがそれぞれ異なる。なぜ?? 

GLASSCATFISH> l
1 declare
2 v_uri httpUriType;
3 v_xml xmlType;
4 begin
5 v_uri := HttpUriType.createUri('https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/index.rdf');
6 v_xml := v_uri.getXML();
7 dbms_output.put_line(v_xml.extract('/rdf:RDF/channel/title/text()').getStringVal());
8 dbms_output.put_line(v_xml.extract('/rdf:RDF/channel/link/text()').getStringVal());
9* end;
GLASSCATFISH> /
declare
*
行1でエラーが発生しました。:
ORA-31011: XML解析に失敗しました
ORA-19202: XML処理
LPX-00225: 要素の終了タグ"channel"が開始タグ"description"と一致しません
Error at line 42
中にエラーが発生しました
ORA-06512: "SYS.XMLTYPE", 行5
ORA-06512: "SYS.HTTPURITYPE", 行97
ORA-06512: 行6


GLASSCATFISH> l
1 declare
2 v_uri httpUriType;
3 v_xml xmlType;
4 begin
5 v_uri := HttpUriType.createUri('http://megawatt.blogdns.net/blog/rdf10_xml');
6 v_xml := v_uri.getXML();
7 dbms_output.put_line(v_xml.extract('/rdf:RDF/channel/title/text()').getStringVal());
8 dbms_output.put_line(v_xml.extract('/rdf:RDF/channel/link/text()').getStringVal());
9* end;
GLASSCATFISH> /
declare
*
行1でエラーが発生しました。:
ORA-30625: NULL SELF引数のメソッド・ディスパッチは使用できません
ORA-06512: 行7


GLASSCATFISH>


最後にATOM。結果は、RSS 2.0、RDF、ATOMは撃沈である。ここでも、エラー内容がそれぞれ異なる。
この方法では、上手くいったのは、RSS 0.9xのみ。 どうする。。 (><)。
GLASSCATFISH> l
1 declare
2 v_uri httpUriType;
3 v_xml xmlType;
4 begin
5 v_uri := HttpUriType.createUri('http://atsu-log.way-nifty.com/today_s_menu/atom.xml');
6 v_xml := v_uri.getXML();
7 dbms_output.put_line(v_xml.extract('/feed/title/text()').getStringVal());
8 dbms_output.put_line(v_xml.extract('/feed/link[@type="text/html"]/@href').getStringVal());
9* end;
GLASSCATFISH> /
declare
*
行1でエラーが発生しました。:
ORA-31011: XML解析に失敗しました
ORA-19202: XML処理
LPX-00225: 要素の終了タグ"a"が開始タグ"p"と一致しません
Error at line 30
中にエラーが発生しました
ORA-06512: "SYS.XMLTYPE", 行5
ORA-06512: "SYS.HTTPURITYPE", 行97
ORA-06512: 行6

GLASSCATFISH> l
1 declare
2 v_uri httpUriType;
3 v_xml xmlType;
4 begin
5 v_uri := HttpUriType.createUri('http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/atom.xml');
6 v_xml := v_uri.getXML();
7 dbms_output.put_line(v_xml.extract('/feed/title/text()').getStringVal());
8 dbms_output.put_line(v_xml.extract('/feed/link[@type="text/html"]/@href').getStringVal());
9* end;
GLASSCATFISH> /
declare
*
行1でエラーが発生しました。:
ORA-30625: NULL SELF引数のメソッド・ディスパッチは使用できません
ORA-06512: 行7


GLASSCATFISH>

これらの解決方法は????

エラーメッセージもいろいろ。上記のコードを眺めていても原因は全く浮かばない。

視点を変え、各FEEDのXMLソースを眺めると、RSS 0.9xとRSS2.0、RDF、ATOMのXMLには異なる点があるのみ気付く。それが何か分かりますか? このエラー原因の一なのである。
http://technology.amis.nl/blog/?feed=rss
http://megawatt.blogdns.net/blog/rdf91_xml
http://miwa2006.exblog.jp/index.xml
http://megawatt.blogdns.net/blog/rdf10_xml
http://atsu-log.way-nifty.com/today_s_menu/atom.xml
http://www.kaminoge-design.tamabi.ac.jp/faculty/takami/atom.xml


今日はここまで、次回はこれらの回避方法。



聞いている曲:え〜〜と、iTMSにはこれもありませんでした。。Gloria Estefan & Miami Sound Machine - 1.2.3

| | | コメント (0) | トラックバック (0)

2006年10月12日 (木)

Mac De PL/SQL RSS Reader #16

前回のつづき。rfc3339の日付/日時フォーマット対応ストアドプロシージャのソースコードは以下。。

create or replace FUNCTION RFC3339_DateStrToTimeStampLTZ
(
i_dateTimeString IN VARCHAR2
)
RETURN TIMESTAMP WITH LOCAL TIME ZONE
AS
v_tempTimeStampTz TIMESTAMP WITH LOCAL TIME ZONE := NULL;
v_dateTimeString VARCHAR2(50) := NULL;
v_timeStampTzFormat VARCHAR2(40) := NULL;
c_nls_date_language CONSTANT VARCHAR2(40) := 'NLS_DATE_LANGUAGE=AMERICAN';
BEGIN
v_dateTimeString := REGEXP_REPLACE(
REGEXP_REPLACE(
UPPER(i_dateTimeString),
'T', NULL, 1, 1
),
'Z$', 'UTC'
);

IF v_dateTimeString IS NULL THEN
RETURN NULL;
END IF;


v_timeStampTzFormat := 'RRRR-MM-DD';

IF LENGTH(v_dateTimeString) >= 10 THEN

IF REGEXP_INSTR(v_dateTimeString , '([0-9]{2}:){2}[0-9]{2}\.?') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI:SSXFF';
END IF;

IF REGEXP_INSTR(v_dateTimeString, '(\+|\-)[0-9]{2}:[0-9]{2}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZH:TZM';
ELSIF REGEXP_INSTR(v_dateTimeString, 'UTC$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZR';
END IF;
END IF;

v_tempTimeStampTz := TO_TIMESTAMP_TZ(
v_dateTimeString,
v_timeStampTzFormat,
c_nls_date_language
);

RETURN v_tempTimeStampTz;

EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(
-20002,
'RFC3339_DateStrToTimeStampLTZ():' || SQLERRM()
);
END;
/
前々回も書いたように、入力される文字列はrfc3339日付/日時フォーマットを前提としているため、入力書式の厳密なチェックなどは行っていない。
聞いている曲:
やはり、80sにはすばらしい曲、というか記憶に残る曲が多いよね。 まぁ、このAvalonに関しては、日産のCMで使われたりしたので最近の曲? という方も多いのだけれど。。。
Roxy Music - Avalon - The Space Between Roxy Music - Avalon - The Space Between

| | | コメント (0) | トラックバック (0)

2006年10月11日 (水)

Mac De PL/SQL RSS Reader #15

前回のつづき。簡単なテスト。

GLASSCATFISH> !cat rfc3339_func_test.sql
select rfc3339_datestrtotimestampltz('2003-12-13T18:30:02.25Z') from dual;
select rfc3339_datestrtotimestampltz('2003-12-13T18:30:02Z') from dual;
select rfc3339_datestrtotimestampltz('2003-12-13T18:30:02+09:00') from dual;
select rfc3339_datestrtotimestampltz('2003-12-13T18:30:02-09:00') from dual;
select rfc3339_datestrtotimestampltz('2003-12-13T18:30:02.25-09:00') from dual;
select rfc3339_datestrtotimestampltz('2003-12-13T18:30:02.25+09:00') from dual;
select rfc3339_datestrtotimestampltz('2003-12-13') from dual;

GLASSCATFISH> @rfc3339_func_test

RFC3339_DATESTRTOTIMESTAMPLTZ('2003-12-13T18:30:02.25Z')
---------------------------------------------------------------------------
03-12-14 03:30:02.250000000


RFC3339_DATESTRTOTIMESTAMPLTZ('2003-12-13T18:30:02Z')
---------------------------------------------------------------------------
03-12-14 03:30:02.000000000


RFC3339_DATESTRTOTIMESTAMPLTZ('2003-12-13T18:30:02+09:00')
---------------------------------------------------------------------------
03-12-13 18:30:02.000000000


RFC3339_DATESTRTOTIMESTAMPLTZ('2003-12-13T18:30:02-09:00')
---------------------------------------------------------------------------
03-12-14 12:30:02.000000000


RFC3339_DATESTRTOTIMESTAMPLTZ('2003-12-13T18:30:02.25-09:00')
---------------------------------------------------------------------------
03-12-14 12:30:02.250000000


RFC3339_DATESTRTOTIMESTAMPLTZ('2003-12-13T18:30:02.25+09:00')
---------------------------------------------------------------------------
03-12-13 18:30:02.250000000


RFC3339_DATESTRTOTIMESTAMPLTZ('2003-12-13')
---------------------------------------------------------------------------
03-12-13 00:00:00.000000000

GLASSCATFISH>

rfc822よりはシンプルね。

今日はここまで。次回はソースコードを載せる予定。



聞いている曲:iTMS Japanの曲の少なさはまだまだ改善されず。。。まあ、Tribute Albumも悪くないんですが、やはり、オリジナルも聞きたいわけで。。。iTMS Japanに無いものはAmazonか。。
Sade Tribute Band - Smooth Sax Tribute To Sade Sade Tribute Band - Smooth Sax Tribute To Sade - Smooth Operator



| | | コメント (0) | トラックバック (0)

2006年10月10日 (火)

Mac De PL/SQL RSS Reader #14

前回につづき、今回は、rfc3339の日付/時間フォーマット向けに、暫定版として RFC3339_DateStrToTimeStampLTZ()というストアドプロシージャを作ってみた。この関数も、rfc3339日付/時間フォーマットを文字列として入力し、TIMESTAMP WITH LOCAL TIME ZONE型で返すようにした。

尚、rfc3339で規定されている time-offset の "Z"は、ストアドファンクション内部で "UTC"に変換して扱うことにした。

また、入力する文字列もrfc3339の日付/時間フォーマットであることを前提とし、 RFC3339_DateStrToTimeStampLTZ()ファンクション内部ではフォーマットの厳密な等は行わない。(但し、オラクルのTO_TIMESTAMP_TZ()関数では、オラクルの日付/時間書式に対するチェックは行われる。)


次回は、簡単なテストとソースコードを載せる予定。



秋、おいしいワインの新酒が楽しみな季節になってきました。今年のできはどうなんだろう?
などど思いながら、私のお気に入り、高畠ワイナリーのブログを見ると、、、「予想以上」というタイトル!。 しかし、ワインの話ではなく、昨日まで開催さてていた、恒例の「秋の収穫祭」の来場者の話題でした。。。(^^;;

ところで、高畠ワイナリーさん主催のワインセミナーがあったと記憶しているが、最近はどうなんだろう?。ホームページを見ても書いていないので、時間が取れそうなら確認してみようかと。

以前(といっても、もう7年も前だが)、私も参加したことがある。高畠ワイナリーで生産しているワインをテイスティングしてワインの楽しみ方を知るというもの(とは言っても、入門レベルだけど、これが結構楽しい)

その時に貰ったセミナーの終了証がこれ。(ちなみにワインセミナーは、ワイナリー内で行われていたので、当日は、新幹線で日帰りした記憶が・・・・・。結構、飲む事になるので、車では絶対行けませんよ。)

Takahata_wine_certified

奥さんのブログ「今日のゴハン」でもなんどか書いてましたね。

| | | コメント (0) | トラックバック (0)

2006年10月 9日 (月)

Mac De PL/SQL RSS Reader #13

前回の続き。ストアドファンクションrfc822_dateStrToTimestampLTZ()のソースコードは以下の通り。

CREATE OR REPLACE FUNCTION RFC822_DateStrToTimeStampLTZ
(
i_dateTimeString IN VARCHAR2
)
RETURN TIMESTAMP WITH LOCAL TIME ZONE
AS
v_tempTimeStampTz TIMESTAMP WITH LOCAL TIME ZONE := NULL;
v_dateTimeString VARCHAR2(50) := NULL;
v_timeStampTzFormat VARCHAR2(40) := NULL;
c_nls_date_language CONSTANT VARCHAR2(40) := 'NLS_DATE_LANGUAGE=AMERICAN';
BEGIN
v_dateTimeString := UPPER(i_dateTimeString);
IF v_dateTimeString IS NULL THEN
RETURN NULL;
END IF;

IF REGEXP_INSTR(v_dateTimeString, '^[A-Z]{3}\, ') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'DY, ';
END IF;

v_timeStampTzFormat := v_timeStampTzFormat || 'DD MON RRRR';

IF REGEXP_INSTR(v_dateTimeString, '[0-9]{2}:[0-9]{2}:[0-9]{2}') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI:SS ';
ELSIF REGEXP_INSTR(v_dateTimeString, '[0-9]{2}:[0-9]{2}') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'HH24:MI ';
END IF;

IF REGEXP_INSTR(v_dateTimeString, ' (\+|\-)[0-9]{4}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZHTZM';
ELSIF REGEXP_INSTR(v_dateTimeString, ':[0-9]{2}$') = 0
AND REGEXP_INSTR(v_dateTImeString, '[0-9]{2} [A-Z]{3} [0-9]{2,4}$') = 0
AND REGEXP_INSTR(v_dateTImeString, ' [A-Z]{1,3}$') >= 1 THEN
v_timeStampTzFormat := v_timeStampTzFormat || 'TZR';
END IF;

v_tempTimeStampTz := TO_TIMESTAMP_TZ(
v_dateTimeString,
v_timeStampTzFormat,
c_nls_date_language
);

RETURN v_tempTimeStampTz;

EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(
-20001,
'RFC822_DateStrToTimeStampLTZ():' || SQLERRM()
);
END;
/

というように、日付/時間文字列を一旦、TO_TIMESTAMP_TZ()関数でTIMESTAMP WITH TIME ZONE型に変換する。この時点でタイムゾーンリージョンが指定されていれば、夏時間も考慮され変換される。最終的に、TIMESTAMP WITH LOCAL TIME ZONE型で返すようにした。(尚、入力される日付/日時文字列が、RFC822形式であることを前提にしているため文字列妥当性などのチェックは厳密は行っていない。)

・・・・ファンクション名は変えたほうがいいか・・・・



聞いている曲:今日もSmooth Jazzで。
Warren Hill - Popjazz - Toronto Warren Hill - Popjazz - Toronto

| | | コメント (0) | トラックバック (0)

2006年10月 8日 (日)

Mac De PL/SQL RSS Reader #12

PL/SQL RSS Readerの続きです。では、早速、rfc822_dateStrToTimestampLTZ()のテストから、

GLASSCATFISH> !cat rfc822_func_test.sql
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 GMT') from dual;
select rfc822_datestrtotimestampltz('07 Sep 2002 00:00:01 GMT') from dual;
select rfc822_datestrtotimestampltz('07 Sep 02 00:00:01 GMT') from dual;
select rfc822_datestrtotimestampltz('07 Sep 02 00:00 GMT') from dual;
select rfc822_datestrtotimestampltz('07 Sep 02') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 02 00:00:01 +0900') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 02 00:00:01 -0900') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 EST') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 CST') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 MST') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 PST') from dual;
select rfc822_datestrtotimestampltz('Sun, 8 Oct 2006 07:00:00 +0900') from dual;
select rfc822_datestrtotimestampltz('Sat, 7 Oct 2006 22:00:00 GMT') from dual;
PROMPT 夏時間の確認
select rfc822_datestrtotimestampltz('7 Oct 2006 18:00:00 EST') from dual;
select rfc822_datestrtotimestampltz('7 Oct 2006 18:00:00 -0500') from dual;
select rfc822_datestrtotimestampltz('7 Oct 2006 18:00:00 -0400') from dual;
select rfc822_datestrtotimestampltz('31 Oct 2006 22:00:00 EST') from dual;
select rfc822_datestrtotimestampltz('31 Oct 2006 22:00:00 -0500') from dual;
select rfc822_datestrtotimestampltz('31 Oct 2006 22:00:00 -0400') from dual;
PROMPT オラクルのタイムゾーンリージョンに無いコード
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 EDT') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 CDT') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 MDT') from dual;
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 PDT') from dual;
--
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 UT') from dual;


GLASSCATFISH> @rfc822_func_test

RFC822_DATESTRTOTIMESTAMPLTZ('SAT,07SEP200200:00:01GMT')
---------------------------------------------------------------------------
02-09-07 09:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('07SEP200200:00:01GMT')
---------------------------------------------------------------------------
02-09-07 09:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('07SEP0200:00:01GMT')
---------------------------------------------------------------------------
02-09-07 09:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('07SEP0200:00GMT')
---------------------------------------------------------------------------
02-09-07 09:00:00.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('07SEP02')
---------------------------------------------------------------------------
02-09-07 00:00:00.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('SAT,07SEP0200:00:01+0900')
---------------------------------------------------------------------------
02-09-07 00:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('SAT,07SEP0200:00:01-0900')
---------------------------------------------------------------------------
02-09-07 18:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('SAT,07SEP200200:00:01EST')
---------------------------------------------------------------------------
02-09-07 13:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('SAT,07SEP200200:00:01CST')
---------------------------------------------------------------------------
02-09-07 14:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('SAT,07SEP200200:00:01MST')
---------------------------------------------------------------------------
02-09-07 15:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('SAT,07SEP200200:00:01PST')
---------------------------------------------------------------------------
02-09-07 16:00:01.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('SUN,8OCT200607:00:00+0900')
---------------------------------------------------------------------------
06-10-08 07:00:00.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('SAT,7OCT200622:00:00GMT')
---------------------------------------------------------------------------
06-10-08 07:00:00.000000000

夏時間の確認

RFC822_DATESTRTOTIMESTAMPLTZ('7OCT200618:00:00EST')
---------------------------------------------------------------------------
06-10-08 07:00:00.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('7OCT200618:00:00-0500')
---------------------------------------------------------------------------
06-10-08 08:00:00.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('7OCT200618:00:00-0400')
---------------------------------------------------------------------------
06-10-08 07:00:00.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('31OCT200622:00:00EST')
---------------------------------------------------------------------------
06-11-01 12:00:00.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('31OCT200622:00:00-0500')
---------------------------------------------------------------------------
06-11-01 12:00:00.000000000


RFC822_DATESTRTOTIMESTAMPLTZ('31OCT200622:00:00-0400')
---------------------------------------------------------------------------
06-11-01 11:00:00.000000000

オラクルのタイムゾーンリージョンに無いコード
select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 EDT') from dual
*
行1でエラーが発生しました。:
ORA-20001: RFC822_DateStrToTimeStampLTZ():ORA-01882:
タイムゾーンのリージョンが見つかりません。
ORA-06512: "GLASSCATFISH.RFC822_DATESTRTOTIMESTAMPLTZ", 行48


select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 CDT') from dual
*
行1でエラーが発生しました。:
ORA-20001: RFC822_DateStrToTimeStampLTZ():ORA-01882:
タイムゾーンのリージョンが見つかりません。
ORA-06512: "GLASSCATFISH.RFC822_DATESTRTOTIMESTAMPLTZ", 行48v

select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 MDT') from dual
*
行1でエラーが発生しました。:
ORA-20001: RFC822_DateStrToTimeStampLTZ():ORA-01882:
タイムゾーンのリージョンが見つかりません。
ORA-06512: "GLASSCATFISH.RFC822_DATESTRTOTIMESTAMPLTZ", 行48


select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 PDT') from dual
*
行1でエラーが発生しました。:
ORA-20001: RFC822_DateStrToTimeStampLTZ():ORA-01882:
タイムゾーンのリージョンが見つかりません。
ORA-06512: "GLASSCATFISH.RFC822_DATESTRTOTIMESTAMPLTZ", 行48


select rfc822_datestrtotimestampltz('Sat, 07 Sep 2002 00:00:01 UT') from dual
*
行1でエラーが発生しました。:
ORA-20001: RFC822_DateStrToTimeStampLTZ():ORA-01882:
タイムゾーンのリージョンが見つかりません。
ORA-06512: "GLASSCATFISH.RFC822_DATESTRTOTIMESTAMPLTZ", 行48


GLASSCATFISH>

と、このような感じになる。夏時間は自動計算されているようだ。
ということは・・・、"EDT"/"CDT"/"MDT"/"PDT"は単純に"EST"/"CST"/"MST"/"PST"に置換するだけで対応できるかも!?

テストのログが長くなってしまったので、今日はここまで。ストアドファンクションのソースコードは次回。



いろいろと行事があって、少々疲れ気味。。。やはりこんなときは、Smooth Jazz....
ということで、今日も Internet Radio SmoothJazz.com

| | | コメント (0) | トラックバック (0)

2006年10月 7日 (土)

Mac De PL/SQL RSS Reader #11

PL/SQLで RSS Readerの続きです。

feedの日付/時間は、rfc822または、rfc3339に準拠ということだった。
今回は、元のタイムゾーンは重要ではないので、timestamp with local time zone型に変換して持つことにした。

まず、暫定だがrfc822向けに、rfc822_dateStrToTimestampLTZ()というストアドファンクションを作成した。
rfc822形式の日時文字列をOracle10g の Timestamp with local time zone型に変換するストアドファンクションである。

尚、rfc822のタイムゾーンとして、

     zone        =  "UT"  / "GMT"

/ "EST" / "EDT"
/ "CST" / "CDT"
/ "MST" / "MDT"
/ "PST" / "PDT"
/ 1ALPHA

/ ( ("+" / "-") 4DIGIT )

と規定されているが、"UT"及び、夏時間(*DT)、英字1文字以外についてのみ実装した。(Oracle10gのto_timestamp_tz()関数で指定できるタイムゾーンリージョン "GMT"/"EST"/"CST"/"MST"/"PST" だけに対応した。"UT"、夏時間、英字1文字、または、上記、以外のタイムゾーンが設定されているような状況があれば、その時に考えることに。ちなみに、私が購読しているblogのfeedでは、"GMT"しか利用されていないのも理由の一つである。)

Oracle10gで指定できるタイムゾーンリージョンは以下のようにして確認できる。詳細は、「マニュアル「Oracle10 database SQLクイック・リファレンス 10g」を参照のこと

SYS> select
2 unique tzname
3 from
4 v$timezone_names
5 where
6 tzname in (
7 'UT','GMT','EST','EDT','CST','CDT','MST','MDT','PST','PDT'
8 )
9 or length(tzname)=1;


TZNAME
----------------------------------------------------------------
CST
EST
GMT
MST
PST

SYS>

今日はここまで。次回は、上記仕様で作成したストアドファンクションのテスト及び、ソースを載せる予定。



2006/10/8追記。

SQLリファレンスの夏時間に関する記述を見ると、OracleのTimestamp with time zone型に関して、タイムゾーンリージョンを指定すると夏時間を考慮した変換がなされるとある。


聞いている曲:うん〜〜〜 iTunesで聞いているのは、Internet Radioなので、曲紹介はなし。
      ちなみに、聞いているInternet Radioは、 SmoothJazz.com

| | | コメント (0) | トラックバック (0)

2006年10月 6日 (金)

Mac De PL/SQL RSS Reader #10

FEEDフォーマットの判定テストの結果。これで、FEEDフォーマットの判定はできるようになった。

URL:http://megawatt.blogdns.net/blog/rdf91_xml
FEEDフォーマット:RSS version = 0.91

URL:http://technology.amis.nl/blog/?feed=rss
FEEDフォーマット:RSS version = 0.92

URL:http://megawatt.blogdns.net/blog/rdf10_xml
FEEDフォーマット:RDF version =

URL:http://miwa2006.exblog.jp/index.xml
FEEDフォーマット:RSS version = 2.0

URL:http://atsu-log.way-nifty.com/today_s_menu/atom.xml
FEEDフォーマット:ATOM version = 0.3

URL:https://discus-hamburg.cocolog-nifty.com/mac_de_oracle/atom.xml
FEEDフォーマット:ATOM version =

今回のテストで使ったソースコードは以下。

CREATE OR REPLACE PROCEDURE RSS_FEED_READER_TEST
(
i_url IN VARCHAR2
)
AS
--============ TYPEs/VARIABLEs ===============================================
C_RSS CONSTANT VARCHAR2(7) := 'rss';
C_RDF CONSTANT VARCHAR2(7) := 'rdf:RDF';
C_ATOM CONSTANT VARCHAR2(7) := 'feed';
C_ATTR_NAME_VERSION CONSTANT VARCHAR2(7) := 'version';

v_url VARCHAR2(1024);

v_myParser DBMS_XMLPARSER.Parser;
v_rssDoc DBMS_XMLDOM.DomDocument;
v_itemNode DBMS_XMLDOM.DomNode;
v_childnode DBMS_XMLDOM.DomNode;
v_rssEntryList DBMS_XMLDOM.DomNodeList;
v_rssItemFields DBMS_XMLDOM.DomNodeList;
v_attrNamedNodeMap DBMS_XMLDOM.DomNamedNodeMap;
v_childNodeText VARCHAR2(32767);

-- FEEDのフォーマット(RSS/RDF/ATOM)を判別するためのルート要素のタグ名
v_rootElementTagName VARCHAR2(10);
-- RSS/RDF/ATOMのversion
v_version VARCHAR2(10);

-- for UTL_HTTP
v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;

-- HTTP経由でRSS/ATOMソースを取り込むためのワーク
v_source VARCHAR2(1024);
v_tempSourceClob CLOB := NULL;

--
BEGIN
DBMS_OUTPUT.ENABLE(200000);
v_url := i_url;

-- FEEDを取得し、一時CLOBへ書き出す。(URL指定パースでの文字化け回避策)
v_req := UTL_HTTP.BEGIN_REQUEST(v_url);
UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1;');
UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
v_resp := UTL_HTTP.GET_RESPONSE(v_req);

DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
BEGIN
LOOP
UTL_HTTP.READ_LINE(v_resp, v_source, true);
v_source := v_source || UTL_TCP.CRLF;
DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(v_resp);
END;

v_myParser := DBMS_XMLPARSER.NEWPARSER();
DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);

v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
v_rootElementTagName :=
DBMS_XMLDOM.GETTAGNAME
(
DBMS_XMLDOM.GETDOCUMENTELEMENT(v_rssDoc)
);
v_version :=
DBMS_XMLDOM.GETVALUE
(
DBMS_XMLDOM.GETATTRIBUTENODE
(
DBMS_XMLDOM.GETDOCUMENTELEMENT(v_rssDoc),
C_ATTR_NAME_VERSION
)
);

DBMS_OUTPUT.PUT_LINE('URL:' || i_url);
CASE v_rootElementTagName
WHEN c_atom THEN
DBMS_OUTPUT.PUT_LINE('FEEDフォーマット:ATOM' || ' version = ' || v_version);
WHEN c_rss THEN
DBMS_OUTPUT.PUT_LINE('FEEDフォーマット:RSS ' || ' version = ' || v_version);
WHEN c_rdf THEN
DBMS_OUTPUT.PUT_LINE('FEEDフォーマット:RDF ' || ' version = ' || v_version);
ELSE
RAISE_APPLICATION_ERROR(-20000,'ありえねぇー');
END CASE;

DBMS_LOB.FREETEMPORARY(v_tempSourceClob);
DBMS_XMLPARSER.FREEPARSER(v_myParser);

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm());
IF v_tempSourceClob IS NOT NULL THEN
DBMS_LOB.FREETEMPORARY(v_tempSourceClob);
END IF;
DBMS_XMLPARSER.FREEPARSER(v_myParser);
RAISE;

END RSS_FEED_READER_TEST;



聞いている曲:

Gary NumanのDance。当時、JapanのメンバーであったMick Karn をフィーチャーしたアルバム。1981年リリース。Gentlemen Take PolaroidsをイメージさせるMick Karnのサックスとフレットレスベースが気持ちいい。 Gary Numanの声は好き嫌いがハッキリとでると思うけど。。。

Gary Numan - Dance Gary Numan - Dance - She's Got Claws
Gary Numan - Dance - She's Got Claws

| | | コメント (0) | トラックバック (0)

2006年10月 4日 (水)

Mac De PL/SQL RSS Reader #9

前回の続き。

feedフォーマットは、RSS 0.91, 0.92, 2.0と、RSS 0.9, 1.0 それに、ATOM 0.3, 1.0があるが、それらを区別するにはどうするのがいいのか?

各フォーマット共通の要素があり、属性として、フォーマット名とバージョンの文字列が取得できるようになっていれば話は簡単なのだが、そのような要素は存在しない。

ということで、やはり、素直に、各feedのルート要素名で区別するのが無難か? と思うようになる。。。

・<rss>ならば、
 Rich Site Summary(RSS 0.9x)とReally Simple Syndication(RSS 2.0)

・<rdf:RDF>ならば
 RDF Site Summary(RSS 0.9とRSS 1.0)

・<feed>ならば
 Atom Syndication Format (ちなみに、私が購読しているBlogには、version = 0.3 と 1.0のサイトがある)

さらに、細かな区別が必要であれば、各要素のversion属性値でより細かな区別を行う。

という手順を踏めば、なんとかなる、かな。。。


ちなみに、XSLT(PL/SQLだとDBMS_XSLPROCESSORパッケージを利用する)で変換する方法もあるが、今後のお楽しみのために残しておき、今回は、DBMS_XMLDOMパッケージ利用し、DOMプログラミングだけで作ろうと考えている。尚、一から作るのではなく、以前作成したRSS 0.91に対応したものを改造し、複数のfeedフォーマットに対応させる予定。



聞いている曲:DVDのほうがいいけどね。。

今年で、18周年を迎える、Blue Note Tokyo。送られてきた案内を見ると、、おや、6年降りの登場なんですね、NIACIN。
しかも、10/22の1日限り。慌てて、予約状況を見るも。。。。すでに満席。残念!!!

ということで、昔のアルバムでも。。。あれ〜、iTMSには、NIACINは無いのね! これまた残念といことで、Amazonにリンク張りま〜す。
BILLY SHEEHAN

 

| | | コメント (0) | トラックバック (0)

2006年10月 3日 (火)

Mac De PL/SQL RSS Reader #8

Mac De PL/SQL RSS Reader #7の続き

さて、SafariのFeed Reader機能だけで配信される記事を読んでいる場合には全く意識していないのだが、いざ、自作となると面倒だと感じるところがある。 
その一つは、やはり、feedフォーマットが複数ある点だろうか。しかもそのフォーマット毎に複数のバージョンが存在する。

二つ目は、日付、時刻のフォーマット。rfc822または、rfc3339のいずれか利用されている点だ。

各feedのspecを斜め読みし、記事の公開日に対応する要素について調べてみると、

RSS 0.91のpubDate要素はrfc822
 但し、pubDate要素は、option要素なので存在しない場合もある。

RSS 1.0dc:date要素は、rfc3339

RSS 0.92 and 2.0pubDate要素も、rfc822
 但し、pubDate要素は、option要素なので存在しない場合もある。
 また、pubDate要素を利用せず、dc:date要素で拡張している場合もあようで、形式は、rfc3339となる。

ATOMのpublished要素は、rfc3339。参照、rfc4287 3.3 Date Constructs


私が作ろうとしているRSS Feed Readerは、ブログタイトル、記事のタイトルとURL、それに公開日時だけという単純ものなのだが、いろいろと調べなければならない。。。
間違いなく忘れそうなので備忘録として残しておく。。調べるだけでも疲れる。。。。。。



2006/10/4 追記、ATOM version="0.3"を使っている場合には、published要素ではなく、issued要素が利用され、rfc3339準拠。
ほんとに面倒ね。


David Sanbornは、今年の暮れも Blue Note Tokyoに来るようですね。

聞いている曲:
宇多田ヒカル - First Love (Featuring David Sanborn) 宇多田ヒカル & David Sanborn - First Love - EP - First Love (Featuring David Sanborn)
宇多田ヒカル & David Sanborn - First Love - EP - First Love (Featuring David Sanborn)

David Sanborn - Closer David Sanborn - Closer - Enchantment
David Sanborn - Closer - Enchantment

| | | コメント (0) | トラックバック (0)

2006年9月28日 (木)

Mac De PL/SQL RSS Reader #7

さて、RSS ReaderをネタにDBMS_XMLDOMパッケージなどでも遊ぼうとしていたら、こんなの見つけた。



Oracle10g R1/R2の PL/SQL Package and Type ReferenceマニュアルによればDBMS_XMLDOM.HASATTRIBUTEファンクションの戻り値は、VARCHAR2となっている。(以下、マニュアルより抜粋)
HASATTRIBUTE Functions


DBMS_XMLDOM.HASATTRIBUTE(
elem IN DOMELEMENT,
name IN VARCHAR2)
RETURN VARCHAR2;

DBMS_XMLDOM.HASATTRIBUTE(
elem IN DOMELEMENT,
name IN VARCHAR2,
ns IN VARCHAR2)
RETURN VARCHAR2;


しかし、実際には、

SYS> spo xmldom_desc.log
SYS> desc dbms_xmldom

中略

SYS> spo off
SYS> !cat xmldom_desc.log | grep HASATTRIBUTE
FUNCTION HASATTRIBUTE RETURNS BOOLEAN
FUNCTION HASATTRIBUTE RETURNS BOOLEAN
FUNCTION HASATTRIBUTES RETURNS BOOLEAN
SYS>

ということで、DBMS_XMLDOM.HASATTRIBUTEファンクションの戻り値は、 BOOLEAN なので注意するべし。

さらに、DBMS_XMLDOM.HASATTRIBUTE()を使ってみると、Oracle10g 10.1.0.3.0, 10.2.0.2.0、それぞれで試してみたが、常に FALSE が返される。

ちなみに、DBMS_XMLDOM.GETATTRIBUTENODE()では、指定した属性ノードが正しく取り出せた。こちらが使えるので、とりあえず、なんとかなるか。。。

今後仕事で使う際には要注意なので、備忘録として記録しておく。(そのうちファンクションの問題共々、修正されるとは思うが。。。。)



聞いている曲:Japan - Gentoemen Take Polaroids - Taking Islands in Africa Japan - Gentlemen Take Polaroids - Taking Islands In Africa
Japan - Gentlemen Take Polaroids - Taking Islands In Africa

| | | コメント (0) | トラックバック (0)

2006年9月 6日 (水)

Mac De Oracle (PL/SQL De COMPRESS/UNCOMPRESS)

OTN JapanCodeTipsに、Mac De Oracle (PL/SQL De UNCOMPRESS)及び、Mac De Oracle (PL/SQL De COMPRESS)に掲載したコードとほぼ同じコードを投稿しました。

| | | コメント (0) | トラックバック (0)

2006年9月 3日 (日)

Mac de APEX! (formerly called HTML DB) #4

つづきです。
HTML DB 2.0からAPEX 2.2へのアップグレードは完了したので、PL/SQL で RSS FEED Readerで作ったコードを動かしてみる。

megawattさんの「メガ放談」のRSS 0.91を読み込み、記事のリンクをリストする。
Apex010

クリックすると、ブログが別ページで開かれる。
Apex011

HTML DB 2.0の時と変わりない。



聞いている曲:

Wikipedia - Mick Karn

Wikipedia(jp) - Japan

Wikipedia(en) - Japan


Mick Karn - Love's Glove(EP) Mick Karn - Love's Glove - EP
Mick Karn - Love's Glove - EP

Mick Karn - Of & About(EP) Mick Karn - Of & About - EP - All You Have
Mick Karn - Of & About - EP

| | | コメント (0) | トラックバック (0)

2006年9月 2日 (土)

Mac de APEX! (formerly called HTML DB) #3

続きです。APEXのインストール残すところ後僅か。

5)日本語環境のインストール

先にインストール(アップグレード)したのは、英語版なので、日本語環境のインストールを行う。手順は「Installation Guide]の「4.5 Installing Oracle Application Express in Other Languages」に記載されている。

注意)環境変数 NLS_LANG を AL32UTF8 にする必要がある。また、Terminalのエンコーディングも一時的に UTF8に変更しておく必要があるので注意すること。

まず、TerminalのエンコーディングをUTF8に変更(インストール時のみ一時的に変更するだけなので「デフォルトに設定」する必要ない)

変更前(SJIS)変更後(UTF8)
Encodeing_bEncodeing_a
pb17:/Volumes/WorkVol/temp/apex oracle$ echo $NLS_LANG
japanese_japan.JA16SJISTILDE
pb17:/Volumes/WorkVol/temp/apex oracle$ export NLS_LANG=japanese_japan.AL32UTF8
pb17:/Volumes/WorkVol/temp/apex oracle$ echo $NLS_LANG
japanese_japan.AL32UTF8

pb17:/Volumes/WorkVol/temp/apex oracle$ sqlplus /nolog

SQL*Plus: Release 10.1.0.3.0 - Production on 土 9月 2 11:00:24 2006

Copyright (c) 1982, 2004, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS> alter session set current_schema = flows_020200;

セッションが変更されました。

SYS>
SYS> !pwd
/Volumes/WorkVol/temp/apex

SYS> spo load_ja.log
SYS> @builder/ja/load_ja.sql
. ____ ____ ____ ____
. / \ | \ /\ / | /
.| || / / \ | | |
.| ||--- ---- | | |--
.| || \ / \ | | |
. \____/ | \/ \ \____ |____ \____
.
. HTML DB Hosted Development Service Installation.
.........................................................

PL/SQLプロシージャが正常に完了しました。

Install HTML DB applications

PL/SQLプロシージャが正常に完了しました。

APPLICATION 4420 - APEX - System Messages
Set Credentials...
Check Compatibility...
API Last Extended:20060224
Your Current Version:20060224
This import is compatible with version: 20050501
COMPATIBLE (You should be able to run this import without issues.)
中略
...PAGE .4009: ページ0(ゼロ)
...PAGE 1.4009: アプリケーション・ビルダー
...PAGE 2.4009: サポートするオブジェクト
...PAGE 3.4009: ブレッドクラム・エントリ
...PAGE 4.4009: マスター表の定義
...PAGE 5.4009: ディテール表を定義
中略
...done
Adjust instance settings

PL/SQLプロシージャが正常に完了しました。

SYS> spo off
現在はスプールしていません。
SYS>

6)JOB_QUEUE_PROCESSESの管理

前もって確認済みであるので、特に行うことはないのだが、値だけは確認しておこう。「Installation Guide」の「4.6 Managing JOB_QUEUE_PROCESSES」に記載されている。

SYS> show parameter job_queue_processes         

NAME TYPE          VALUE
----------------------- ------------------- --------------
job_queue_processes integer 20
SYS> exit
pb17:/Volumes/WorkVol/temp/apex oracle$

これで、APEX 2.2のへのアップグレードは終了である。早速、アクセスしてみる。URLは変更していない。また、ブラウザは、「Installation Guide」の「2.6 Browser Requirement」には記載されていない Safari 2.0.4を利用した。(今のところ特に不都合はないようだ。)
Apex003_1

さて、次回は、以前作成した、RSS FEED Readerが動作するか試してみる予定。




そういえば、放置状態にあった HTML DBで家系図アプリっていうネタを思い出した(完全に忘れていた。。。)のだが、親や叔父に聞いても、父方、母方でも3代程度、遡ることがやっと。(まぁ、家系図が代々引き継がれている訳でもないので、その程度なのでしょう。)でも自分の苗字由来や、氏名(ウジナ)は? など先日のテレビ番組の影響で、いろいろと調べたくなってきた。「明治8年の苗字必証称令」で、その時の雰囲気?で、適当に付けられてしまった可能性もあるので 新訂 寛政重修諸系譜 に記載されている家に繋がっているともいないとも、なんとも言えないが。。 

ということで、自分の苗字の原点を見つけてみようかな。。と、今読んでいる本。
 

それから、図書館で見つけた 新訂 寛政重修諸系譜。 これにも家譜が書かれていたので読んでみたり。

Wikipediaも参考に辿っていくと、確かに私の父方の苗字がある。ただ、その先は Wikipediaにも、 新訂 寛政重修諸系譜書かれていない。ま派生が多過ぎるということなのか。。。 そんなことで、いろいろ話していたら、妻は「平」氏系列という話もあり(私の方も含めて、全くの未確認なので聞き流してくださいね。おもしろがって調べてるだけですから。。)。  
え〜〜〜〜〜。 「源平」ですかい。。。。。。。

と、自分のルーツを辿るということで、思い出したのが(まぁ、自分のルーツを探す繋がりということで)、このテレビドラマ。
これが放送された影響で、クラスには、必ず一人、”クンタ”というニックネームを付けられるヤツがいた。(笑)

| | | コメント (0) | トラックバック (0)

2006年9月 1日 (金)

Mac de APEX! (formerly called HTML DB) #2

US Oracleが、OPEN APEXというサイトを立ち上げたようです。しかもこのサイト自体がAPEXで構築されているようです。

http://apex.oracle.com/
と入力するともうそこは、APEX world! マルチリンガル対応しているようで、日本からのアクセスも快適です。


では、前回の続き。

4)APEX 2.2インストール後の作業

4−1) PL/SQLパッケージのリコンパイル

移行後の作業も付属ドキュメントに丁寧に記載されているのでその通りに行えば問題ないだろう。「Installation Guide」の「4.1 Recompiling Invalid PL/SQL Packages and Restarting Processes」を行う。タイトルからもわかるように、statusが INVALID になった PL/SQLパッケージをリコンパイルするだけの作業だ。

pb17:/Volumes/WorkVol/temp/apex oracle$ sqlplus /nolog

SQL*Plus: Release 10.1.0.3.0 - Production on 金 9月 01 21:14:09 2006

Copyright (c) 1982, 2004, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS> spo utlrp.log
SYS> @?/rdbms/admin/utlrp.sql

TIMESTAMP
--------------------------------------------------------------------------------
COMP_TIMESTAMP UTLRP_BGN 2006-09-01 21:15:44


PL/SQLプロシージャが正常に完了しました。


TIMESTAMP
--------------------------------------------------------------------------------
COMP_TIMESTAMP UTLRP_END 2006-09-01 21:16:43


PL/SQLプロシージャが正常に完了しました。


PL/SQLプロシージャが正常に完了しました。
SYS> spo of
SYS> spo off


4−2)画像ファイルのコピー

「Installation Guilde」の「4.2 Copying the Images Directory」に記載されている作業である。簡単なので解説はしない。
尚、独自の環境変数 $ORACLE_HTMLDB_HOMEには、操作を簡単にするため、HTML DB(アップグレードしたので、APEX)のホームディレクトリを設定してある。

pb17:˜ oracle$ cd $ORACLE_HTMLDB_HOME/Apache/modplsql/conf
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ pwd
/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf

Oracle HTTP Serverを利用していので、marvel.conf で イメージディレクトリのマッピングを確認し、HTML DB 2.0用のディレクトリを念のためにリネームしておく。リーネム後に、APEX 2.2のイメージディレクトリをコピーすれば、OK。
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ cat marvel.conf | grep ¥/i¥/
Alias /i/ "/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel/images/"

pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ cd /Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel

pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ ls -l
total 1136
中略
drwxr-xr-x 998 oracle oinstall 33932 Aug 30 21:29 images
中略
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ mv images images_2_0
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ ll
total 1136
中略
drwxr-xr-x 998 oracle oinstall 33932 Aug 30 21:29 images_2_0
中略
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ cd /Volumes/WorkVol/temp/apex
pb17:/Volumes/WorkVol/temp/apex oracle$ cp -rf images $ORACLE_HTMLDB_HOME/marvel
pb17:/Volumes/WorkVol/temp/apex oracle$
pb17:/Volumes/WorkVol/temp/apex oracle$ cd $ORACLE_HTMLDB_HOME/marvel
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$ ll
total 1136
中略
drwxr-xr-x 876 oracle oinstall 29784 Seq 01 21:26 images
drwxr-xr-x 998 oracle oinstall 33932 Aug 39 21:29 images_2_0
中略
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel oracle$


次に、「Installation Guide」の「4.3 Configuring Oracle HTTP Server When Upgrading」、「4.3.2 Configuring Oracle HTTP Server 10g or Oracle Application Server 10g When Upgrading」を行う。Oracle HTTP Server 10g をインストールしてあるので、マニュアルに従い、marvel.confのPlsqlNLSLanguageを変更する。HTML DB 2.0までは、データベースキャラクタセットに合わせておけばよかったため、私の環境では、データベースキャラクタセットである、JA16SJISTILDE に設定していた。APEX2.2では、データベースキャラクタセットに関係なく、必ず、AL32UTF8 に設定する必要がある。

pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB oracle$ cd $ORACLE_HTMLDB_HOME/Apache/modplsql/conf
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ cp marvel.conf marvel.conf.20
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ vi marvel.conf
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ diff marvel.conf.20 marvel.conf
15c15
< PlsqlNLSLanguage JAPANESE_JAPAN.JA16SJISTILDE
---
> PlsqlNLSLanguage JAPANESE_JAPAN.AL32UTF8
pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$

Oracle HTTP Server 10gの起動するコマンドを、.bash_profile に alias 定義してあるので以下のようにして停止、起動している。
マニュアルには、Oracle HTTP Server 10gの起動、停止は、opmnctl を利用すると書かれているが、MacOSX版のCompanion CDからインストールした Oracle HTTP Server 10gには、opmnctl が存在しない(無理矢理インストールしているからか??)ので、Oracle HTTP Server 9.0.3のころと同じく、apachectl で起動している。この点に関しては、マニュアルに記載されている操作と、ここで行っている操作が異なっているので注意。

pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ stophtmldb
/Volumes/WorkVol/u01/app/oracle/product/10.1.0/tiger/../htmlDB/Apache/Apache/bin/apachectl start: httpd stopped

pb17:/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/Apache/modplsql/conf oracle$ starthtmldb
/Volumes/WorkVol/u01/app/oracle/product/10.1.0/tiger/../htmlDB/Apache/Apache/bin/apachectl start: httpd started


今日はここまで。次回は、APEXの日本語関連リソースをインストールする。


聞いている曲: 9月といえば。。。
Earth Wind & Fire : September

| | | コメント (0) | トラックバック (0)

2006年8月29日 (火)

Mac De Oracle (PL/SQL De COMPRESS)

解凍につづき、圧縮を試す。


まずは gzip するテキストファイルは前回準備しておいたものを利用する。このテキストファイルをcompressed_file.gzとして圧縮してみる。尚、UTL_COMPRESS.LZ_COMPRESS()では、圧縮レベルが1〜9あり、デフォルトは、6。最も圧縮率が高いのは、9。この例では、圧縮率=6(デフォルト)で圧縮する。

G5Server:˜ oracle$ ls -l
total 48096
中略
-rw-r--r-- 1 oracle oinstall 559 Aug 20 06:35 org.txt
中略
G5Server:˜ oracle$
G5Server:˜ oracle$ cat org.txt
操作上の注意
■ LZ* ファンクションによって戻された一時 LOB は、コール元が
DBMS_ LOB.FREETEMPORARY コールを使用して解放する必要があります。
■ LZ_COMPRESS* または LZ_UNCOMPRESS* に渡された BFILE は、
DBMS_LOB.FILEOPENでオープンする必要があります。
■ 特別な状況(入力がすでに圧縮されている場合)では、UTL_COMPRESS サブプログラムの
1 つで生成された出力が、入力と同じ大きさかまたは入力よりもわずかに大きくなる
ことがあります。
■ UTL_COMPRESS で圧縮したデータの出力は、1 つのファイル上の gzip(-n オプション を指定)/gunzip と
互換性があります。
G5Server:˜ oracle$

早速取りかかる。今回も Oracle SQL Developer 1.0 for MacOSXを利用する。

だ〜〜〜〜っと、前回のコードを少々修正して実行(無名PL/SQLブロックにしてあるが、ストアドプロシージャでもファンクションでも、パッケージで実装しても全く問題はない。尚、事前にディレクトリオブジェクトは作成してある。)
Macdecompress1_1

圧縮ファイル(compressed_file.gz)が正しく作成できたか確認する。

G5Server:˜ oracle$ ls -l
total 48112
中略
-rw-r--r-- 1 oracle oinstall 408 Aug 20 13:11 compressed_file.gz
-rw-r--r-- 1 oracle oinstall 559 Aug 20 06:35 org.txt
中略
G5Server:˜ oracle$
G5Server:˜ oracle$ gunzip -lt compressed_file.gz
compressed uncompressed ratio uncompressed_name
408 559 30.2% compressed_file
G5Server:˜ oracle$
G5Server:˜ oracle$ gunzip -c compressed_file.gz
操作上の注意
■ LZ* ファンクションによって戻された一時 LOB は、コール元が
DBMS_ LOB.FREETEMPORARY コールを使用して解放する必要があります。
■ LZ_COMPRESS* または LZ_UNCOMPRESS* に渡された BFILE は、
DBMS_LOB.FILEOPENでオープンする必要があります。
■ 特別な状況(入力がすでに圧縮されている場合)では、UTL_COMPRESS サブプログラムの
1 つで生成された出力が、入力と同じ大きさかまたは入力よりもわずかに大きくなる
ことがあります。
■ UTL_COMPRESS で圧縮したデータの出力は、1 つのファイル上の gzip(-n オプション を指定)/gunzip と
互換性があります。
G5Server:˜ oracle$

前述のように、gunzipで解凍してしまうのもよいのだが、ダブルクリックして解凍しちゃいましょう。すばらしいGUIがあるんですから。
Macdecompress2_1 Macdecompress3


以下に今回作成したコードを示す。

DECLARE
v_src_text_file BFILE;
v_compressed_blob BLOB;
v_directory_name VARCHAR2(30);
v_src_text_file_name VARCHAR2(510);
v_dest_file_name VARCHAR2(510);

PROCEDURE close_bfile
(
i_bfile IN OUT NOCOPY BFILE
)
IS
BEGIN
IF DBMS_LOB.ISOPEN(i_bfile) = 1 THEN
DBMS_LOB.CLOSE(i_bfile);
END IF;
END close_bfile;

PROCEDURE write_blob_to_file
(
i_directory_name IN VARCHAR2,
i_file_name IN VARCHAR2,
i_src_blob IN OUT NOCOPY BLOB
)
IS
v_file UTL_FILE.FILE_TYPE;
BEGIN
v_file := UTL_FILE.FOPEN(
UPPER(i_directory_name),
i_file_name,
'wb',
32767
);

-- ファイルへ書き出すBLOBはオープンされていることを前提とする
DECLARE
c_chunk_size CONSTANT PLS_INTEGER := 32767;

v_buffer RAW(32767);
v_amount PLS_INTEGER := c_chunk_size;
v_num_of_chunk PLS_INTEGER;
BEGIN
v_num_of_chunk := CEIL(DBMS_LOB.GETLENGTH(i_src_blob)/c_chunk_size);
FOR chunk# IN 1..v_num_of_chunk LOOP
DBMS_LOB.READ(
i_src_blob,
v_amount,
(c_chunk_size * (chunk# - 1)) + 1, /*offset*/
v_buffer
);
UTL_FILE.PUT_RAW(v_file, v_buffer, TRUE);
END LOOP;
END;

UTL_FILE.FCLOSE(v_file);
END write_blob_to_file;
--
BEGIN
-- ディレクトリ名の設定
v_directory_name := 'MYDIR';
-- テキストファイル名
v_src_text_file_name := 'org.txt';
-- 出力ファイル名
v_dest_file_name := 'compressed_file.gz';

DBMS_OUTPUT.PUT_LINE('========== compress start ==========');

-- テキストファイルをBFILEとして開く。
v_src_text_file
:= BFILENAME(UPPER(v_directory_name), v_src_text_file_name);
DBMS_LOB.OPEN(v_src_text_file);

-- BFILEを圧縮し、一時BLOBへ (圧縮品質はデフォルトで圧縮する)
v_compressed_blob := UTL_COMPRESS.LZ_COMPRESS(v_src_text_file);

-- BLOB(圧縮済み)を書き出す
write_blob_to_file(
UPPER(v_directory_name),
v_dest_file_name,
v_compressed_blob
);

-- 一時LOBの解放及び、BFILEのクローズ
DBMS_LOB.FREETEMPORARY(v_compressed_blob);
close_bfile(v_src_text_file);

DBMS_OUTPUT.PUT_LINE('========== compress end ==========');

EXCEPTION
WHEN OTHERS THEN
-- 一時LOBの解放及び、BFILEのクローズ
DBMS_LOB.FREETEMPORARY(v_compressed_blob);
close_bfile(v_src_text_file);
RAISE;
END;
/

OTN JapanCodeTipsに公開してあります。
聞いている曲:
KraftWerk - Minimum - Maximum (live) Kraftwerk - Minimum - Maximum (Live)
Kraftwerk - Minimum - Maximum (Live)

| | | コメント (2) | トラックバック (0)

2006年8月28日 (月)

Mac De Oracle (PL/SQL De UNCOMPRESS)

Oracle10g R1から UTL_COMPRESSパッケージという、PL/SQLでデータの圧縮/解凍を行う為のパッケージが提供されているのをご存知だろうか?

恐らく、大きなデータを圧縮してBLOBとしてデータベースに格納し、取り出す時に解凍するというのが一般的な利用方法なのではないかと思うのだが、Mac De Oracle としては、そのまま試すのでは面白くない!
ということで、UTL_FILEパッケージを利用し、ディレクトリオブジェクトにある圧縮ファイルを解凍したり、テキストファイルを圧縮してみることにする。さて、うまくいきますかどうか。。。
尚、BLOBをディレクトリオブジェクト以下に書き出す方法は、PL/SQL De Phython Challengeでも利用した方法である。以前はコードを掲載していなかったが、今回は、それに近いコードも載せることにする。
(UTL_FILEパッケージでディレクトリオブジェクト以下にあるファイルを読み書きするには、Oracle9i R2以降が必要である。)

マニュアルにも書いてあるが、UTL_COMPRESSの圧縮、解凍は、gzip/gunzipと互換があるということなので素直に信じて試してみる!

まずは gzip するテキストファイルの準備から。
以下のようなテキストファイルを用意する。テキストファイルの内容は、「Oracle10g R1 PL/SQL タイプ及びパッケージリファレンス」のUTL_COMPRESSパッケージの解説を一部を引用することにした。(テキストファイルの文字コードは、SJIS、データベースキャラクタセットは、JA16SJISTILDEである。)

操作上の注意 
■ LZ* ファンクションによって戻された一時 LOB は、コール元が
DBMS_ LOB.FREETEMPORARY コールを使用して解放する必要があります。
■ LZ_COMPRESS* または LZ_UNCOMPRESS* に渡された BFILE は、
DBMS_LOB.FILEOPENでオープンする必要があります。
■ 特別な状況(入力がすでに圧縮されている場合)では、UTL_COMPRESS サブプログラムの
1 つで生成された出力が、入力と同じ大きさかまたは入力よりもわずかに大きくなる
ことがあります。
■ UTL_COMPRESS で圧縮したデータの出力は、1 つのファイル上の gzip(-n オプション を指定)/gunzip と
互換性があります。

Last login: Mon Aug 19 16:42:12 on ttyp1
Welcome to Darwin!
G5Server:˜ oracle$ gzip -cvn org.txt > org.gz
org.txt: 30.2%
G5Server:˜ oracle$ gzip -l org.gz
compressed uncompressed ratio uncompressed_name
408 559 30.2% org
G5Server:˜ oracle$ ls -l
total 48096
中略
-rw-r--r-- 1 oracle oinstall 408 Aug 20 06:35 org.gz
-rw-r--r-- 1 oracle oinstall 559 Aug 20 06:35 org.txt
中略
G5Server:˜ oracle$

準備は整ったので早速取りかかる。今回も Oracle SQL Developer 1.0 for MacOSXを利用する。


だ〜〜〜〜っと、コード書をきなぐり、即、実行!(無名PL/SQLブロックにしてあるが、ストアドプロシージャでもファンクションでも、パッケージで実装しても全く問題はない。尚、事前にディレクトリオブジェクトは作成してあり、無名PL/SQLブロックを実行するユーザへ読み込み権限と書き込み権限を付与してある。)
Macdecompress1


圧縮前のテキストファイル(org.txt)と、org.gzをUTL_COMPRESS.LZ_UNCOMPRESS()で解凍したファイル(uncompressed_file.txt)が同じ内容であることを確認する。(diffを取って見せるのがいいのだが、それだけだとMacOSXの奇麗なGUIが見れないので、ファイルを開いて確認する。)
Macdecompress2


以下に今回作成したコードを示す。

DECLARE
v_src_gzipped_file BFILE;
v_uncompressed_blob BLOB;
v_directory_name VARCHAR2(30);
v_src_gzipped_file_name VARCHAR2(510);
v_dest_file_name VARCHAR2(510);

PROCEDURE close_bfile
(
i_bfile IN OUT NOCOPY BFILE
)
IS
BEGIN
IF DBMS_LOB.ISOPEN(i_bfile) = 1 THEN
DBMS_LOB.CLOSE(i_bfile);
END IF;
END close_bfile;

PROCEDURE write_blob_to_file
(
i_directory_name IN VARCHAR2,
i_file_name IN VARCHAR2,
i_src_blob IN OUT NOCOPY BLOB
)
IS
v_file UTL_FILE.FILE_TYPE;
BEGIN
v_file := UTL_FILE.FOPEN(
UPPER(i_directory_name),
i_file_name,
'wb',
32767
);

-- ファイルへ書き出すBLOBはオープンされていることを前提とする
DECLARE
c_chunk_size CONSTANT PLS_INTEGER := 32767;

v_buffer RAW(32767);
v_amount PLS_INTEGER := c_chunk_size;
v_num_of_chunk PLS_INTEGER;
BEGIN
v_num_of_chunk := CEIL(DBMS_LOB.GETLENGTH(i_src_blob)/c_chunk_size);
FOR chunk# IN 1..v_num_of_chunk LOOP
DBMS_LOB.READ(
i_src_blob,
v_amount,
(c_chunk_size * (chunk# - 1)) + 1, /*offet*/
v_buffer
);
UTL_FILE.PUT_RAW(v_file, v_buffer, TRUE);
END LOOP;
END;

UTL_FILE.FCLOSE(v_file);
END write_blob_to_file;
--
BEGIN
-- ディレクトリ名の設定
v_directory_name := 'MYDIR';
-- 圧縮ファイル名
v_src_gzipped_file_name := 'org.gz';
-- 解凍ファイル名
v_dest_file_name := 'uncompressed_file.txt';

DBMS_OUTPUT.PUT_LINE('========== uncompress start ==========');

-- 圧縮ファイルをBFILEとして取り込む
v_src_gzipped_file
:= BFILENAME(UPPER(v_directory_name), v_src_gzipped_file_name);
DBMS_LOB.OPEN(v_src_gzipped_file);

-- BFILEを解凍し一時BLOBへ
v_uncompressed_blob := UTL_COMPRESS.LZ_UNCOMPRESS(v_src_gzipped_file);

-- BLOBを書き出す
write_blob_to_file(
UPPER(v_directory_name),
v_dest_file_name,
v_uncompressed_blob
);

-- 一時LOBの解放及び、BFILEのクローズ
DBMS_LOB.FREETEMPORARY(v_uncompressed_blob);
close_bfile(v_src_gzipped_file);

DBMS_OUTPUT.PUT_LINE('========== uncompress end ==========');

EXCEPTION
WHEN OTHERS THEN
-- 一時LOBの解放及び、BFILEのクローズ
DBMS_LOB.FREETEMPORARY(v_uncompressed_blob);
close_bfile(v_src_gzipped_file);
RAISE;
END;
/

さて、次回は、テキストファイルを読み込み圧縮してみることにする。

OTN JapanCodeTipsに公開してあります。

聞いている曲:
Marcus Miller - Silver Rain
Marcus Miller - Silver Rain - Silver Rain (Featuring Eric Clapton)

| | | コメント (0) | トラックバック (0)

2006年8月14日 (月)

Mac De PL/SQL RSS Reader #6

つづきです。
PL/SQLで日本語対応RSS Readerが作れる事が確認できたので HTMLDB 2.0 に組み込み確認する。

Oracle HTMLDB 2.0で PL/SQL RSS Reader作成中のスナップショットをQuickTimeムービーにしました。
再生には、Apple QuickTimeプラグインが必要です。


サウンドトラックの無い、ファイルサイズ:2.3MB版のQuickTimeムービーはこちらから。



Apple iLifeのGarage Bandでサウンドトラックを付けたバージョン。ファイルサイズ:38MB版QuickTimeムービーはこちら。


聞いている曲:
David Bowie - TVC 15 David Bowie - Station to Station - TVC 15

| | | コメント (0) | トラックバック (0)

2006年8月13日 (日)

Mac De PL/SQL RSS Reader #5

前回のつづきです。
当初のからmegawattさんのRSS 0.91フォーマットを対象にしているためその他のRSSフォーマットやATOMには対応していない。元ネタのようにXSLTスタイルシートを利用するのも良いだろう。
今回は、「元ネタの通りの方法で、日本語が文字化け無く処理できるのだろうか?」 という点を確認するということが目的であるため複数のRSSフォーマットに対応するつもりは無い。そのウチ試す予定だが。

ということで、最後に、今までほったらかしていた Oracle HTMLDB 2.0 を利用して RSS Reader を組み込んでみることにした。
Oracle HTMLDB 2.0については以前の記事を参考にして貰いたい。



HTMLDBに組み込んだコードはこれ。

DECLARE
TYPE rss_type IS RECORD
(
title VARCHAR2(2000),
link VARCHAR2(200)
);
v_rss_item rss_type;
v_rss_empty_item rss_type;

v_url VARCHAR2(32767)
:= 'http://megawatt.blogdns.net/blog/rdf91_xml';

v_myParser DBMS_XMLPARSER.Parser;
v_rssDoc DBMS_XMLDOM.DomDocument;
v_itemNode DBMS_XMLDOM.DomNode;
v_childnode DBMS_XMLDOM.DomNode;
v_rssEntryList DBMS_XMLDOM.DomNodeList;
v_rssItemFields DBMS_XMLDOM.DomNodeList;
v_childNodeText VARCHAR2(32767);

v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;

v_source VARCHAR2(1024);
v_tempSourceClob CLOB;
v_tempXMLTYPE XMLTYPE;


PROCEDURE report_rss_item(p_rss_item IN rss_type)
IS
BEGIN
HTP.TABLEROWOPEN();
HTP.TABLEDATA(
'<a href="'
|| p_rss_item.link
|| '" style="color:#FF0000" target="_blank">'
|| p_rss_item.title
|| '</a>'
);
HTP.TABLEROWCLOSE();

EXCEPTION
WHEN OTHERS THEN
HTP.PRINT('Error occured in report_rss_item internal procedure');
RAISE;
END report_rss_item;

--****************************************************************************
-- Main procedure
--****************************************************************************
BEGIN
v_req := UTL_HTTP.BEGIN_REQUEST(v_url);
UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1');
UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
v_resp := UTL_HTTP.GET_RESPONSE(v_req);

DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
BEGIN
LOOP
UTL_HTTP.READ_LINE(v_resp, v_source, true);

-- 改行コード強制的に付加。
-- xml_println()でDBMS_OUTPUT.PUT_LINE()が、最大255文字までしか
-- 表示できないことへの一時的な対策。(試しているサイトについてはこれで
-- 回避できているので今のところはこのままで。)
v_source := v_source || UTL_TCP.CRLF;
DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);

END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(v_resp);
END;

v_myParser := DBMS_XMLPARSER.NEWPARSER();
DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);
v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
v_tempXMLType := DBMS_XMLDOM.GETXMLTYPE(v_rssDoc);
v_rssEntryList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(v_rssDoc, 'item');
v_rss_item := v_rss_empty_item;

HTP.TABLEOPEN();
HTP.TABLEROWOPEN();
HTP.TABLEHEADER(
cvalue => v_tempXMLType.EXTRACT('/rss/channel/title/text()').GETSTRINGVAL
|| ' - most recent '
|| DBMS_XMLDOM.GETLENGTH(v_rssEntryList)
|| ' articles:',
cattributes => 'style="font-size:18px"',
calign => 'align="left"'
);
HTP.TABLEROWCLOSE();

FOR i IN 1..DBMS_XMLDOM.GETLENGTH(v_rssEntryList) LOOP
v_itemNode := DBMS_XMLDOM.ITEM(v_rssEntryList, i-1);
v_rssItemFields := DBMS_XMLDOM.GETCHILDNODES(v_itemNode);

FOR j IN 1..DBMS_XMLDOM.GETLENGTH(v_rssItemFields) LOOP
v_childnode := DBMS_XMLDOM.ITEM(v_rssItemFields, j-1);
v_childNodeText := DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.GETFIRSTCHILD(v_childnode)
);

CASE DBMS_XMLDOM.GETNODENAME(v_childnode)
WHEN 'title' THEN
v_rss_item.title := v_childNodeText;

WHEN 'link' THEN
v_rss_item.link := v_childNodeText;

ELSE
NULL;
END CASE;
END LOOP;
report_rss_item(v_rss_item);
END LOOP;

HTP.TABLECLOSE();
DBMS_LOB.FREETEMPORARY(v_tempSourceClob);
DBMS_XMLPARSER.FREEPARSER(v_myParser);

EXCEPTION
WHEN OTHERS THEN
HTP.PRINT(
'Main Procedure:'
|| sqlerrm()
);
RAISE;
END;



前回のストアドプロシージャのロジックとの違いはあまり無いが、無名PL/SQLブロックとして組み込むことと、DBMS_OUTPUTパッケージを利用している部分を、HTPパッケージを利用するように変更しなければならない。(やっつけ仕事的なコードになってしまったなが、文字化け回避の方法が見えればOKなので他には目をつぶってくださいませ。。。)

尚、
HTPパッケージについては、PL/SQL パッケージ・プロシージャおよびタイプ・リファレンス
HTMLDB 2.0についてはOracle HTMLDB 2.0(英語)や、HTMLDB 2.0のオンラインヘルプなどが参考になるだろう。
#今回は、オンラインヘルプしか利用ませんでした。そんな程度でも作れるくらい簡単です。。。


次回は、HTMLDB 2.0 に前述の無名PL/SQLブロックを組み込んで試すことにする。




これから見ようとしているDVD:
随分前、こんな記事を書いたが、なんと ザ・インターネット2がリリースされていた。
邦題:インターネットの続編?のようなのだが。。。。不覚にも全くしらなかった。でも主演はサンドラブロックではなさそうだな。。。そういえば、ザ・インターネットが公開されたのは1996年。10年も経ったのか・・・と。


ところで、インターネット2っていうと、次世代インターネットを指していたりする。


##############################
2006/08/13
ザ・インターネット2見ましたが、ぜんぜん面白くない。ザ・インターネットの続編だなんて、とんでもない無いですね。
ザ・インターネットの延長線上にあると思ってみてしまうとがっかりするので、完全に別ものと思って見てくださいね。

| | | コメント (0) | トラックバック (0)

2006年8月 9日 (水)

Mac De PL/SQL RSS Reader #4

前回のつづきです。
前回までで文字化けを回避できることが確認できたので、本題RSSリーダーの元ネタで試してみることにする。
尚、オリジナルになるべく近い形にしてある。(対象は、megawattさんRSS 0.91フォーマット)

まずは、結果から。(いつものように、Oracle SQL Developer 1.0 for MacOSXを使ってます)
Sqldevok



データベースOracle10g R1 grasscatfishに接続中です。
begin...
====================
メガ放談 - most recent 15 articles:
====================
TITLE: ギザ耳は地域猫の証し
URL : http://megawatt.blogdns.net/blog/254
—————————————-

TITLE: 亀田家親子夏休み日記
URL : http://megawatt.blogdns.net/blog/253
—————————————-

TITLE: 社会保険事務所に行ってきた
URL : http://megawatt.blogdns.net/blog/252
—————————————-

TITLE: 重なるときは重なる Part2
URL : http://megawatt.blogdns.net/blog/251
—————————————-

TITLE: 重なるときは重なる
URL : http://megawatt.blogdns.net/blog/250
—————————————-

TITLE: うしろすがた
URL : http://megawatt.blogdns.net/blog/249
—————————————-

TITLE: 猫のための写真講座/2006年度前期・4
URL : http://megawatt.blogdns.net/blog/248
—————————————-

TITLE: ワイン100mlが80キロカロリー!!
URL : http://megawatt.blogdns.net/blog/247
—————————————-

TITLE: はっちゃんではないはっちゃん
URL : http://megawatt.blogdns.net/blog/246
—————————————-

TITLE: ちょっと、走ってる
URL : http://megawatt.blogdns.net/blog/245
—————————————-

TITLE: 命名、じみへん
URL : http://megawatt.blogdns.net/blog/244
—————————————-

TITLE: 猫まんが at門前仲町
URL : http://megawatt.blogdns.net/blog/243
—————————————-

TITLE: 猫を拾った日
URL : http://megawatt.blogdns.net/blog/242
—————————————-

TITLE: お飲物なににしますか?
URL : http://megawatt.blogdns.net/blog/241
—————————————-

TITLE: 門前仲町・深川不動堂
URL : http://megawatt.blogdns.net/blog/240
—————————————-

...End
プロセス終了。
データベースOracle10g R1 glasscatfishから切断中です。

以下、プロシージャのソース。
比較し易いように、オリジナルのソースになるべく近い形式にしてある。

CREATE OR REPLACE PROCEDURE RSS_FEED_READER
(
i_url IN VARCHAR2
)
AS
--============ TYPEs/VARIABLEs ===============================================
TYPE rss_type IS RECORD
(
title VARCHAR2(2000),
link VARCHAR2(200),
pubDate VARCHAR2(200),
category VARCHAR2(500),
description VARCHAR2(4000)
);
v_rss_item rss_type;
v_rss_empty_item rss_type; -- v_rss_item初期化用

v_url VARCHAR2(32767);

-- XML PARSER
v_myParser DBMS_XMLPARSER.Parser;

-- RSSまたは、ATOMフォーマットのDOM
v_rssDoc DBMS_XMLDOM.DomDocument;

v_itemNode DBMS_XMLDOM.DomNode;
v_childnode DBMS_XMLDOM.DomNode;
v_rssEntryList DBMS_XMLDOM.DomNodeList;
v_rssItemFields DBMS_XMLDOM.DomNodeList;
v_childNodeText VARCHAR2(32767);

-- for UTL_HTTP
v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;
-- HTTP経由でRSS/ATOMソースを取り込むためのワーク
v_source VARCHAR2(1024);
v_tempSourceClob CLOB;

-- RSS/ATOMをXMLTYPEで扱うためのワーク
v_tempXMLTYPE XMLTYPE;

--========== Internal PROCEDUREs/FUNCTIONs ===================================
--
--****************************************************************************
--* VARCHAR2の文字列を最大で255文字または、改行コード毎にプリントする内部プロシージャ
--*
--* NOTE:
--* Oracle10g R2では、DBMS_OUTPUT.PUT_LINEプロシージャの255文字制限がないため、
--* 255文字毎に分割出力する必要はなくなる
--****************************************************************************
PROCEDURE println
(
i_text IN VARCHAR2
)
IS
v_src VARCHAR2(32767);
v_tempStr VARCHAR2(32767);
BEGIN
v_src := i_text;
IF INSTR(v_src, UTL_TCP.CRLF) <> 0 THEN
v_tempStr := SUBSTR(v_src, 1, INSTR(v_src, UTL_TCP.CRLF)-1);
v_src := SUBSTR(v_src, INSTR(v_src, UTL_TCP.CRLF)+LENGTH(UTL_TCP.CRLF));
ELSE
v_tempStr := v_src;
END IF;

WHILE v_tempStr IS NOT NULL LOOP
IF LENGTH(v_tempStr)> 255 THEN
FOR i IN 1..(TRUNC(LENGTH(v_tempStr)/255)+1) LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(v_tempStr,1+ 255*(i-1),255));
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE(v_tempStr);
END IF;

IF INSTR(v_src, UTL_TCP.CRLF) <> 0 THEN
v_tempStr := SUBSTR(v_src, 1, INSTR(v_src, UTL_TCP.CRLF)-1);
v_src := SUBSTR(v_src, INSTR(v_src, UTL_TCP.CRLF)+LENGTH(UTL_TCP.CRLF));
ELSE
EXIT;
END IF;
END LOOP;

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error Occured : println(VARCHAR2) internal procedure.');
DBMS_OUTPUT.PUT_LINE(sqlerrm());
RAISE;
END println;


PROCEDURE report_rss_item(p_rss_item IN rss_type)
IS
BEGIN
println('TITLE: '||p_rss_item.title);
println('URL : '||p_rss_item.link);

IF p_rss_item.pubDate IS NOT NULL THEN
println('Published on: '||p_rss_item.pubDate);
END IF;

IF p_rss_item.category IS NOT NULL THEN
println('Category: '||p_rss_item.category);
END IF;

IF p_rss_item.description IS NOT NULL THEN
println('==');
println('Summary: '||p_rss_item.description);
END IF;

println('—————————————-');
println(' ');

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error occured : report_rss_item internal procedure');
DBMS_OUTPUT.PUT_LINE(sqlerrm());
RAISE;
END report_rss_item;

--****************************************************************************
--* Main procedure
--****************************************************************************
BEGIN
DBMS_OUTPUT.ENABLE(200000);
println('begin...');

v_url := i_url;
IF v_url IS NULL THEN
RAISE_APPLICATION_ERROR(-20000, 'URLを指定してください。');
END IF;

v_req := UTL_HTTP.BEGIN_REQUEST(v_url);
UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1');
UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
v_resp := UTL_HTTP.GET_RESPONSE(v_req);

-- RSSソースを一時CLOBへ書き出す
DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
BEGIN
LOOP
UTL_HTTP.READ_LINE(v_resp, v_source, true);

-- 改行コード強制的に付加。
-- xml_println()でDBMS_OUTPUT.PUT_LINE()が、最大255文字までしか
-- 表示できないことへの一時的な対策。(試しているサイトについてはこれで
-- 回避できているので今のところはこのままで。)
v_source := v_source || UTL_TCP.CRLF;
DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(v_resp);
END;

println('====================');
v_myParser := DBMS_XMLPARSER.NEWPARSER();
DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);
v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
v_tempXMLType := DBMS_XMLDOM.GETXMLTYPE(v_rssDoc);
v_rssEntryList := DBMS_XMLDOM.GETELEMENTSBYTAGNAME(v_rssDoc, 'item');
v_rss_item := v_rss_empty_item;

println
(
v_tempXMLType.EXTRACT('/rss/channel/title/text()').GETSTRINGVAL
|| ' - most recent '
|| DBMS_XMLDOM.GETLENGTH(v_rssEntryList)
|| ' articles:'
);
println('====================');

FOR i IN 1..DBMS_XMLDOM.GETLENGTH(v_rssEntryList) LOOP
v_itemNode := DBMS_XMLDOM.ITEM(v_rssEntryList, i-1);
v_rssItemFields := DBMS_XMLDOM.GETCHILDNODES(v_itemNode);

FOR j IN 1..DBMS_XMLDOM.GETLENGTH(v_rssItemFields) LOOP
v_childnode := DBMS_XMLDOM.ITEM(v_rssItemFields, j-1);
v_childNodeText := DBMS_XMLDOM.GETNODEVALUE(
DBMS_XMLDOM.GETFIRSTCHILD(v_childnode)
);

CASE DBMS_XMLDOM.GETNODENAME(v_childnode)
WHEN 'title' THEN
v_rss_item.title := v_childNodeText;

WHEN 'link' THEN
v_rss_item.link := v_childNodeText;

WHEN 'published' THEN
v_rss_item.pubDate := v_childNodeText;

WHEN 'category' THEN
v_rss_item.category := v_childNodeText;

WHEN 'description' THEN
v_rss_item.description := v_childNodeText;

ELSE
NULL;
END CASE;
END LOOP;

report_rss_item(v_rss_item);
END LOOP;

DBMS_LOB.FREETEMPORARY(v_tempSourceClob);
DBMS_XMLPARSER.FREEPARSER(v_myParser);

println('...End');

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(
'Main Procedure:'
|| sqlerrm()
);
RAISE;

END RSS_FEED_READER;


今聞いている曲:
これらの曲がリリースされた時期から随分経っているが、種々の問題は残されたままのような気がする。と考えつつ。。。

ALCATRAZZ -- Hiroshima mon amour

Human Leage - The LebanonThe Human League - Hysteria - The Lebanon
The Human League - Hysteria - The Lebanon

| | | コメント (0) | トラックバック (0)

2006年8月 8日 (火)

Mac De PL/SQL RSS Reader #3

前回のつづきです。
さて、文字化けを回避する方法はないかと考えてみた。
URL指定で、Oracle10g R1のDBMS_XMLPARSERパッケージのパーサーでパースする場合、裏で行われて言うHTTPで正しくエンコーディングされていないのが原因でだろうと考え、試しにUTL_HTTPパッケージを使いRSSソースを取得してみることにした。

尚、DBMS_XMLPARDERパッケージ、DBMS_XMLDOMパッケージ、UTL_HTTPパッケージ、UTL_TCPパッケージの詳細については、「PL/SQL パッケージ・プロシージャおよびタイプ・リファレンス」を参照のこと。

まずは、結果から。
以下のように、HTTPで取得したRSSを表示すると文字化けは発生しなかった。(思った通り!)

データベースOracle10g R1 glasscatfishに接続中です。<br>
begin...
====================
DBMS_XMLPARSERバージョン:10.1.0.2.0
DBMS_XMLDOMバージョン   :

<rss version="0.91">
  <channel>
    <title>メガ放談</title>
    <link>http://megawatt.blogdns.net/blog</link>
    <description>A Weblog Product for Zope</description>
    <item>
      <title>亀田家親子夏休み日記</title>
      <link>http://megawatt.blogdns.net/blog/253</link>
    </item>
    <item>
      <title>社会保険事務所に行ってきた</title>
      <link>http://megawatt.blogdns.net/blog/252</link>
    </item>
    <item>
      <title>重なるときは重なる Part2</title>
      <link>http://megawatt.blogdns.net/blog/251</link>
    </item>
    <item>
      <title>重なるときは重なる</title>
      <link>http://megawatt.blogdns.net/blog/250</link>
    </item>
    <item>
      <title>うしろすがた</title>
      <link>http://megawatt.blogdns.net/blog/249</link>
    </item>
    <item>
      <title>猫のための写真講座/2006年度前期・4</title>
      <link>http://megawatt.blogdns.net/blog/248</link>
    </item>
    <item>
      <title>ワイン100mlが80キロカロリー!!</title>
      <link>http://megawatt.blogdns.net/blog/247</link>
    </item>
    <item>
      <title>はっちゃんではないはっちゃん</title>
      <link>http://megawatt.blogdns.net/blog/246</link>
    </item>
    <item>
      <title>ちょっと、走ってる</title>
      <link>http://megawatt.blogdns.net/blog/245</link>
    </item>
    <item>
      <title>命名、じみへん</title>
      <link>http://megawatt.blogdns.net/blog/244</link>
    </item>
    <item>
      <title>猫まんが at門前仲町</title>
      <link>http://megawatt.blogdns.net/blog/243</link>
    </item>
    <item>
      <title>猫を拾った日</title>
      <link>http://megawatt.blogdns.net/blog/242</link>
    </item>
    <item>
      <title>お飲物なににしますか?</title>
      <link>http://megawatt.blogdns.net/blog/241</link>
    </item>
    <item>
      <title>門前仲町・深川不動堂</title>
      <link>http://megawatt.blogdns.net/blog/240</link>
    </item>
    <item>
      <title>う〜ん、まずい。</title>
      <link>http://megawatt.blogdns.net/blog/239</link>
    </item>
    <item>
      <title>短歌熱</title>
      <link>http://megawatt.blogdns.net/blog/238</link>
    </item>
  </channel>
</rss>
====================

...End
プロセス終了。<br>
データベースOracle10g R1 glasscatfishから切断中です。<br>

以下、変更後のプロシージャのソースを示す。
変更点は、
DBMS_XMLPARSERパッケージのパーサーで、URL直指定でパースするのではなく、HTTP経由でCLOBにRSSソースを取り込み、CLOBをパースするようにした。
XMLをプリントする内部プロシージャも変更し、println(CLOB)からxml_println(XMLTYPE)へ変更した。(XMLTYPEにしておいたほうがなにかと便利なので。尚、元ネタは、ここ) 

CREATE OR REPLACE PROCEDURE RSS_FEED_READER
(
  i_url IN VARCHAR2
)
AS
  --============ TYPEs/VARIABLEs ===============================================
  TYPE rss_type IS RECORD
  (
    title       VARCHAR2(2000),
    link        VARCHAR2(200),
    pubDate     VARCHAR2(200),
    category    VARCHAR2(500),
    description VARCHAR2(4000)
  );
  v_rss_item        rss_type;
  v_rss_empty_item  rss_type; -- v_rss_item初期化用

  v_url             VARCHAR2(32767);

 
    -- XML PARSER
  v_myParser        DBMS_XMLPARSER.Parser;

  -- RSSまたは、ATOMフォーマットのDOM
  v_rssDoc          DBMS_XMLDOM.DomDocument; 

    -- for UTL_HTTP
  v_req             UTL_HTTP.REQ;
  v_resp            UTL_HTTP.RESP;

  -- HTTP経由でRSS/ATOMソースを取り込むためのワーク
  v_source          VARCHAR2(1024);
  v_tempSourceClob  CLOB;
         
   --========== Internal PROCEDUREs/FUNCTIONs ===================================
  --
  --****************************************************************************
  --* VARCHAR2の文字列を最大で255文字または、改行コード毎にプリントする内部プロシージャ
  --*
  --* NOTE:
  --* Oracle10g R2では、DBMS_OUTPUT.PUT_LINEプロシージャの255文字制限が緩和され32767文字まで出力できるようになるためがないため
  --* 255文字毎に分割出力する必要はなくなるなぁ。
  --****************************************************************************
  PROCEDURE println
  (
    i_text IN VARCHAR2
  )
  IS
    v_src     VARCHAR2(32767);
    v_tempStr VARCHAR2(32767);
  BEGIN
    v_src := i_text;
    IF INSTR(v_src, UTL_TCP.CRLF) <> 0 THEN
      v_tempStr := SUBSTR(v_src, 1, INSTR(v_src, UTL_TCP.CRLF)-1);
      v_src := SUBSTR(v_src, INSTR(v_src, UTL_TCP.CRLF)+LENGTH(UTL_TCP.CRLF));
    ELSE
      v_tempStr := v_src;
    END IF;
   
    WHILE v_tempStr IS NOT NULL LOOP
      IF LENGTH(v_tempStr)> 255 THEN
        FOR i IN 1..(TRUNC(LENGTH(v_tempStr)/255)+1) LOOP
          DBMS_OUTPUT.PUT_LINE(SUBSTR(v_tempStr,1+ 255*(i-1),255));
        END LOOP;
      ELSE
        DBMS_OUTPUT.PUT_LINE(v_tempStr);
      END IF;

      IF INSTR(v_src, UTL_TCP.CRLF) <> 0 THEN
        v_tempStr := SUBSTR(v_src, 1, INSTR(v_src, UTL_TCP.CRLF)-1);
        v_src := SUBSTR(v_src, INSTR(v_src, UTL_TCP.CRLF)+LENGTH(UTL_TCP.CRLF));
      ELSE
        EXIT;
      END IF;
    END LOOP;
   
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE('Error Occured : println(VARCHAR2) internal procedure.');
      DBMS_OUTPUT.PUT_LINE(sqlerrm());
      RAISE;
  END println;

  --****************************************************************************
  --* XMLTYPEに格納されたXMLソースをプリントする。
  --* (Oracle10g R1だと、255文字毎の分割プリントは必要かも。。。未実装)
  --*
  --* NOTE:
  --* Oracle10g R2では、DBMS_OUTPUT.PUT_LINEプロシージャの255文字制限がないため、
  --* 255文字毎に分割出力する必要はなくなる32767文字毎の分割出力でもOK.
  --****************************************************************************
  PROCEDURE xml_println
  (
    i_text IN XMLTYPE
  )
  IS
    v_str LONG;
  BEGIN
    v_str := i_text.EXTRACT('/*').GETSTRINGVAL();
    WHILE(v_str IS NOT NULL) LOOP
      DBMS_OUTPUT.PUB_LINE(SUBSTR(v_str, 1, INSTR(v_str, CHR(10)) - 1));
      v_str := SUBSTR(v_str, INSTR(v_str, CHR(10)) + 1);
    END LOOP;

  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE('Error Occured : xml_println(XMLTYPE) internal procedure.');
      DBMS_OUTPUT.PUT_LINE(sqlerrm());
      RAISE;
  END xml_println;

--****************************************************************************
--* Main Procedure
--****************************************************************************
BEGIN
  DBMS_OUTPUT.ENABLE(200000);
  println('begin...');

  v_url := i_url;
  IF v_url IS NULL THEN
    RAISE_APPLICATION_ERROR(-20000, 'URLを指定してください。');
  END IF;

  v_req := UTL_HTTP.BEGIN_REQUEST(v_url);
  UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle UTL_HTTP/Oracle10g R1');
  UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'text/xml;charset=UTF-8');
  v_resp := UTL_HTTP.GET_RESPONSE(v_req);
 
  -- RSSソースを一時CLOBへ書き出す
  DBMS_LOB.CREATETEMPORARY(v_tempSourceClob, FALSE);
  BEGIN
    LOOP
      UTL_HTTP.READ_LINE(v_resp, v_source, true);
      
      -- 改行コード強制的に付加。
      -- xml_println()でDBMS_OUTPUT.PUT_LINE()が、最大255文字までしか
      -- 表示できないことへの一時的な対策。(試しているサイトについてはこれで
      -- 回避できているので今のところはこのままで。)
      v_source := v_source || UTL_TCP.CRLF;
      DBMS_LOB.WRITEAPPEND(v_tempSourceClob, LENGTH(v_source), v_source);
    END LOOP;
  EXCEPTION
    WHEN UTL_HTTP.END_OF_BODY THEN
      UTL_HTTP.END_RESPONSE(v_resp);
  END;
 
  println('====================');
  v_myParser := DBMS_XMLPARSER.NEWPARSER();
  DBMS_XMLPARSER.PARSECLOB(v_myParser, v_tempSourceClob);
  v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
 
  println('DBMS_XMLPARSERバージョン:'||DBMS_XMLPARSER.GETRELEASEVERSION());
  println('DBMS_XMLDOMバージョン   :'||DBMS_XMLDOM.GETVERSION(v_rssDoc));
  println(' ');
  xml_println(DBMS_XMLDOM.GETXMLTYPE(v_rssDoc));
  println('====================');
  println(' ');
  DBMS_LOB.FREETEMPORARY(v_tempSourceClob);
  DBMS_XMLPARSER.FREEPARSER(v_myParser);

  println('...End');

EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('Error Occured : main procedure.');
    DBMS_OUTPUT.PUT_LINE(sqlerrm());
    RAISE;

    END RSS_FEED_READER;

また、前述のようにした事で、Mac De Oracleなどで発生してたココログのATOM配信フォーマットのパースエラーも消えた!?(やはり文字化けが影響していたようだ。)
以下に、Mac De OracleのATOMを読み込んだOracle SQL Developer 1.0 for MacOSXのスナップショットを示す。


Jdevok

Oracle10g R1で、なんとか日本語対応のRSS Readerを作ることもできそうな感触。。。。Oracle10g R2は、MacOSX版がリリースされていないの(本当にリリースされるのか??)で、それまではLinux版のOracel10g R2で試してみるか。。。

今聞いている曲:
Malene Mortensen - Date with a DreamMalene Mortensen - デイト・ウィズ・ア・ドリーム GENAI - HAEVEN OF EARTHジェナイ - ヘブン・オン・アース

| | | コメント (0) | トラックバック (0)

2006年8月 7日 (月)

Mac De PL/SQL RSS Reader #2

前回のつづきです。

前回、


RSS Readerの実験に利用するBlogは、私の友人であるmegawattさんのサイトを利用させて頂いた。(自分のブログである、Mac De OracleのRSSを利用していないのだが、理由は後日!)

と書いたが、当初、私のブログ(Mac De Oracle)で試そうと思ったのだが、何故、利用しなかったのか書いておく。
ココログは、RSSフォーマットではなく、Atom配信フォーマット。これはXMLのフォーマットが異なるだけなので、利用しなかった理由ではない。問題は、ココログのATOM配信フォーマットをDBMS_XMLPARSERパッケージのParserでパースすると、パースエラーが発生するためだ。

以下、前回のプロシージャを利用してMac De OracleのATOMを解析した実行結果を示す。(実行には、Oracle PL/SQL Developer 1.0を利用した。)

データベースOracle10g Scottに接続中です。
ORA-31011: XML解析に失敗しました
ORA-19202: XML処理
LPX-00244: 小なり不等号 ('<')文字の使用が無効です。 (&lt;を使用します)
Error at line 63
中にエラーが発生しました
ORA-06512: "SCOTT.RSS_FEED_READER", 行155
ORA-06512: 行6
begin...
Main Procedure:ORA-31011: XML解析に失敗しました
ORA-19202: XML処理
LPX-00244: 小なり不等号 ('<')文字の使用が無効です。 (&lt;を使用します)
Error at line 63
中にエラーが発生しました
プロセス終了。
データベースOracle10g Scottから切断中です。

私のブログだけが問題なのか? 少々、心配になったので、私のお気に入りブログの一つである「鎌倉withLove」(現在は、ご自身で立ち上げたサイトに移行されているが、まだ、ココログにも残っているのでココログのATOMでも試させていただいた。)でも試してみた。やはり、同じエラーでXMLのパースエラーとなる。

Macdeoracleatom_error

新しい「鎌倉withLove」のサイトのATOMでも試してみたのだが、そちらではパースエラーは発生していない。文字化けは発生するが。。。(ココログもベースはMovableTypeだと思うのだが。。。随分カスタマイズされているようで。。)

Kamakurawithlove_ok

#尚、パースエラーの原因は、DBMS_XMLPARDERパッケージで行うURL直指定パースで発生する文字化けが絡んでいる可能性も捨てきれない。(これは後日確認する予定。)


今聞いている曲:
Najee - Tokyo Blue Kim Waters - All for Love

| | | コメント (0) | トラックバック (0)

2006年8月 6日 (日)

Mac De PL/SQL RSS Reader #1

Mac De Oracle、久々の投稿です。前回までの騒ぎはなんとか収束。(あとはベンダーさん次第。。)

ということで、以前から読んでいるこのブログの記事にあるPL/SQLでRSS Reader!
そのネタの一部?!を試してみようと思う。まず、手始めに一番気になっているエンコーディングの部分から始めることにする。

環境は、 いつもの Oracle10g R1 EE for MacOSX Serverを利用する。(インストール方法などは、こちらを参考にしてください。
尚、MacOS Xは、以下のように現在の最新版を利用している。なお、データベースキャラクタセットは、JA16SJISTILDEである。

Os

RSS Readerの実験に利用するBlogは、私の友人であるmegawattさんのサイトを利用させて頂いた。(自分のブログである、Mac De OracleのRSSを利用していないのだが、理由は後日!)
megawattさんRSSのバージョンは、RSS 0.91RSS 1.0を提供しているが、当面はRSS 0.91で遊ぶことにする。

megawattさん、いろいろと試していたので、アクセスログがタップリ吐かれていると思いますが、DOS攻撃ではないのでご安心を! (^^;;;;;

さて、本題。
結果を先に書いておくと、Oracle10g R1のDBMS_XMLPARSERパッケージのXMLパーサーでRSS FeedのURLを直指定しパースした場合、マルチバイト文字は文字化した。(Oracle10g R2ではどうなのかは後日確認する予定)。 なんとかエンコーディングを指定できないものかと調べてみたが、Oracle10g R1のDBMS_XMLPARSERでは、XMLパーサーに対してエンコーディングを指定することはできないようだ。(マニュアルをざっと読んでみたが、該当するプロシージャもファンクションも存在しない。)


以下は、Oracle10g R1 EE for MacOSX ServerのDBMS_XMLPARSERパッケージを利用してパース後、XMLDOMからRSSソースを取り出しリストした結果である。文字の化け方から想像すると、UTF-8をISO Latin 1としてエンコードしているのかもしれない。
尚、すべての操作はOracle SQL Developer 1.0を利用して行った。(便利なのだがMacOSXだと、debuggerの動作がかなり遅いかも。。実際にはdebuggerを起動しなくても試せるので問題は無かったのだが。。)


_1 _2

_3 _4



データベースOracle10g R1 glasscatfishに接続中です。
begin...
DBMS_XMLPARSERバージョン:10.1.0.2.0
DBMS_XMLDOMバージョン :1.0
=== パース済みDOMから取得したRSSソース ===
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
"http://my.netscape.com/publish/formats/rss-0.91.dtd">
<rss version="0.91">
<channel>
<title>a?!a?¬a??e≪?</title>
<link>http://megawatt.blogdns.net/blog</link>
<description>A Weblog Product for Zope</description>

<item>
<title>c??a??a??e?oao?a??a??a?≪e!?a?£a?|a??a??</title>
<link>http://megawatt.blogdns.net/blog/252</link>
</item>

<item>
<title>e??a?aa??a?¨a??a? ̄e??a?aa?? Part2</title>
<link>http://megawatt.blogdns.net/blog/251</link>
</item>

<item>
<title>e??a?aa??a?¨a??a? ̄e??a?aa??</title>
<link>http://megawatt.blogdns.net/blog/250</link>
</item>

<item>
<title>a??a??a??a??a??a??</title>
<link>http://megawatt.blogdns.net/blog/249</link>
</item>

<item>
<title>c?≪a?Ra??a??a?Ra??c??e¬?ao§i??2006a1´ao|a??a??a?≫i??</title>
<link>http://megawatt.blogdns.net/blog/248</link>
</item>

<item>
<title>a? ̄a??a?3100mla??80a?-a?-a?≪a?-a?aa??!!</title>
<link>http://megawatt.blogdns.net/blog/247</link>
</item>

<item>
<title>a? ̄a?£a?!a??a??a?§a? ̄a?aa??a? ̄a?£a?!a??a??</title>
<link>http://megawatt.blogdns.net/blog/246</link>
</item>


<item>
<title>a?!a??a?£a?¨a??eμ°a?£a?|a??</title>
<link>http://megawatt.blogdns.net/blog/245</link>
</item>

<item>
<title>a??a??a??a??a??a?,a??</title>
<link>http://megawatt.blogdns.net/blog/244</link>
</item>

<item>
<title>c?≪a??a??a?? ate??a??a≫2c?o</title>
<link>http://megawatt.blogdns.net/blog/243</link>
</item>

<item>
<title>c?≪a??a??a?£a??a?¥</title>
<link>http://megawatt.blogdns.net/blog/242</link>
</item>

<item>
<title>a??e£2c?ca?aa?≪a?≪a??a??a??a???</title>
<link>http://megawatt.blogdns.net/blog/241</link>
</item>

<item>
<title>e??a??a≫2c?oa?≫a・±a・?a,?a??a??</title>
<link>http://megawatt.blogdns.net/blog/240</link>
</item>

<item>
<title>a??a??a??a??a??a??a??a??</title>
<link>http://megawatt.blogdns.net/blog/239</link>
</item>

<item>
<title>c?-a-?c?±</title>
<link>http://megawatt.blogdns.net/blog/238</link>
</item>

<item>
<title>a??a??a??a??a??a??a?aa??a?§</title>
<link>http://megawatt.blogdns.net/blog/237</link>
</item>

</channel>
</rss>
====================
...End
プロセス終了。
データベースOracle10g R1 glasscatfishから切断中です。

以下は、AMIS Technology Blogのサンプルコードを参考にして作成したプロシージャのソースである。
URLで指定されたRSSソースをDBMS_XMLPARSERパッケージのXMLパーサーでパースし、生成されたXMLDOMオブジェクトからRSSソースをリストするしている。


CREATE OR REPLACE PROCEDURE RSS_FEED_READER
(
i_url IN VARCHAR2
)
AS
--============ TYPEs/VARIABLEs ===============================================
TYPE rss_type IS RECORD
(
title VARCHAR2(2000),
link VARCHAR2(200),
pubDate VARCHAR2(200),
category VARCHAR2(500),
description VARCHAR2(4000)
);
v_rss_item rss_type;
v_rss_empty_item rss_type; -- v_rss_item初期化用


v_url VARCHAR2(32767);

-- XML PARSER
v_myParser DBMS_XMLPARSER.Parser;

-- XMLDOM
v_rssDoc DBMS_XMLDOM.DomDocument;

-- for UTL_HTTP
v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;
v_source VARCHAR2(1024);

-- RSSソース用CLOB(SYS.XMLTYPE型にしたほうがよかったかもしれないが、現時点ではCLOBで!)
v_tempClob CLOB;

--========== Internal PROCEDUREs/FUNCTIONs ===================================
--
--****************************************************************************
--* VARCHAR2の文字列を最大で255文字または、改行コード毎にプリントする内部プロシージャ
--****************************************************************************
--
PROCEDURE println
(
i_text IN VARCHAR2
)
IS
v_src VARCHAR2(32767);
v_tempStr VARCHAR2(32767);
BEGIN
v_src := i_text;
IF INSTR(v_src, UTL_TCP.CRLF) <> 0 THEN
v_tempStr := SUBSTR(v_src, 1, INSTR(v_src, UTL_TCP.CRLF)-1);
v_src := SUBSTR(v_src, INSTR(v_src, UTL_TCP.CRLF)+LENGTH(UTL_TCP.CRLF));
ELSE
v_tempStr := v_src;
END IF;

WHILE v_tempStr IS NOT NULL LOOP
IF LENGTH(v_tempStr)> 255 THEN
FOR i IN 1..(TRUNC(LENGTH(v_tempStr)/255)+1) LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(v_tempStr,1+ 255*(i-1),255));
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE(v_tempStr);
END IF;

IF INSTR(v_src, UTL_TCP.CRLF) <> 0 THEN
v_tempStr := SUBSTR(v_src, 1, INSTR(v_src, UTL_TCP.CRLF)-1);
v_src := SUBSTR(v_src, INSTR(v_src, UTL_TCP.CRLF)+LENGTH(UTL_TCP.CRLF));
ELSE
EXIT;
END IF;
END LOOP;

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(
'println(VARCHAR2) internal procedure:'
|| sqlerrm()
);
RAISE;
END println;

--****************************************************************************
--* CLOBの文字列を最大で255文字または、改行コード毎にプリントする内部プロシージャ
--****************************************************************************
--
PROCEDURE println
(
i_text IN CLOB
)
IS
v_src CLOB;
v_tempStr CLOB;
BEGIN
v_src := i_text;
IF INSTR(v_src, UTL_TCP.CRLF) = 0 THEN
v_src := REPLACE(v_src, '><', '>'||UTL_TCP.CRLF||'<');
END IF;

v_tempStr := SUBSTR(v_src, 1, INSTR(v_src, UTL_TCP.CRLF)-1);
v_src := TRIM(LEADING UTL_TCP.CRLF FROM SUBSTR(v_src, INSTR(v_src, UTL_TCP.CRLF)));

WHILE v_tempStr IS NOT NULL LOOP
IF LENGTH(v_tempStr) > 255 THEN
FOR i IN 1..(TRUNC(LENGTH(v_tempStr)/255)+1) LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(v_tempStr,1+ 255*(i-1),255));
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE(v_tempStr);
END IF;
v_tempStr := SUBSTR(v_src, 1, INSTR(v_src, UTL_TCP.CRLF)-1);
v_src := TRIM(LEADING UTL_TCP.CRLF FROM SUBSTR(v_src, INSTR(v_src, UTL_TCP.CRLF)));
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(
'println(CLOB) internal procedure:'
|| sqlerrm()
);
RAISE;
END println;

--****************************************************************************
--* Main Procedure
--****************************************************************************
BEGIN
DBMS_OUTPUT.ENABLE(200000);
println('begin...');


v_url := i_url;
IF v_url IS NULL THEN
RAISE_APPLICATION_ERROR(-20000, 'URLを指定してください。');
END IF;

v_myParser := DBMS_XMLPARSER.NEWPARSER();
DBMS_XMLPARSER.PARSE(v_myParser, v_url);
v_rssDoc := DBMS_XMLPARSER.GETDOCUMENT(v_myParser);
DBMS_LOB.CREATETEMPORARY(v_tempClob, FALSE);
DBMS_XMLDOM.WRITETOCLOB(v_rssDoc, v_tempClob);
println('DBMS_XMLPARSERバージョン:'||DBMS_XMLPARSER.GETRELEASEVERSION());
println('DBMS_XMLDOMバージョン :'||DBMS_XMLDOM.GETVERSION(v_rssDoc));
println(' ');
println('=== パース済みDOMから取得したRSSソース ===');
println(v_tempClob);
println('====================');
println(' ');
DBMS_LOB.FREETEMPORARY(v_tempClob);

DBMS_XMLPARSER.FREEPARSER(v_myParser);

println('...End');

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(
'Main Procedure:'
|| sqlerrm()
);
RAISE;

END RSS_FEED_READER;


次回は、RSSをパースする際、RSS FEEDのURLを直指定したパースではなく、一旦、UTL_HTTPパッケージを利用してRSSを取得、CLOB変換する。そのCLOBをDBMS_XMLPARSERパッケージのパーサーでパースし、結果を確認してみることにする。
(ちなみに、WindowsのOracle10g R2 EEではXMLDOMが修正されたのか、DBMS_XMLPARSERパッケージかDBMS_XMLDOMパッケージが改良されたのか?。異なる結果をしてしている。それに関しては別記事にて書く予定)


今聞いている曲:
Dave Koz - Lucky Man Dave Koz - The Dance Richard Elliot - Metro Blue

| | | コメント (0) | トラックバック (0)

2006年5月10日 (水)

Mac De SQL Developer #11

MacOSXでしか発生しない問題は、最新版では修正されています。他の問題も暇を見てみて見る予定。(しかし、忙しいのか、そうじゃないのか微妙な状態。。。)

| | | コメント (0) | トラックバック (0)

2006年5月 8日 (月)

Mac De SQL Developer #10

本家OTNで Oracle SQL Developer 1.0, patch 1 (1.0.0.15.27) がリリースされていた。

あとで、ダウンロードしておきますかね。

| | | コメント (0) | トラックバック (0)

2006年5月 6日 (土)

Mac De SQL Developer #9

前回に引き続き、SQL Developer 1.0 (Build 1.0.0.14.67)の問題点。文字化けに比べると、軽微なUI上の問題について。

「接続」Paneでオブジェクトを削除すると、展開していたツリーが閉じてしまうという問題と
一括削除時に、各オブジェクト毎に毎回、確認ダイアログが表示されてしまう。

以下は複数のTYPEを選択し一括削除した場合の例。
MacOSXの場合、SHIFTキー又は、コマンドキーを押しながら該当オブジェクトをクリックすると複数のオブジェクトを選択できる。

Sqldev91


「メニュー」→「削除」を選択して該当オブジェクトを削除する。

Sqldev92


複数選択で一括削除なのだが、各オブジェクト毎に「Drop Confirmation」ダイアログが表示される。この例では2つのオブジェクトなので手間にはならないが、数が多いと確認ダイアログに答えるという操作はかなり面倒。
(この確認ダイアログ上に、「以降の確認ダイアログを表示しない。とかいうチェックボックスを付けてくれると便利だと思うが。。」)

「ばい」ボタンをクリックして削除する。

Sqldev93 Sqldev94


さて、問題のUI挙動はここで発生する。削除確認ダイアログで「はい」ボタンをクリックすると該当オブジェクトが削除されるのだが、それと同時に、展開していたツリーが閉じてしまう。

Sqldev95 Sqldev96


次に操作をするには、「接続ツリー」を再度展開しないといけない。

Sqldev97

| | | コメント (0) | トラックバック (0)

2006年5月 5日 (金)

Mac De SQL Developer #8

前回、Oracle SQL Developer 1.0 (build 1.0.0.14.67)で気付いた問題がいくつかあると書いたが、今回はその一つについて書く事にする。
無名PL/SQLブロックのデバックの際(ストアドプロシージャでも同じかもしれないが未確認だが、気がついたことがあればコメントとして追加する予定)、実行方法によっては結果が文字化けするのだ。(正式版では修正されるだろうけど。。)

以下の実行結果では 無名PL/SQLブロックを「Execute Statement」ボタンをクリックして実行した結果なのだが、実行した結果は、「DBMS Output」Paneに表示され文字化けもない。

Sqldev81_1


ところが、「Run Script」ボタンをクリックして実行した場合、「Script Output」paneに表示される実行結果は文字化けする。

Sqldev82


いろいろPreferenceを見てみたものの、それに関連しそうな設定はなさそうだ。

| | | コメント (0) | トラックバック (0)

2006年5月 1日 (月)

PL/SQL De SORT

なんと、ストアドプロシージャ内で配列をソートしてほしいという要求。
Oracle7のころからPL/SQLで開発をしてきてはいるが、そんな要求、はじめてじゃ!


え。。。。なんで? 


データを問い合わせるときに ORDER BY句でソートしちゃえば早いじゃん。
(扱うデータは表から問い合わせたものでは無く、呼び出し元から引数で渡される配列ということらしい。 )

え?、呼出し元って、javaじゃないの? java側でできるでしょう、そんなこと?。。。

まぁ、あちらさんにもいろいろと事情があるようで。。。。。そのシステムでは必要だ、というのだからサンプルだけは作ってあげましょう。
(ちなみに、Oracle8のころに、こんな事言われたら、即、荷物纏めて撤収してたと思うぞ。。。本気で。)


ということで、Oracle10gを利用する前提で考えてみたが、マニュアルなどの資料では、Oracle8i 8.1.6以降であればできるとなっている。
(以降のサンプルで示すTABLE演算子や、CAST関数が登場するのは、Oracle8i 8.1.6のSQLリファレンスマニュアルから登場している。)
珍しい要求だったこともあり備忘録として書き留めておく。
尚、PL/SQLの言語仕様上、コレクションの要素数に上限はないタイプのコレクションもあるので、設定する要素のサイズや、要素数によっては、それ相当のメモリが消費されることも頭の片隅に置いておくこと。


PL/SQLで扱える3タイプのコレクションの簡単な特徴は以下の通り。

(1)結合配列


Oracle7のころからあるコレクション。索引付き表とも呼ばれる。添字には任意の数字、文字列を利用できる。Perlの連想配列や、JavaのHashTableなどをイメージすればわかりやすいだろう。任意の数の要素を持てる。
この配列でVARCHAR2などの文字型を添字にした場合には要素数の上限は無くなる。binary_integerなどの数値型を利用した場合には各数値型の範囲が要素数の上限となる。

(2)ネスト表

Oracle8以降で登場したコレクション。定義は結合配列に似ているが、配列の添字は1から始まる連続した正の整数のみ(1〜2147483647)であるため要素数には上限がある。結合配列と同様に任意の数の要素を持てる。また、結合配列とは異なり表に格納することもできる。

(3)VARRAY表

Oracle8以降で登場したコレクション。配列の添字はネスト表と同じであるが、要素は固定数保持することができる点が異なる。ネスト表と同様に表に格納することもできる。

Oracle8までならば、PL/SQLの結合配列をソートしろなんてことになると、ロジックをガリガリ書いてみたり、場合によっては、一旦、作業用表へ格納後、取り出す際にORDER BYでソートといった方法が考えられる。いずれにしても、あまりイケてないわけ。

そこで、ネスト表とオブジェクト型を利用し、PL/SQLでコレクションをソートするという方法の簡単な例。(この例ではプロシージャにはしていない)


Oracle10g R1を使用。

※ソート条件として、同値の場合は、元のデータ順が維持されること。(つまり、安定ソートが条件。ちなみに、クイックソートは不安定ソート。)

このソートは、コレクション(ネスト表又はVARRAY)のネスト解除と呼ばれる方法を利用して行う。(マニュアル「SQLリファレンス 10g リリース1」SELECT文の table_collection_expression P19-22 などを参照のこと。)

(1)オブジェクト型の作成


2つの属性を持たせ、user_idまたは、user_nameいずれかでソートすることにする。
SCOTT> l
1 create type UserInfo_typ as object
2 (
3 user_id number(5),
4 user_name varchar2(40)
5* )
SCOTT> /

型が作成されました。


(2)コレクションの定義。

UserInfo_typ型のネスト表を定義した。
SCOTT> create type UserList_typ as table of UserInfo_typ;
2 /

型が作成されました。

(3)コレクション UserList_typを user_nameで昇順にソートする。
'みた'というデータが2件存在するが、元の順序が変わらない安定したソートである点に注目。
SCOTT> set timi on
SCOTT> set serveroutput on
SCOTT> l
1 DECLARE
2 UserList UserList_typ := UserList_typ(
3 UserInfo_typ(1,'あいだ'),
4 UserInfo_typ(2,'さえき'),
5 UserInfo_typ(3,'やおか'),
6 UserInfo_typ(8,'おだ'),
7 UserInfo_typ(5,'みた'),
8 UserInfo_typ(6,'みた'),
9 UserInfo_typ(null,null)
10 );
11 BEGIN
12 FOR user_rec IN (
13 SELECT *
14 FROM
15 TABLE(CAST(UserList as UserList_typ))
16 ORDER BY
17 user_name
18 )
19 LOOP
20 dbms_output.put_line(
21 nvl(to_char(user_rec.user_id),'NULL') ||
22 '=' ||
23 nvl(user_rec.user_name,'NULL')
24 );
25 END LOOP;
26* END;
SCOTT> /
1=あいだ
8=おだ
2=さえき
5=みた
6=みた
3=やおか
NULL=NULL

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.01
SCOTT>


(4)コレクション UserList_typを user_nameで降順にソートする。

'みた'というデータが2件存在するが、元の順序が変わらない安定したソートである点に注目。
SCOTT> l
1 DECLARE
2 UserList UserList_typ := UserList_typ(
3 UserInfo_typ(1,'あいだ'),
4 UserInfo_typ(2,'さえき'),
5 UserInfo_typ(3,'やおか'),
6 UserInfo_typ(8,'おだ'),
7 UserInfo_typ(5,'みた'),
8 UserInfo_typ(6,'みた'),
9 UserInfo_typ(null,null)
10 );
11 BEGIN
12 FOR user_rec IN (
13 SELECT *
14 FROM
15 TABLE(CAST(UserList as UserList_typ))
16 ORDER BY
17 user_name DESC
18 )
19 LOOP
20 dbms_output.put_line(
21 nvl(to_char(user_rec.user_id),'NULL') ||
22 '=' ||
23 nvl(user_rec.user_name,'NULL')
24 );
25 END LOOP;
26* END;
SCOTT> /
NULL=NULL
3=やおか
5=みた
6=みた
2=さえき
8=おだ
1=あいだ

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.03


(5)コレクション UserList_typを user_idで降順にソートする。
SCOTT> l
1 DECLARE
2 UserList UserList_typ := UserList_typ(
3 UserInfo_typ(1,'あいだ'),
4 UserInfo_typ(2,'さえき'),
5 UserInfo_typ(3,'やおか'),
6 UserInfo_typ(8,'おだ'),
7 UserInfo_typ(5,'みた'),
8 UserInfo_typ(6,'みた'),
9 UserInfo_typ(null,null)
10 );
11 BEGIN
12 FOR user_rec IN (
13 SELECT *
14 FROM
15 TABLE(CAST(UserList as UserList_typ))
16 ORDER BY
17 user_id DESC
18 )
19 LOOP
20 dbms_output.put_line(
21 nvl(to_char(user_rec.user_id),'NULL') ||
22 '=' ||
23 nvl(user_rec.user_name,'NULL')
24 );
25 END LOOP;
26* END;
SCOTT> /
NULL=NULL
8=おだ
6=みた
5=みた
3=やおか
2=さえき
1=あいだ

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.03

(6)コレクション UserList_typを user_idで昇順にソートする。

SCOTT> l
1 DECLARE
2 UserList UserList_typ := UserList_typ(
3 UserInfo_typ(1,'あいだ'),
4 UserInfo_typ(2,'さえき'),
5 UserInfo_typ(3,'やおか'),
6 UserInfo_typ(8,'おだ'),
7 UserInfo_typ(5,'みた'),
8 UserInfo_typ(6,'みた'),
9 UserInfo_typ(null,null)
10 );
11 BEGIN
12 FOR user_rec IN (
13 SELECT *
14 FROM
15 TABLE(CAST(UserList as UserList_typ))
16 ORDER BY
17 user_id
18 )
19 LOOP
20 dbms_output.put_line(
21 nvl(to_char(user_rec.user_id),'NULL') ||
22 '=' ||
23 nvl(user_rec.user_name,'NULL')
24 );
25 END LOOP;
26* END;
SCOTT> /
1=あいだ
2=さえき
3=やおか
5=みた
6=みた
8=おだ
NULL=NULL

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.03

となる。
ソートはOracleにお任せなので、特に小難しいロジックを書く事も無く、PL/SQL内で、簡単にコレクションをソートすることができる。
小難しいロジックの記述はないので単体テストは簡単だろう。
しかし、もし、このような方法を採用するのであれば、本番で想定されるコレクションのサイズ、プログラムの並列実行数でのメモリ消費量のチェックやシステム全体への影響などを確認するための十分な負荷テストが必要だと考える。(Oracle側で Resource Managerや、Profileを使用し、リソース制限をしていれば最悪の事態は防げるが、このような要求をしてくる所にかぎって、Resource ManagerやProfileによるリソース管理が行われていない、ということが多いように感じる。)

この例では、SQL*Plusで実行した例を載せたのだが、当初、Oracle SQL Developer 1.0で試していたものがあるので次回は、その時に気付いたSQL Developerの問題について書く予定。

Sqldev81

| | | コメント (0) | トラックバック (0)

2006年4月15日 (土)

Mac De SQL Developer #7

Oracle SQL Developer 1.0 for MacOSXネタというより、OTN-Jで公開していたCodeTips #33関連のネタで、あるユーザが特定のアプリケーションでのみデータベースに接続できるようにしたいというもの。 無理矢理 SQL Developerネタにしてみた。(笑)

尚、OTN-JのCodeTipsを参照するには、OTN-Jにユーザ登録する必要がある。(無料)

CodeTips #33は、JOB QUEUEにジョブを登録し即座に実行、該当する接続を切断するというサンプルである。

JOB QUEUEに登録して実行するという方式であるため ”即座”とは言っても、LOGONクライアントイベントと完全に同期して接続を切断している訳ではない。

JOB QUEUEに関して、Oracle8iまでは、JOB_QUEUE_INTERVALというパラメータでCJQ0プロセス(コーディネータ・ジョブ・プロセス)が、ジョブキューをスキャンする間隔を制御できたが、9i以降廃止されたためこのスキャン間隔を調整することはできない。

また、多数のジョブがエンキューされた場合、登録されたJOBの実行が多少遅延する可能性もある。(JOB_QUEUE_PROCESSESパラメータで、ジョブを実行するJnnn(ジョブ・キュー・プロセス。nnnは数字で000から999まで)プロセス数を調整し、遅延を少なくするという方法はあるが、ただ多くすればいいってものでもない。)

ということで、あるユーザが接続してから接続が切断されるまでには、ある程度のタイムラグが発生する可能性がある。データベースサーバーの負荷が低い状態であれば、ログインとほぼ同時に、接続は切断されることが多いが、当然、そうならない場合もある。

尚、バックグランド・プロセスについては、マニュアル「データベース管理者ガイド Oracleプロセスの管理」及び、「データベース概要の第三部 Oracleプロセスの概要」、
  データベーストリガーについては、マニュアル「アプリケーション開発者ガイド 基礎編」、
  v$ 表に関しては、マニュアル「データース・リファレンス」などを参照すると良いだろう。

その結果、接続が切断されるまでの間にいくつかのDML文などを実行することも可能なのである。
この投稿では、あるユーザが SQL*Plus以外で接続してきた場合、何も出来ないようにしたいと言うことだったため、CodeTips#33のような ジョブキューを利用する方式では要件に合わなかったということなのである。(CodeTipsに載せたコメントに、”ログオンしたとたんに・・・接続を切る”と書いてしまったことも混乱させた原因ではあると思うが。。。。反省)

そこで、私が考えた方法は、接続されてしまってから何とかしようという方式ではなく、接続自体を拒否してしまえばよいのでは?
と思い、以下の方法を提示した。

以下の内容は、OTN-Jの会議室に載せた内容に加えて、Oracle SQL Developerによる接続が拒否され接続エラーとなった画面のスナップショットや、該当トリガーを管理する専用ユーザの作成例も載せておく。

この例では、ログオントリガー管理専用ユーザを作成することにする。

SQL*Plus: Release 10.2.0.1.0 - Production on 水 4月 12 23:30:16 2006

Copyright (c) 1982, 2005, Oracle. All rights reserved.

SQL> conn / as sysdba
接続されました。
SQL> create user caretaker identified by xxxxx
2 default tablespace users
3 temporary tablespace temp
4 quota 1m on users;

ユーザーが作成されました。

SQL> grant create session to caretaker;

権限付与が成功しました。

SQL> grant create trigger to caretaker;

権限付与が成功しました。

SQL> grant select any dictionary to caretaker;

権限付与が成功しました。

SQL> grant administer database trigger to caretaker;

権限付与が成功しました。

作成したcaretakerユーザに接続しトリガーを作成する。

SQL> conn caretaker/xxxxxxx
接続されました。
SQL> CREATE OR REPLACE TRIGGER restrict_logon
2 --
3 -- administer database triggerシステム権限を持つユーザでは
4 -- LOGONクライアントイベントは発生しないのでご注意ください。
5 --
6 AFTER LOGON
7 ON DATABASE
8 DECLARE
9 CURSOR cs_session
10 IS
11 SELECT
12 username,
13 program
14 FROM
15 v$session
16 WHERE
17 audsid = (
18 SELECT
19 USERENV('SESSIONID')
20 FROM
21 DUAL
22 )
23 AND username IS NOT NULL;
24 --
25 c_sqlplus CONSTANT VARCHAR2(7) := 'SQLPLUS';
26 c_target_user CONSTANT VARCHAR2(30) := 'SCOTT';
27 --
28 BEGIN
29 FOR session_rec IN cs_session LOOP
30 IF session_rec.username = c_target_user
31 AND INSTR(UPPER(NVL(session_rec.program, ' ')), c_sqlplus) = 0 THEN
32 RAISE_APPLICATION_ERROR(-20000,'SQL*Plus以外からの接続はできません。');
33 END IF;
34 END LOOP;
35 END;
36 /

トリガーが作成されました。

SQL>


以下、Oracle SQL Developer 1.0でトリガーを作成している画面。(トリガー名やコードの一部が異なりますが気にしないでください)
Mac_sql_dev_restrict_logon_source


SQL*Plusから接続し操作できるかを確認する。
SQL> conn / as sysdba
接続されました。
SQL> select * from dual;

DU
--
X

SQL> conn system/xxxxxx@catfish
接続されました。
SQL> select * from dual;

DU
--
X

SQL> conn scott/tiger
接続されました。
SQL> select count(*) from emp;

COUNT(*)
----------
14

SQL> conn caretaker/xxxxxx@catfish
接続されました。
SQL> select * from dual;

DU
--
X

SQL>

以下、SQL*Plus以外から接続した場合は

ORA-00604: 再帰SQLレベル1でエラーが発生しました。 
ORA-20000: SQL*Plus以外からの接続はできません。
ORA-06512: 行24

のようなエラーが返され接続できなくなっていることを確認。
以下 Oracle SQL Developer 1.0から接続した際のエラー画面。(MacOSX以外のプラットフォームの画面もあります)

尚、SQL Developerの”接続”設定は、新規作成で接続が確立できないと保存できない為、ログオントリガーを無効化した状態で作成し、その後、ログオントリガーを有効化してテストしたものである。

Oracle SQL Developer 1.0 for MacOSX版のスナップショット
Macosx_sql_dev_connect_error


Oracle SQL Developer 1.0 for Linux版のスナップショット
Linux_sql_dev_connect_error


Oracle SQL Developer 1.0 for Windows版のスナップショット
Win_sql_dev_connect_error

おまけ
Oracle 9i R2 EE for Linuxでも同じトリガーを作成し、SQL Developerから接続するとエラーが発生し接続できないことも確認できた。

Oracle9i_r2_connect_error



尚、このトリガーは、OTN-JのCodeTips#1531 でも公開しています。

| | | コメント (0) | トラックバック (0)

2006年4月10日 (月)

Mac De SQL Developer #6

WindowsやLinux版では発生することのない問題点。

MacOSXのGUIには完全に対応していないことが理由だと思うが、ショートカット(command key - Q key) でプログラムが終了しようとするとメニューバーの「SQL Developer」がハイライトしたままでOracle SQL Developerは終了しない。また、同じく、メニューバーの「SQL Developer」から 「Quit SQL Developer」を選択しても終了しない。
Problem0
Problem1


終了させるには、Oracle SQL Developerのアプリケーションメニューの「ファイル」->「終了」を選ぶしかありません。
Problem2


Oracle JDeveloper10gは、MacOSXに最適化されているようで、このようなことはない。(そのうち修正されるでしょうね。)

#他のショートカットは、効くようだ。

| | | コメント (0) | トラックバック (0)

2006年4月 7日 (金)

Mac De SQL Developer #5

アイコンが。。

動作がどうのではなく、アイコンがチープというところだけなのですが、
MacOSXの美しいアイコンに慣れてしまうと、すごく気になるわけですね。このアイコンだと。

Sqldevelopericon


で、XCodeの金槌とODBCアドミニストレータのアイコン、それと、XCodeで作成したERDを込み合わせたような感じのアイコンなんてどう?。
OdbcadminXcodeErd

JDeveloper10gのアイコンは、Appleのページにあるアイコンに似ているので、
Macjava Jdev10g
この際、Appleさんにも協力してもらってアイコンを作ってほしいのもですね。

ということで、こんな感じ? になったらよいかもね。(アイコンにも著作権がありますからぼかしてます。きれいなアイコンは 3つの元画像から勝手に ”想像”してくださいませ。Strata 3D をMacOSX版にアップデートしていなかったので、Classic環境ではうまく動かなくなっていた。。。。Classic環境で動けばオリジナルアイコン作ろうかと思っていたのに。。。とほほ。。 )

Icon_1


とお遊びはこのくらいにして、
MacOSXのSQL Developerだけなのかと思っていたら、他のプラットフォームのsql formaterダイアログのレイアウトも真ん中にグチャっと纏まっている。操作上の問題ないが、少々気になるところ。(他のダイアログはきれいに配置されているのでね。。。)
MacOSX版でテーマを切り替えて表示したスナップショット2点と、Linux版(KDE)のスナップショット1点。

Sqlformat1
Sqlformat2
Sqlformat_linux


==================
Boot Campが発表されたので、
そのうち、 "Mac の Windows De Oracle" なんてことになるかもと言った瞬間、妻からは、ん〜〜〜〜っ。
"Mac の Linux De Oracle" のほうがいいんじゃない? ですと、、、さすが、Slackware、Debian育ちの妻らしい答え。(^^;; (今は、Mac好きな妻になってますがね)

私としては、"Mac の MacOSX De Oracle" 路線で行くのは間違いないんですがね。
(と、その前に、 Mac/Intelは、まだうちには無いし。。。。)

-------------
その後、 Mac No Windows De Oracle の Noが、英語の Noに見えちゃうという妻の突っ込みがあり、"No -> の" に変更。

| | | コメント (0) | トラックバック (0)

2006年4月 6日 (木)

Mac De SQL Developer #4

前回軽く触れた程度だったが、本格的に試そうかということで以下のようなお遊び環境を準備
Dev

OTN-JでもSQL Developer早期アクセスプログラムを開始したようですね。
http://otn.oracle.co.jp/products/database/sql_developer/index.html

ただ、OTN-Jのページからは、WindowsとLinuxだけがダウンロードできるみたいです。。
http://otn.oracle.co.jp/software/products/sql/index.html

まあ、本家OTNのものをOTN-JでダウンロードできるようにしただけなのでMacOSX版は、本家からダウンロードすればいいとは思います。
しかし、
> Windows 及び、Linuxでサポートされます。
MacOSX版は?
(サポートしないってことになってしまうのかどうかは知りませんが・・・)

とりあえず、Oracle SQL Developer 1.0を試してみた環境の情報など。

MacOSX Serverで試した記事だけ載せますが、Server以外でも動作します。

G5Server:˜ discus$ sw_vers
ProductName: Mac OS X Server
ProductVersion: 10.4.6
BuildVersion: 8I127

G5Server:˜ discus$
G5Server:˜ discus$ /System/Library/Frameworks/JavaVM.framework/Versions/1.5/Home/bin/java -version
java version "1.5.0_05"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-83)
Java HotSpot(TM) Client VM (build 1.5.0_05-48, mixed mode, sharing)

JDKに関して、
MacOSXでのデフォルトは、JDK1.4.2のままで J2SE 5.0と共存。
G5Server:˜ discus$ java -version
java version "1.4.2_09"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_09-232)
Java HotSpot(TM) Client VM (build 1.4.2-54, mixed mode)

ライブラリフォルダ以下の内容は以下

Jdk

MacOSXだけでは少々物足りないので、LinuxとWindowsでも軽く遊んでみました。使い勝手には少々問題のあるところもありますが好印象です。(JDeveloperでPL/SQLを開発したことのある方ならすぐにでも移行できるでしょう。また、データベースオブジェクトの操作でも重宝しそうです)

SQL Developerの起動時間は、Linux、MacOSX、Windowsとも意外とサクッと起動するかなという感じで、もたつき感はない。

尚、マシンスペックは以下の通り、
MacOSX 10.4.6 : Apple PowerMac G5 Dual 2.7Ghz RAM:1GB 
United Linux 1.0 (KDE) : Toshiba DynaBook SS SX/210LNLW PentiumM 1Ghz RAM:768MB
Windows XP Professional SP2 : AMD Dual AthronMP 2800+ RAM:1GB

SQL Developerのバージョンは 1.0.0.14.67

#United LinuxからRedHatに乗り換えなきゃな〜そろそろ。(^^;;

前回の記事で、MacOSX版のSQL Developerでは、スプラッシュが表示されないということを書いたが、LinuxとWindows版では見事に表示されていたので、MacOSX版だけの問題のようだ。

使い込んでいくうちにいろいろな問題が見つかるかもしれないので、随時載せる予定。

| | | コメント (0) | トラックバック (0)

2006年3月14日 (火)

Mac De Mo Oracle SQL Developer #3

素早いリリースが続いていた Oracle SQL Developer。 1.0がリリースされたようですね。

EA6からみてもそれほど大きな変更はなく安定感がありそう。(そんなに使い込んでいるわけではないので、ただそう感じているだけですが・・・。)http://www.oracle.com/technology/

でも、ICONがちょいと気になるMacOSXユーザなのです。。。
まぁ、ICONなんてちょちょいのちょいとカスタマイズできるので自分で作っちゃえばいいわけですが・・・。

SQLDeveloperICON

SQLDeveloper1



でツリーの下に目をやると、Recycle BINのノードがありました。
お〜〜、Flashback tableもここから行えちゃうのですね。何かと便利そうです。
他にもいろいろな機能がてんこ盛りといった感じがあるのでこれからいろいろ遊んでみる予定。

SQLDeveloper1



そして、これまた素早いというか、タイミングもよく、
SQL Developerがらみの Podcastingもやってますね

| | | コメント (0) | トラックバック (0)

2006年3月 1日 (水)

Mac De Mo Oracle SQL Developer #2(遊んでみた)

ダウンロードしたOracle SQL Developer for MacOSXを簡単に操作してみた。意外にサクサク動くのね。

ちなみに、PowerBook G4 1Ghz MacOSX Tiger 10.4.5 RAM:1GB。

ダウンロードしたアーカイブをダブルクリックして解凍すると以下のようなアイコンが登場する。
インストーラかと思ったら、これがSQL Developer の本体でした。
extracted


EA5版なのでアイコンは、もっと、イケてるものに換えてくれると期待しつつ、ダブルクリックして起動。すると、JDeveloperの環境を移行するか?と聞いてくる。これはご愛嬌かな。移行はしたくないので「いいえ」を答えた。
img_00


無事に起動した。(ばんざ〜〜い!)
外見や使用感はJDeveloperのそれに近い。いろいろいじり倒してみると、DB操作やレポート機能など、JDeveloperで行うより使いやすいと感じる点が多い。
img_01
version


以下、Oracle SQL Developer EA5 for MacOSXをいろいろいじっているところ(Flash)






ちなみに、Oracle Open World 2006が今日から始まりました。

3/1(本日)は、
JDeveloperのハンズオンは、Java One 2005で行われたハンズオンと同じタイトルだったので、それ以外を受講。
backup系2つ、OEM系2つ、それと BI系1つの系5つのセッションを受講した。
睡眠不足から、1E-9 データベースパフォーマンス徹底解説の開始前に少々居眠り。気がつくと両サイドに立ち見の方が沢山いてビックリ。パフォーマンス維持やチューニング方法等への関心が高い証拠だろう。

あと一点、今年は遂に、 Oracle Master専用受付や専用ラウンジなども無くなりましたね。ちょいと寂しい感じはするけど。 
それと Java One 祭り的な要素があってもいいのではないか? と感じた次第。(遊び心も大切さ〜っ。俺だけかもそんなこと考えるは?)
一日目の感想はこのぐらいにして、続きは、atsu-logで。

| | | コメント (4) | トラックバック (0)

2006年2月28日 (火)

Mac De Mo Oracle SQL Developer

http://www.oracle.com/technology/software/products/sql/index.html

Project Raptor改め、Oracle SQL DeveloperのMacOSX版 EA5がリリースされていますね。

意外に早い対応ですね。

開発ツールのサポートはスムーズなのですが、XEも含めたデータベースのリリースが楽しみですね。(XEはリリースされるとはアナウンスされていませんが・・・・。Macユーザの要望が多ければ・・・・なんとかなったりして・・・)

ダウンロードはしたので暇をみて試してみる予定。

| | | コメント (0) | トラックバック (0)

2005年11月 8日 (火)

Mac De HTML DB 10g その8

なんとか風邪も完治!?
TSUTAYAで借りた天龍八部の8、9、10巻とJazzのCDを買い物のついでに返却しようと外出。

こちらは運転するのでTSUTAYAの袋を妻に預けたのだが、その袋のレンタルレシートに、

天龍八部 8巻
天龍八部 9巻
天龍八部 10巻
矢野沙織/YANO SAORI

とリストされていた、最後にあるのがJazzのCD(矢野沙織さんは16歳にしてjazz sax CDデビュー。現在確か、18歳くらい。)

なのだが、妻は何を勘違いしたのか?(Hビデオでも借りてきたものと勘違いして)いや〜〜〜ん。とか、きゃ〜〜っ。とか叫んでいる。
「おいおい、Hビデオじゃないぞ〜〜〜。JazzのCDだって、それ!!!! (^^;;;;」とツッコミ!。(爆)





ちなみに、「天龍八部」は大映テレビのドラマの人間関係以上にドロドロしているので笑う場面ではないが笑いがでる。ワイヤーアクション+VFX+カンフー好きにはおすすめ。(ただ、殺戮シーンなどが多い。)




それに近いシリーズに「風雲」がある。「風雲」は千葉真一も出演していたことで有名だが。こちらも人間関係のドロドロさとか、失恋したかと思ったら飲んだくれてしまうところとか、またすぐ恋いしちゃうとか、上げたり下げたり、いろいろ面白いです。VFXはちょっとチープな感じがある。(予算少なかったんだろうか)

前述の映画というかドラマか?は、家系図を書いたら大変そうだ。皆兄弟いや姉妹、いや異母兄弟とか異母兄妹か?!、実は血のつながりは無い兄妹だったりと、人間関係が複雑すぎる。。。。




さて、なが〜〜い前置きはこのくらいにして、本題。
使い勝手を試そうと、HTML DBのサンプルアプリとして選んだ”ネタ”が家系図データ管理だったが、家系図データのモデルって深く考えれば考えるほど複雑。本気で考えるとハマりそうなので我が家の家系図データを管理するのに必要最低限な情報+我が家の家系図には不要なんだけどちょっと飾りを付けた感じのERモデルにしてお茶を濁すことにした。

(職業病というかなんというか、このようなモデルを考え始めると、、、、ちょっとした時間があると考えてる(Python Challengeも同じだが)。隣で奥さんがなんか言ってたけど、全然耳に入っていないのに空返事。返事をしてから、何だっけ? いまの? って聞き返したりして。。(^^;;;;  
目的は家系図作ることではなくて、HTML DBの使い勝手を調べるということなので脱線しないようにしないといけない。。。。。)

ということで現在進行形。。。だが、明日(もう今日か!)からJavaOneだな〜。

| | | コメント (0) | トラックバック (0)

2005年10月30日 (日)

Mac De HTML DB 10g その7

いや〜〜。女性は強い!?のか? 

私の母が風邪で熱を出したり寝込んだ姿を一度も見た事が無い。妻も滅多に風邪をひかない(3年に1度くらい)。なのに私は、毎年1度年中行事の如く風邪をひく。先日、妻の職場で”げほげほ”していて4日も風邪で休んだ方がいたそうで、風邪だけには弱い私を気にして外でホコリ払いと除菌消臭スプレーをスーツに(ほんとうにそれで風邪ウィルスがなくなるのか疑問)したうえに、イゾジンでうがいしている妻だったが、そいつはやはりやってきた。そう、4年ぶりに38度超えの熱がでる風邪をひいてしまった。喉の痛みから始まり、翌日には熱が高くなりあっというまに38度、熱が引いたのは翌々日。それが引いたかと思ったら鼻づまりのために呼吸するのがつらく眠れない(まさに、風邪の症状フルコースなのである。こんなフルコースはうれしくない。)。そしてやっと回復間近? という状況でこれを書いている。これが書けるくらいだからあとちょっとで復活だろうな〜。

と前置きかかなり長くなったが、前回、Mac de HTML DBで作る簡単なサンプルアプリケーションに悩んでいると書いた。いろいろ悩んだ末、家系図管理アプリを作ってみることにした。
何故、家系図に落ち着いたかというと、先日叔母が逝去し、通夜と葬儀の為に集まった親戚の顔と名前をすべて妻に理解してもらおうと手書きの家系図を書いていたのだが、従姉妹のご主人からその家系図がほしいと言われたのがきっかけだった。まあ、うちの家系図といっても曾祖父母くらいからのものだが。。。。。手書きもなんなので、MacOSX用の家系図作成ソフトを探してみた。。。ところが、日本のものはほとんどがWindows版(がく〜〜〜っ。)そしてたどり着いたのが、これ

http://www.imac.com/downloads/macosx/home_learning/macfamilytree.html

メニューなどは英語というかエラーメッセージはドイツ語っぽいが、マルチバイト言語にも対応しているようだ

001
002

いろいろなビューがあるが、私は2番目にあるビューさえあれば十分。

003

004

006


これ結構イケテいるのだが、$40のsharewareなのである。滅多にメンテナンスしない我が家の家系図(孔子から始まる家系図は300万人以上いるらしいのでメンテナンスするのは大変だろうけど。。)に$40をかけるつもりはないし、我が家の家系図が描ければ十分なのでのでこの際、HTMLDBのサンプルで作ってしまえ! となった次第である。(どんなものができるか。。。。あまり期待しないでお待ちください。。)。

| | | コメント (0) | トラックバック (0)

2005年10月22日 (土)

Mac De HTML DB 10g その6 Version確認

簡単なアプリケーションを作ってみよう。という段階なのだが、何を作るか悩んでいる。アドレス帳? それとも、簡単な家計簿? ん〜〜〜〜。 

ネタが無いので、Versionの確認方法でも書いておきますかね。。。

HTML DB 10g (v2.0)にすると、 FLOWS_020000 というユーザが作成されている。

SQL*Plusからだと、 FLOWS_020000.wwv_flows_release というファンクションを呼び出せばバージョンが確認できる。(なお、FLOWS_020000は、ロックされているのでsysユーザから実行して確認する。アンロックはしないほうがよいだろう。最初からロックされているので。。)

G5Server:〜 oracle$ sqlplus /nolog

SQL*Plus: Release 10.1.0.3.0 - Production on 土 10月 22 17:13:58 2005

Copyright (c) 1982, 2004, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS>
SYS>
SYS> select flows_020000.wwv_flows_release from dual;

WWV_FLOWS_RELEASE
-------------------------------------------------------
2.0.0.00.49

SYS>

そのほかの方法は、HTML DBにログインして確認する方法。(通常はこちらの方法で確認するのだろうね。)

check_version

check_version2

check_version3

| | | コメント (0) | トラックバック (0)

2005年10月15日 (土)

Mac De HTML DB 10g その5 動作確認

一通りの作業は終えた。HTML DB v2.0へのアップデートがうまく行ったか簡単な動作確認(デモアプリケーションの起動などを行ってみる)

簡単に動作確認を行った。文字化けもなくすんなり動作しているように見える。
また、画面のアイコンボタンが個人的にはいい感じ。しかもドロップダウンメニューが階層化されているのもいいですね。CA の CleverPath Portalのポーレットデザイン時のUIがお気に入りだったが、これはこれでいいかもしれないね。








次は簡単なアプリケーションを作ってみる予定。

| | | コメント (0) | トラックバック (0)

2005年10月14日 (金)

Mac De HTML DB 10g その4 パッチ適用とHTML DB 2.0へのアップデート

前回までで、HTML DB 10g (1.5)のインストールと構成は終了した。今回は、OWAのパッチ適用及び、HTML DB 10g (2.0)へのupdateを行う。

NLS_LANGをAL32UTF8に変更するため、Terminlの文字エンコードもUTF8にしておく。


1)owaのパッチを適用する。

htmldb_2.0.zipを解凍すると作成されるディレクトリにあるowaのパッチが含まれるディレクトリに移動してパッチを適用する。

尚、これ以降の作業では、NLS_LANGを JAPANESE_JAPAN.AL32UTF8に変更して行う。

(手順の詳細はパッチ用ディレクトリに含まれるREADME.txtを参照のこと。)

Last login: Thu Oct 13 23:42:47 on ttyp1
Welcome to Darwin!
G5Server:˜ oracle$ echo $NLS_LANG
Japanese_Japan.JA16SJISTILDE
G5Server:˜ oracle$ export NLS_LANG=JAPANESE_JAPAN.AL32UTF8
G5Server:˜ oracle$ cd /Volumes/Discus/4oracle/oracle/Desktop/htmldb/patch/bug4554072
G5Server:˜/Desktop/htmldb/patch/bug4554072 oracle$ ll
total 192
drwxr-xr-x 3 oracle oinstall 102 Aug 25 17:47 309x
drwxr-xr-x 4 oracle oinstall 136 Sep 12 20:07 904x_1012x
-rw-r--r-- 1 oracle oinstall 3666 Sep 12 13:34 README.txt
-rw-r--r-- 1 oracle oinstall 5773 Aug 19 15:22 htp_htf_backup.sql
G5Server:˜/Desktop/htmldb/patch/bug4554072 oracle$ sqlplus /nolog

SQL*Plus: Release 10.1.0.3.0 - Production on 木 10月 13 23:45:33 2005

Copyright (c) 1982, 2004, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS> @htp_htf_backup
SYS> @904x_1012x/privht
SYS> exit
Oracle Database 10g Enterprise Edition Release 10.1.0.3.0 - Production
With the Partitioning, OLAP and Data Mining optionsとの接続が切断されました。

2)HTML DB 2.0インストール

htmldbins.sqlの引数の詳細については、htmldb_2.0.zipを解凍して作成されるディレクトリにある、welcome.htmをブラウザで開き、「documentation」タブ->HTML DB DocumentationにあるInstallation Guide HTMLリンクをクリック->3.2 Installing the Oracle HTML DB Softwareを参照のこと。
G5Server:˜/Desktop/htmldb/patch/bug4554072 oracle$ cd ../..
G5Server:˜/Desktop/htmldb oracle$
G5Server:˜/Desktop/htmldb oracle$ sqlplus /nolog
SQL*Plus: Release 10.1.0.3.0 - Production on 木 10月 13 23:45:33 2005

Copyright (c) 1982, 2004, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS> @htmldbins xtiger SYSAUX SYSAUX TEMP /i/ xtiger
. ____ ____ ____ ____
. / \ | \ /\ / | /
.| || / / \ | | |
.| ||--- ---- | | |--
.| || \ / \ | | |
. \____/ | \/ \ \____ |____ \____
.
. HTML DB Hosted Development Service Installation.
.........................................................

...中略...

3)HTML DB 2.0 日本語版インストール

htmldbins.sqlの引数の詳細については、htmldb_2.0.zipを解凍して作成されるディレクトリにある、welcome.htmをブラウザで開き、「documentation」タブ->HTML DB DocumentationにあるInstallation Guide HTMLリンクをクリック->4.5 Installing Oracle HTML DB in Other Languagesを参照のこと。
G5Server:˜/Desktop/htmldb oracle$ cd builder/ja
G5Server:˜/Desktop/htmldb/builder/ja oracle$ ll
total 65432
-r--r--r-- 1 oracle oinstall 21729796 Sep 7 15:40 f4000_ja.sql
-r--r--r-- 1 oracle oinstall 1739940 Aug 21 16:59 f4050_ja.sql
-r--r--r-- 1 oracle oinstall 59058 Aug 21 17:00 f4155_ja.sql
-r--r--r-- 1 oracle oinstall 783102 Aug 21 17:00 f4300_ja.sql
-r--r--r-- 1 oracle oinstall 1370464 Aug 21 16:59 f4350_ja.sql
-r--r--r-- 1 oracle oinstall 1147649 Aug 21 16:59 f4411_ja.sql
-r--r--r-- 1 oracle oinstall 6406200 Aug 21 16:59 f4500_ja.sql
-r--r--r-- 1 oracle oinstall 96005 Aug 21 17:00 f4550_ja.sql
-r--r--r-- 1 oracle oinstall 131682 Aug 21 16:59 f4700_ja.sql
-r--r--r-- 1 oracle oinstall 2281 Dec 29 2003 load_ja.sql
-r--r--r-- 1 oracle oinstall 1869 Oct 4 2004 unload_ja.sql
G5Server:˜/Desktop/htmldb/builder/ja oracle$ sqlplus /nolog
SQL*Plus: Release 10.1.0.3.0 - Production on 木 10月 13 23:45:33 2005

Copyright (c) 1982, 2004, Oracle. All rights reserved.

> conn / as sysdba
接続されました。
SYS> ALTER SESSION SET CURRENT_SCHEMA = FLOWS_020000;

セッションが変更されました。

SYS> @load_ja
. ____ ____ ____ ____
. / \ | \ /\ / | /
.| || / / \ | | |
.| ||--- ---- | | |--
.| || \ / \ | | |
. \____/ | \/ \ \____ |____ \____
.
. HTML DB Hosted Development Service Installation.
.........................................................

...中略...



これでOWAへのパッチ適用と HTML DB 2.0 へのアップグレード作業が完了した。

う˜˜ん。順調過ぎて逆に不安(笑)。

次回、HTML DB 2.0 簡単な動作確認へつづく。

| | | コメント (1) | トラックバック (0)

2005年10月13日 (木)

Mac De HTML DB 10g その3 インストール後の作業

前回までで、Oracle HTTP Serverのインストールと HTML DB 10g (1.5) のインストールは終了した。

さて、今回はインストール後の作業として、Oracle HTTP ServerからHTML DBを利用するために必要な構成ファイルの編集(OUIのインストールでほとんど出来上がっているのだが)及び、Oracle HTTP Serverの起動、そしてアプリケ−ション開発に必要な作業領域の作成から開発用ユーザの作成までを行う。

HTML DBは、Oracle HTTP Server (Apache)からmod_plsqlを介してWeb Tool Kit (htpパッケージや、htfパッケージなど)やHTML DBのパッケージ群などのストアドプロシージャを実行する。
OWSやWASのころは構成ファイルは dad.conf だったのだが、現在は marvel.conf がデフォルトのようである。(ただし、dad.conf も利用可能で dad.conf がある場合には dad.conf も読み込まれる。”も”というのがポイントだ。2つの構成ファイルはどちら"も"読み込まれるのだ。dad.conf 、marvel.conf の順にimport されている。マニュアルはまだそれほど読み込んでいないのでdad.confとmarvel.confについては別途調べておいた方が良さそうだ。ちなみにPlsqlDatabasePasswordは暗号化されていた。)


では早速、marvel.conf を編集してみる。
(以下、HTML DBをインストールしたディレクトリを$HTMLDB_HOMEと記す。)

$ cat $HTMLDB_HOME/Apache/modplsql/conf/marvel.conf
Alias /i/ "/Volumes/WorkVol/u01/app/oracle/product/10.1.0/htmlDB/marvel/images/"
<Location /pls/htmldb>
SetHandler pls_handler
Order deny,allow
Allow from all
AllowOverride None
PlsqlDatabaseUsername HTMLDB_PUBLIC_USER
PlsqlDatabasePassword @BXDpf8GmfAmsbuOTY0ApZ78=
PlsqlDatabaseConnectString 192.168.1.19:1521:tiger ServiceNameFormat
PlsqlDefaultPage htmldb
PlsqlDocumentTablename wwv_flow_file_objects$
PlsqlDocumentPath docs
PlsqlDocumentProcedure wwv_flow_file_manager.process_download
PlsqlAuthenticationMode Basic
PlsqlNLSLanguage JAPANESE_JAPAN.JA16SJISTILDE
</Location>
AddType text/xml xbl
AddType text/x-component htc

marvel.conf はインストール完了時点でほぼ完成している。加えた変更は PlsqlNLSLanguage を AMERICAN_AMERICA.JA16SJISTILDE から JAPANESE_JAPAN.JA16SJISTILDE に変更し、最後にある AddType の2行を追加しただけである。
尚、タグの詳細は付属マニュアルに"きっちり"書かれているのでそちらを参照のこと。

編集が終わったら、Oracle HTTP Serverを起動する。(OUIでインストールした場合には、起動されているはずなので一旦停止してから起動する。)

$ $HTMLDB_HOME/Apache/Apache/bin/apachectl stop
$HTMLDB_HOME/Apache/Apache/bin/apachectl start: httpd stopped

$ $HTMLDB_HOME/Apache/Apache/bin/apachectl start
$HTMLDB_HOME/Apache/Apache/bin/apachectl start: httpd started

インストールの最後(OUIの画面)にOracle HTTP ServerのURLが表示されていたのを覚えているだろうか? port番号を忘れてしまう方が多いがそのような場合には、
$ cat $HTMLDB_HOME/install/portlist.ini
;OracleAS Components reserve the following ports at install time.
;As a post-installation step, you can reconfigure a component to use a different port.
;Those changes will not be visible in this file.

[System]
Host Name = 192.168.1.19

[Ports]
Oracle HTTP Server port = 7780
Oracle HTTP Server Listen port = 7780
Oracle HTTP Server SSL port = 4458
Oracle HTTP Server Listen (SSL) port = 4458
Oracle HTTP Server Diagnostic port = 7202
Oracle HTTP Server Listen port = 7780
Oracle HTTP Server Listen (SSL) port = 4458

で確認できる。

今回は、localhostでも、192.168.1.19でもどちらでも問題ないはずなので、http://localhost:7780/pls/htmldb/htmldb_adminにアクセスし作業領域と開発用ユーザを作成する。
htmldbの管理ユーザは adminで、パスワードは ouiで指定したパスワードを入力すればよい。行った操作は以下のFlashMovie参照。
この例では、作業領域AQUARIUMを作成時に新規スキーマAQUARIUMを作成し50MBの表領域(FLOWS_1)を新たに作成した。










うまく出来たようなので、先ほど作成した開発用ユーザでログインしてみる。urlは、http://localhost:7780/pls/htmldb/である。










と、ここまでは順調!。
次回、HTML DB2.0に付属する、Web Tool Kit 用パッチの適用へつづく。

| | | コメント (0) | トラックバック (0)

2005年10月12日 (水)

Mac De HTML DB 10g その2 インストール編

前回からのつづき。

環境変数の設定。(インストールの注意点など、詳細は付属ドキュメントをよく読んでくださいね)


今回は、Oracle HTTP ServerとHTML DB 10g をインストールするので、Oracle10g Database及び、listenerを起動しておき、OUIを利用してインストールする。(MacOSX では、onsctlでOracle NamesOracle Notification Serverを起動しておくことをお忘れなく。忘れるとListenerが停止できなくなり、killすることになる。)

dbstart


ORACLE_BASEだけを設定しておけば問題ないのだが、データベースキャラクタセットがマルチバイトキャラクタセットである場合には問題があるようで、英語メッセージを表示させる場合にはAMERICAN_AMERICA.AL32UTF8、日本語メッセージを表示させる場合は、JAPANESE_JAPAN.AL32UTF8のいずれかを、NLS_LANGに設定しておく必要がある。これを忘れるとHTML DBの日本語はすべて文字化けするので注意が必要である。その他のオラクル用環境変数はunsetしておく。
尚、ORACLE_BASEは、Oracle10g R1のORACLE_BASEと同じであるが、HTML DB向けにOracle HTTP Serverもインストールするためインストール先のORACLE_HOME用ディレクトリは別にする必要があるので注意すること。(既存ORACLE_HOMEにインストールしようとするとOUIが警告を出してくれる)

また、Terminalの文字コードは一時的にUTF8にしておくとよいだろう。
runinstall


ship_mac_companioncd.cpio.gzを解凍すると、Disk1というディレクトリが作成されているのでそこに移動して、runInstallerを実行する。最後に、root.shを実行してインストールは完了する。(OUIのログの最後にjavaの例外が出ているようだがOUI自体は正常終了するので気にせずに!)









ちなみに、NLS_LANGをAL32UTF8に設定することを忘れると、OUIのログが文字化けを起こすので間違いに気が付くと思う。尚、やり直す際にはインストールがどこまで進んだかにもよるが、OUIでHTML DBを削除、HTML DBをインストールしたORACLE_HOME以下のディレクトリを削除、そして、FLOWS_010500、FLOWS_FILES、HTMLDB_PUBLIC_USERの3ユーザをdrop user ... cascade文で削除してから再インストールする。
(Windowsと違ってレジストリなんてのが無いので再インストールは楽です)


インストール後の作業へ続く。。。。

| | | コメント (0) | トラックバック (0)

2005年10月11日 (火)

Mac De HTML DB 10g その1 インストール編

PL/SQL で Python Challengeはやる気がでるまで(すぐ再開したりしてね)お休み。 

前回触れたが Oracle10g R1 以降に付属するHTML DBで遊んでみることにする。このHTML DB、Oracle10g R1が発表された2003年のOracle Worldで初めてHTML DBを見たとき、Oracle9i AS R2 Portalのデータベースポートレットとどう違うのさ! と思っていた。(OW2003会場で担当者に聞いても差が見えなかったので最近まで気にも留めていなかった)。 しかし、これを導入したら楽なのになと思うシステムに最近関わったことや、MacOSX版Oracle10gでも動きそうなので Mac De Oracle としては放っておけなくなったのであった。

Oracle9i AS R2 Portalや、Oracle AS10g R1 Portal(中身はOracle9i AS R3といってもいいくらいなのだが。。)のデータベースポートレットは、Oracle AS Portalが必要なのである。(あたりまえじゃん! と自分へツッコミ。)。 PL/SQLを使って比較的簡単なWeb Applicationをウィザードを利用して作成するためだけに、Oracle9i AS Portalや、OracleAS 10g Portalを導入するのは無駄だと思う。 Portal導入なしにWeb Tool Kitによる作成より簡単(WASやOASの時代より簡単)に、PL/SQLでWeb Applicationを作成したいというシステムでは重宝しそうに思える。(OracleAS 10g R2 Portalはまだ見ていないのでどれだけ進化したか不明だが。。)


前置きはこれくらいにして、早速インストールしてみることにする。

HTML DBは、CD版だとCompanion CDに含まれており、Oracle HTTP Server (ApacheをOracleが拡張したもの)も含まれている。 (DVD版だと1枚にまとめられている。) 今回はUS OTNからダウンロードしたものを利用した。



Oracle Database 10g Companion CD Release 1 (10.1.0.3) for Apple Mac OS X
ship_mac_companioncd.cpio.gz (511,023,225 bytes) (cksum - 1634262664)
Oracle HTML DB v2.0 (56,104,916 bytes) - latest standalone version of HTML DB New (13-Sep-05)

尚、Oracle10g R1に付属するHTML DBは、1.5であるが、先月 2.0がリリースされたので2.0へのアップグレードも行うことにする。

以下の手順で進める。


  1. オラクル所有者でログインする。(このユーザで、Oracle10g R1 for MacOSX Serverをインストール及び構成済みである。)

  2. Oralce NamesOracle Notification Service(マニュアルバグのため修正)、Listener、Oracle10g Databaseインスタンスの順に起動する。

  3. ship_mac_companioncd.cpio.gzと Oracle HTML DB v2.0 の2つをダウンロードする。
    (尚、ダウンロードする際には、Oracle Technology Network Developer License Terms に同意する必要がある。)

  4. ship_mac_companioncd.cpio.gzと htmldb_2.0.zipを解凍する。

  5. 環境変数を設定する。
    マルチバイトキャラクタセットをデータベースキャラクタセットとしている場合には、ORACLE_BASE以外にNLS_LANGも設定する。
  6. Oracle Universal Installer(OUI)によるインストール。
    ship_mac_companioncd.cpio.gzを解凍するとDisk1というディレクトリができる。Disk1にディレクトリを移動して、./runInstaller でOUIを起動する。

  7. インストール終了後の作業。


    1. marvel.confを編集する。

    2. Oracle HTTP Serverを起動する。

    3. 作業領域を作成する。

    4. 開発ユーザを追加する。


  8. Web Tool Kit のPatchを適用する。

  9. HTML DB 2.0にアップグレードする。

  10. アプリケーションを開発する。

次回は、5.以降の作業から続きを書く予定である。

2009/5/12変更
Oracle namesはマニュアルバグであるため、Oracle Notification Serviceへ修正した。

| | | コメント (0) | トラックバック (0)

2005年10月 7日 (金)

PL/SQL で Python Challenge 16 え、また画像ですかい。

Level16 ざっと眺めたら、また、画像ですか! (ちょいとやる気を無くす)
画像扱う問題だと以前に解いた問題と同じ流れになりがちで興味が薄れてきた。

先月中頃に、HTMLDB2.0 が Oracle10g R1 for MacOSX Serverのダウンロードページに追加されていたのでそちらのほうが面白そうだな。Oracle AS Portalのデータベースポートレットのような感じだが。。。

| | | コメント (0) | トラックバック (0)

2005年10月 4日 (火)

PL/SQL で Python Challenge 15 やっぱり書いておこう

「UTL_HTTPパッケージと正規表現で終わりそうだった」というのはLevel 15を解いた結果がどうなるか見えてしまったから、その先を(URLを求めるプログラム)プログラミングしてしまおうかなと考えていたためだったのだが、URLを直接求めるプログラミングを要求しているわけではないので、やはり Level 15のURLを探るために必要な答えを導きだす方法は書いておくべきだな。と考え直した。
(私はプログラミングなしで判ってしまったが、一般的にそれに気付く方は少ないよな〜と思ったためである。)

なので、Level 15 をちゃんと解くことにした。
ヒントとしては、Oracleの日付関連の関数を活用すればいい。
ここまで書けば答えを書いているようなものだが。。


今回は特別にLevel 15のHTMLにあるコメント(ヒント)を載せておく。
タイトルは、whom?
コメント(ヒント1):he ain't the youngest, he is the second
コメント(ヒント2):todo: buy flowers for tomorrow


(注意)
日本人にはヒント2がヒントにならないかもしれない。その日のために、花を買うような習慣のある方は少ないだろうから。でも私はすぐにピンときてしまったのだ。)



今回は、ストアドファンクションにした、javaも不要なので、MacOSX 10.4.2 Tigerに作成したOracle10g for MacOSX Serverで行える。
create or replace function level15
return varchar2
is
type date_tbl_typ is table of date index by binary_integer;
days date_tbl_typ;
y pls_integer := 1006;
ctr pls_integer := 0;
mmdd constant char(4) := '0126';
begin
while y<=1996 loop
if to_char(to_date(to_char(y)||mmdd, 'yyyymmdd'),'d') = 2 then
ctr := ctr + 1;
days(ctr) := to_date(to_char(y)||mmdd, 'yyyymmdd');
end if;
y := y + 10;
end loop;
return to_char(days(days.last-2), 'YYYY/MM/DD DAY');
end;
/


/Users/oracle> @level15

ファンクションが作成されました。

/Users/oracle> select level15() from dual;

LEVEL15()
--------------------------------------------------------------------------
1756/01/26 月曜日

/Users/oracle>

この結果が本当の答えではない。ヒント2に関するロジックを、あえてプログラミングしていない。
上記の結果を、ヒント2を元に調整する必要がある。(簡単でしょ!)調整した日付がURLを完成させるための「鍵」になるのだ。

今回は完全にネタバレですね。でもね。この問題プログラミングしなくても判る方は判るし、別な方はググるだけでも解けてしまうくらい単純なのであえてネタバレにしてみました。m(__)m

今度こそ本当に、Level 16へつづく。

| | | コメント (0) | トラックバック (0)

2005年10月 3日 (月)

PL/SQL で Python Challenge 15 いきなり完結編

Level 15 は、なんとプログラミングするまでもなく答えが判ってしまった。いきなり、Level 16のページが見えてしまった。(プログラミングもUTL_HTTPと正規表現程度で終わってしまいそうだったのでプログラミングする気も起きず) プログラミングもせずに、Level 15 は終了することにした。 だれの? というタイトルと、HTMLコメントを見れば、すぐに解決できる。簡単すぎて引っ掛けかと思ったほどだった。(実は深読みさせて迷路に誘い込むつもりだったのかもしれないが。。。。)


Level 16 へつづく。

| | | コメント (0) | トラックバック (0)

2005年10月 1日 (土)

PL/SQL で Python Challenge 14 完結編

さて、バグっていたプログラムを修正して完結!

Level14 はそのページにある画像が問題になっているのでLevel 11の時とやり方はほぼ同じ。違いは、サイズが、### × ### のファイルを新規に作成することだけ。できあがった画像を元に該当するURLをブラウザで開くと、意地悪ともクイズとも言えないようなページが開く。これも簡単なのですぐに Level 15 が見つかる。すぐに謎は解けると思うのでこれ以上は解説はしない。

定番になってしまった JDeveloper10g for Macでjava をdeploy
jdev

そして、java stored functionとしてラップして実行。(パッケージ化しているが管理しやすくするためである。パッケージ化しなくても問題はない) ユーザ定義関数として利用できるのでSQL文から直接利用できる。(でもディレクトリオブジェクトぐらいは引数で渡すようにしたほうがよかったかも。。)
result


ただ、正解の画像にある赤いドットが気になったので遊んでみた。(画像は小さいので拡大してみないと判りずらいと思う)このURLを叩けばその絵は間違っているとわざわざ教えてくれるのだが、こんな引っかけに引っかかるるのかな?〜(引っかかっている間もなく答えが判ってしまったのに。)




Level 15へつづく。

| | | コメント (0) | トラックバック (0)

2005年9月30日 (金)

PL/SQL で Python Challenge 14 Eureka!

Eureka!
Level 14 は、ヒントを見て仕掛けが判ってしまうほど簡単なのだが、プログラムがバグっていて答えがきれいに描画できていない。ただ、それが何かは判るような状態なのでURLを叩くと、正解! でした。 
完結編へつづく。(プログラムを修正してからだが。。)

| | | コメント (0) | トラックバック (0)

2005年9月11日 (日)

PL/SQL で Python Challenge 14 またグラフィック系か!

Level 14は、また画像処理系の問題みたいだ。なんとなく答えが見えたのだが。。。どうしよう、Oracle10g R1 for MacOSX Serverを無理矢理インストールしたTigerの環境では、java stored procedureが動作しない。Oracle10g R2もリリースされたが、今のところLinux X86, AIX, Solaris, HP-UX, Windowsがリリースされたが、MacOSXはまだだ。。。R2がリリースされるまで待つか。。。。。

| | | コメント (0) | トラックバック (0)

2005年9月10日 (土)

PL/SQL で Python Challenge 13 完結編

Level 13 は Level11Level 12の画像系問題ではなかったのでちょいと面白い。
簡単だしね。Level12 でセサミストリートまで辿り着いていればの話だが。。

Level 13 は、Level 12の解決には余分な情報が”伏線”となっている。

その伏線を Level 12で見ていれば Level 13 は 70%くらいまで解決しているのだ!。
そして、Level 13ではちょいといじわるな?! 罠がある。私は問題がバグっているのか、サイトがトラブルでも起こしているのかと思い、その翌日に再度アクセスしてみたくらいであった。
しかし、そのエラーメッセージこそが最大のヒントなのである。それに気づけばあとはプログラミングあるのみ! なのである。


前回も書いたがこの問題はPL/SQLだけで解ける。簡単に書くつもりだったが作っているうちにちょっとだけ凝ることにした。そのため全体では40行程度のファンクションとなった。



そのファンクションはSQL文から実行するようにした。4回発行だけで解けるので見かけ上は、Python を利用して4行で解くのとあまり変わらない(ソースコードの量は随分違うが。。。笑。)

プログラムは簡単なのでコードの欠片も載せないが、UTL_HTTPパッケージと、___type型を使う(伏せ文字のところはこの問題でどんなプログラミングをすればよいか見えた方ならすぐ判るはず!。)

level13

Pythonで解くと。。。
python13




今回は、これだけでは終わらない。最後の最後まで”いじわる”な問題である。(面白いけどね)

PL/SQL で Python Challenge 14 へつづく。



Python Challengeの問題の意地悪で思い出したが、その昔、ログインのバナーにクラッシュダンプようなバナーを表示するようにして、ログインした方を慌てさせるというおバカなことやっていたことを思い出した。

| | | コメント (0) | トラックバック (0)

2005年9月 9日 (金)

PL/SQL で Python Challenge 13 謎は解けた!

Python Challenge Level 13  
画像系の問題が連続していたが今回は違うね。Oracleさんも力を入れている分野だけどJavaを使わなくてもPL/SQLのパッケージだけで出来るな。凝ればかっこ良くつくれるが一番単純なプグラミングでやっつけようと思っている。恐らくPL/SQLでも10行程度でできるでと思う。(ちなみに、Python なら Global Module Index にあるモジュールをimportすれば4行で答えが得られる。)

次回、 Level 13 完結編につづく。

| | | コメント (0) | トラックバック (0)

2005年9月 7日 (水)

PL/SQL で Python Challenge 12 完結編

この問題、プログラミングは簡単なんだがそこに辿り着くまでが大変だった。

Level 12のページには画像が1つあるだけ。ページのタイトルも、ヒントなのか、そうではないのか意味不明。


Level 11 と同じなのか?(Level 11と同じな訳ないのだが。。)と感じさせるような画像なのである。しかし、なんのことはない、普通のjpgファイルだったのである。

一体どうすればいいのか?

どこに答えが隠れているのか???? ーー> 一週間悩んだ。


ちょっとしたことから仕掛けに気がついたのだが、これが、問題作成者の狙い通り!? という罠にハマり、セサミストリートまで行くことになる。(さらに、3日悩み続けた。。。)


セサミストリートまで行っちゃうと行き過ぎみたいなのだが、それはそれで何かの伏線なのか? と思える実に怪しいページになっている。それは置いといて。。

そこに辿り着くまでにいくつかのアイテムを拾い集めていた、そのアイテムの一つに、”ほんとうの問題”があったのだ。

それをPL/SQLで解いた所。PL/SQLのどのような機能を利用したかは前回の記事を見てもらうとして。。。


l12


あとはご自分で、といっても感のいい方は答えに気付いてしまうかもしれない。

尚、今回は、PowerBook G4, MacOS X Tigerに構築した Oracle10g R1 EEを利用している。
Level 13 へつづく。

| | | コメント (0) | トラックバック (0)

2005年9月 5日 (月)

PL/SQL で Python Challenge 12 やっぱり謎は解けた!

前回、謎は解けた! かもしれないと書いたが、実は.....。

謎を解いている私の横で奥さんがそれを検証し始めたのだ。 その結果、私の推測は間違っていると指摘されてしまった! (><)。      
ということで、今日のランチタイムに弁当を食べながら問題と睨めっこしていた。そして見えました〜〜答えが!。 自分なりに検証したところPL/SQLだけで解けたのであった。ちょっとだけ解説しておくと、 DBMS_LOBパッケージ、UTL_RAWパッケージ、UTL_FILEパッケージ及びBILEといつものディレクトリオブジェクトなどを利用するのみで全体で40行〜50行程度になっただろうか。

今回の問題は、いろいろな”いじわる”がたっぷり盛り込まれている。 答えが判れば、Level 12のページにある画像を見て、なるほどね〜〜。という気になってくるから不思議である。
次回、PL/SQL で Python Challenge 12 完結編につづく。

| | | コメント (0) | トラックバック (0)

2005年9月 4日 (日)

PL/SQL で Python Challenge 12 謎は解けた! かもしれない。

PL/SQL で Python Challenge Level 12


実は先週末から本格的にやり始めたのだが、これが私には難解で、難解で、、、記事のネタにもならないのと、仕事の納期が迫っていたため投稿は控えていた。 でも、先ほどから始まった雷の閃光とともに、謎はとけた! 。。。と思う。(ちょいと不安だが。。。。)

おそらく、PL/SQLだけで解けるのではないかと思っている。(私の推測が間違っていなければ。。。)  

次回へつづく。(話は変わるが、外はすごい雨になっているね〜。)

| | | コメント (0) | トラックバック (0)

2005年8月18日 (木)

PL/SQL で Python Challenge 11 完結編

Level11は、 Level7及び、Level9で作成したパッケージを再利用したことと、、Level7で作成したjavaのコードを多少手直ししただけなので書き直したコードは15行程度だった。(全体では、Level7程度のコード量だが。。)

javaのコードの概要は以下のようになる。

    1.問題の画像を、オラクルのDirectoryオブジェクトに置く。

    2.PL/SQL で Python Challenge Level7で作成した、PL/SQLパッケージを再利用。Level7.getBfile()を利用して、1.説明した画像ファイルを外部LOBファイルとして取り込む。

    3.javax.ImageIO をクラス利用して、外部LOBとして読み込んだ画像の複製を作る。(複製した画像は、背景色を黒にしておく)

    4.3.で複製した画像(BLOB)に、2.で取り込んだ画像(外部LOB)から取り出した答えを描画。

    5.答えを描画した画像(BLOB)を一時LOBとしてデータベースへ書き出す。 dbms_lob.createTemporary() を利用する。

    6.一時LOBを PL/SQL で Python Challenge Level9 で作成した Level9.writeImageFile() プロシージャを利用して、オラクルのDirectoryオブジェクト以下に、level11.jpg というファイル名で書き出す。

    7.前述のjavaクラスをデータベースへ配布する。(JDeveloper又は、loadjava コマンドを利用する。)


jdev_deploy


    8.SQL*Plusより、Java Stored Procedureとして呼び出せるようにして実行!。Oracleは、MacOSX 10.3.9 Pahtnerに作成した、Oracle10g である。

cube: oracle$ sqlplus scott/tiger

SQL*Plus: Release 10.1.0.3.0 - Production on 水 8月 17 00:00:20 2005

Copyright (c) 1982, 2004, Oracle. All rights reserved.



Oracle Database 10g Enterprise Edition Release 10.1.0.3.0 - Production
With the Partitioning, OLAP and Data Mining options
に接続されました。
SQL> !cat level11.sql
-- level11
create or replace package level11 as
function getAnswer return number;
end level11;
/
show error

create or replace package body level11 as
function getAnswer return number
is
language java
name 'jp.discus.Level11.getAnswer() return java.lang.Intger';

end level11;
/
show errors

SQL> @level11

パッケージが作成されました。

エラーはありません。

パッケージ本体が作成されました。

エラーはありません。

SQL> set timi on
SQL> select
2 case
3 when level11.getanswer() = 1 then
4 '成功'
5 else
6 '失敗'
7 end as "結果"
8 from
9 dual;

結果
----
成功

経過: 00:00:05.68
SQL>


実行後、directoryオブジェクトに対応するフォルダには Level11.jpg が作成される。

dir

    9.うまくいったようなので結果を見てみよう! (内容はいつものように隠してある)

level11_copy


Level12 へつづく

| | | コメント (0) | トラックバック (0)

2005年8月15日 (月)

PL/SQL で Python Challenge 11 謎は解けた。

前回の予想通り、画像の中に答えがあった。Level11 の ページタイトルがヒントになっている簡単な問題でしたねこれも。(Level11はJavaだけで答えが見えてしまうのだが、、、Level7を解いたときと同じ方法で、PL/SQL から呼び出す形にする。) 次回、完結編へつづく。



何年ぶりだろう!? 東京湾大華火祭を見たのは・・・・・。 

Oracle, PL/SQL, Mac, Java, PL/SQL で Python Challenge | | | コメント (0) | トラックバック (0)

2005年8月11日 (木)

PL/SQL で Python Challenge 11

ここのところ忙しくて脳の体操してなかったが久々に再開?!、Python Challenge Level11。
こいつはまた画像に答えが埋め込まれてますね〜おそらく。。。となんとなく思っただけ。。で、疲れてやる気でず。。。

| | | コメント (0) | トラックバック (0)

2005年7月29日 (金)

PL/SQL で Python Challenge 10 完結編

Level10は、前回の通りPL/SQLパッケージだけで解けた。珈琲ブレイクの間にコーディングしたのだが、すんなりコンパイルも通りLEVEL10クリア。。

いつものようにコードは載せないが、コレクションの一つであるVARRAY、正規表現関数(REGEXP_SUBSTR()とREGEXP_REPLACE())、あとは昔からある、SUBSTR()関数や、LENGTH()関数を利用したプログラミングで十分。

結果の画像を見てもらうと判るがパッケージの本体は35行である。Level5以降キツい問題が続いたが、このくらいだとPL/SQLで解くのもランチタイムや珈琲ブレイクの間で終わるので十分楽しめる。


level10_source

level10_res



Level11へつづく。。。のだが、Tiger Server De Oracle10g は? と突っ込まれたこともあり、次回はMac De Oracleらしく、Tiger Server De Oracle10gにする予定。

| | | コメント (0) | トラックバック (0)

2005年7月28日 (木)

PL/SQL で Python Challenge 10 謎は解けた

Level10は、こんな感じの適性試験があったような記憶が蘇るクイズだな。。。。

Level10では、Level9の答えに出てくる”やつ”が問題のページにあるのだ。。htmlソースをみると、、お〜、 clickable mapじゃないか! またなんか嫌らしい問題にしてんのかな〜。
早速、”やつ”をクリックすると基本認証が設定してある。おや〜〜これは以前解いた問題と同じだな。。。ということで、やっと問題にたどり着く。入手したテキストファイルが意味不明だったが、眺めているとあるパターンがあることに気づく。あ、、、、なるほど。。。謎は解けた!  Level10のページには答えを導きだすためのヒントがあるので、あとはその通りにやればいいはずだ。(最後にひねりが何段かある可能性はあるが。。)
あとは、PL/SQLでどうプログラミングするかだな。Level10は、久々に、PL/SQLだけで解けそうな感じがする。 
 Level 10 完結編につづく。


軽井沢に行きたくなってきた。。。暑い、寝苦しい〜。

| | | コメント (0) | トラックバック (0)

2005年7月27日 (水)

PL/SQL で Python Challenge 9 完結編

Python Challenge Level9 を画像処理(Java Stored Procedure)、その他(PL/SQL stored procedure)とOracle10gの機能で解いた。


方法の概略は昨日の記事を見てもらいたい。

Oracleのdirectory オブジェクトに対応づけられたOS上のフォルダ(ディレクトリ)には、以下のスナップショットのようにlevel9.jpg というLevel9の答えが作成されている。(このlevel9.jpg は、最初からあるわけでなく、Level9を解いた結果作成された画像である) 

(この機能自体がサポートされたのはかなり前なのだが、create directoy文だけでOS上のディレクトリにアクセスできるようになったのは、Oracle9i R2以降である)
result_dir


level9 という PL/SQL パッケージを作成したのだが、今回はpackageの仕様部だけをネタバレになりそうな関数名などをHogeなどと置換して公開しておこう。


最初の create type文で、ユーザ定義型のコレクションを2つ定義している。見ればわかると思うが、hogehoge_typ型は2次元のコレクションになっている。また、hoge_typ型が、2つのintegerを要素とするコレクションであることも判ると思う。感のよい方はこれを見ただけでもLevel9の答えが判ってしまったかもしれない。。(前日の記事で、「答えを画像ファイルに出力する必要ない。」と書いた理由はこれである) 

ではpackage仕様部の解説に移ろう。
最初の2つは、定数なので解説は省略する。getHoge()関数は、単純に2次元のnest tableコレクションを返してくるものである。画像ファイルに答えを描画しないのであればここまでできれば十分である。

私は答えをjpegファイルに出力するので、メモリ上で作成した画像をjpegとして一時LOBとしてOracle10gデータベースの一時記憶域に書き出し、その一時LOBを directoryオブジェクト以下に level9.jpg というファイルとして書き出す処理を作成した。

writeImageFile()プロシージャは、PL/SQLだけで記述してある。プロシージャ名からもある程度想像できると思うが、一時LOBを、指定した directoryオブジェクト以下に指定したファイル名(level9.jpg)で書き出すプロシージャである。(コード量は20行程度) 残りの getAnswer()関数はLevel9を解くためのメイン処理になっおり java stored procedure である。java では画像処理だけを行いそれ以外の処理は、PL/SQLを利用している。(javaのコード量は、jdbcを利用していることもありコメント以外の行は100行程度)
$ cat level9.sql
-- Level9 package
create or replace type hoge_typ is varray(2) of integer;
/
create or replace type hogehoge_typ is table of hoge_typ;
/
create or replace package level9 as
c_first constant varchar2(16) := 'first.txt';
c_second constant varchar2(16) := 'second.txt';
function gethoge(which in varchar2) return hogehoge_typ;
procedure writeimagefile(
directoryname in varchar2,
filename in varchar2,
srcimage in out nocopy blob
);
function getanswer() return boolean;
end level9;
/
show error


コーディング後のloadjavaは、JDeveloper10g for MacOSX Developer Previewを利用している。

jdev10g_loadjava

SQL*Plusを起動し、level9のパッケージや型を作成後、getAnswer()の実行!
level9_exec

注)いつもの通りネタバレになるので答えそのものは載せていないのであしからず!(そうだと思った〜っ。という声が聞こえてきそうだが。。)
level9_result
画像を開くとなるほど〜〜〜と思う結果になるのだが、日本人だと50%は間違かもしれない。特に日本語では通常、”それ”を区別していないからだ。(画像をよ〜〜〜く見ると区別できる”もの”があるのだが・・) でも心配しなくてよい。間違っている方の xxx.htmlを入力すると正しいURLへのヒントのページが表示される。(いつの間にか、英語の勉強にもなっている気がする Python Challenge )


Level10 へつづく!

尚、nest tableやVARRAY又は、結合配列(むかしのPL/SQL表相当)を関数の戻り値として返すサンプルやBLOBをdirectoryオブジェクト以下に書き出すPL/SQLの簡単なサンプルは別途掲載するかもしれない。

20050727001台風一過、波は高いが気持ちのいい湘南にて。。

| | | コメント (0) | トラックバック (0)

2005年7月26日 (火)

PL/SQL で Python Challenge 9 簡単でした

Level9。 最初はそのページにある画像に疑いを持っていたのだが、htmlソースと、ページタイトルで謎は解けた。 Pythonだと、画像処理用のモジュールをimportすれば20行も必要ないだろう。 PL/SQL+Java(Java Stored Procedure) + Oracle10g では、PL/SQLのコードとJavaのコードを合わせて、100行で収まるかな?といったところ。 今回も、MacOS X Panther 10.3.9に構築した、Oracle10gデータベースと、JDeveloper10g for MacOSX Developer Preview Releaseそして、J2SE 1.4を利用して解いてみる。ただ、この問題では解答をjpegファイルで出力することにした。(ちなみに、画像ファイルにしなくても問題ないと思う)

さて、処理方法は、

Level9のhtmlソースをファイルに保存後、答えを導きだすためのデータ群(2つある)をそれぞれテキストファイル(以下、テキストファイル1、テキストファイル2と呼ぶことにする)にして、Oracle の directory オブジェクトに対応する OS上のフォルダに保存する。さらに、Level9 のページに表示されている画像( good.jpg ) も同じ場所に保存する。
ちなみに、directory オブジェクトには、書き込み権限と読み込み権限が付与されていること。


最初に、VARRAYまたは、NEST TABLEをを利用した2次元のユーザ定義型を create type文で作成する。私は1次元目のコレクションは、NEST TABLE、2次元目のコレクションには、VARRAYを利用した。

テキストファイル1と2をutl_fileパッケージを利用して読み込み必要な形に変換する関数を作成する。この関数はPL/SQLだけで記述する。正規表現関数と前述の2次元コレクションも利用する。

good.jpgファイルをBFILEとして返す関数を作成(ただし、Level7で作成したものをそのまま再利用)。これもPL/SQLだけである。utl_fileパッケージを利用している。なぜ、good.jpgを読み込むのかといえば、答えを描画する画像のサイズを good.jpgと同じにしたいということが理由である。答えとなる画像は、黒背景に白で答えを描画する。

次のプロシージャが大事なのだが、BLOB を directory オブジェクト に書き出すプロシージャを作成する。このプロシージャは、DBMS_LOBパッケージ及び、UTLFILEパッケージを利用する。PL/SQLだけで記述する。このプロシージャで答えを描画したjpegファイルを Oracle管理下の directory オブジェクトに書き出すという仕掛けである。

最後に、java stored procedureなのだが、このプロシージャに対応する java classでは

前述したPL/SQL関数を使って、問題のデータ(テキストファイル1と2及び、 good.jpgファイル)を読み込み、”答えをjpegファイル(BLOB)として作成する。


作成したjpegファイル(BLOB)を前述したPL/SQLのプロシージャを利用して、directoryオブジェクト以下に書き出す。(DBMS_LOBパッケージを利用し、一時LOBにjpegを仮作成。仮作成した一時LOBをjpegファイルとして書き出すという方法をとる)

思いついた方法を書いただけなのだが、これでうまく解けると思う。(画像処理については、PL/SQLだけでは無理なので、その部分を Javaで行う)

Level9 完結編へつづく。。。

 あ、そういえば、 PowerMac G5 Dual 2.7Ghzが到着して、 MacOS X Server Tiger 10.4.2 はセットアップできたので、 そろそろ  Tiger Server De Oracle10g ( Oracle10g が Tiger未対応なので無理矢理構築するのだが。。。) をやりますか。。。。

| | | コメント (0) | トラックバック (0)

2005年7月25日 (月)

PL/SQL で Python Challenge 8完結編

Python challenge Level8 を解いてみた。 PL/SQLJavaOracleを使って解くと結構大変なことも多々あるなぁ。。。それを承知でやっているのではあるが。。。(^^;;;

Python challenge Level8のヒントは前回の通り、「ブンブン蜂が飛ぶ。その音」(もちろん英語で)。

Python Global Module Indexにあるどのモジュールを利用するのかがわかれば簡単で、Pythonでは3行で解けてしまった。
しかし、PL/SQL+Java+Oracle10g では手こずった。プログラミング以前に、PL/SQLJava(J2SE)にもPythonのモジュールに該当するものがないのである。
検索したところ、Apache Antプロジェクトに同じ機能を持つpackageがあることを発見した。(Ant全体ではなく該当部分だけが jar として公開されていたのでそれを利用することにした。)
そのページのNOTE部分には大事なことが書いてある。それを読まないで該当packageを利用するとハマるので注意。(私はまる一日ハマってしまった)ネタバレになるので、そのページのURLは書かない。自分で探してもらいたい。

今回は、以下のように level8 というPL/SQLパッケージを作成した。(Level7と同様に、MacOS X Panther 10.3.9にインストールしたOracle10g 10.1.0.3 を利用している

SQL> desc level8
FUNCTION GETANSWER RETURNS VARCHAR2
FUNCTION GETPS RETURNS VARCHAR2
FUNCTION GETBLOBSOURCE RETURNS BLOB
引数名 タイプ In/Out Default?
------------------------------ ----------------------- ------ --------
SOURCE VARCHAR2 IN
FUNCTION GETUN RETURNS VARCHAR2

SQL>

PL/SQLのコードはネタバレになるのでいつもの通り非公開だが、作成手順やネタバレにならない程度の概要は書いておく。

getUn() 関数と getPs() 関数は、Level8の問題そのものを取得するPL/SQLだけで記述した関数である。長い文字列なので入力の手間を省く為にPL/SQL package中のプライベート定数としてあり、その文字列を取得するだけの関数である。。
コード量は、それぞれ数行程度だった。


getBlobSource() 関数は、getUn()関数、getPs()関数の戻り値を入力して、加工後、BLOBとして返す。これもPL/SQLだけで記述した関数である。どんな加工を行ったのか詳細は書かないが
エスケープシーケンスを含む文字列を処理したとだけ書いておく。Oracleでは特定の関数などで
エスケープシーケンスを利用するものがあるが、それ以外では通常の文字列として扱われるのでこの処理がポイントになるのではないかと思う。Oracle10g新機能である正規表現関数はすべて利用した。
コード量は、20行程度だった。


getAnswer() 関数は、getBlobSource()関数を呼び出し、戻り値から解答を取り出だすjava stored function としてある。この関数はラップされている method内で、Apache ant
に含まれているあるpackage ( jar ファイルは該当package分だけ入手可能 )を利用している。
javaのコード量は、60行〜70行程度だった。


手順も書いておく。
  • 1.入手したjarファイル(Apache antの該当packageだけの jar)を loadjava コマンドを利用して、Oracleにロードする。 

  • 例) jarファイル名などは隠してあります。(それを見せたらネタバレなので)
    cube: oracle$ loadjava -v -user scott/tiger xxxxx.jar
    arguments: '-v' '-user' 'scott/tiger' 'xxxxx.jar'
    identical: META-INF/MANIFEST.MF
    identical: org/apache/xxxxxxxxxxxxxxxxxxxxxxxxxxxx
    identical: org/apache/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    identical: org/apache/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    identical: org/apache/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    identical: org/apache/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    identical: org/apache/xxxxxxxxxxxxxxxxx
    identical: META-INF/LICENSE.txt
    cube: oracle$

  • 2.getUn()、getPs()、getBlobSource()までをPL/SQLだけで実する。

  • 3.getAnswer()でラップする java classを実装及びテストする。

  •  1のjarファイルはライブラリとして必要。(このclassで、getBlobSource()を jdbcを利用して実行する)。 

  • 4.3で作成した classを loadjavaまたは、JDeveloper10g Developer Preview for MacOSXで、データベースにロードする。
  • level8_jdev

    以下、実行結果のスナップショット(いつものように答えは隠してある)。
     level8




    Level9へつづく。

    | | | コメント (0) | トラックバック (0)

    2005年7月20日 (水)

    PL/SQL で Python Challenge 8

    Level8は、非常に簡単! Pythonでは!

    Pythonなら3行程度で終わりそうな問題だ。しかし、PL/SQL+Javaでやるにはちょいと面倒かもしれない。いくつかポイントはあるのだが、escapeシーケンスと”ブンブンブン”蜂が飛ぶ〜〜がヒントになるpython global moduleだ。javaのJ2SEにはそのようなパッケージはないし、もちろん、PL/SQLやオラクルにはない。javaだと、apache ant に ”それ” に相当するパッケージがあるのだが使えるのかな?  調べるしかない・・・。

    | | | コメント (0) | トラックバック (0)

    2005年7月19日 (火)

    PL/SQL で Python Challenge 7 再チャレンジ!

    前回、「PL/SQL で Python Challenge 7 残念!」 でPL/SQL+Oracle10gで解くことを断念するつもりだった。しかし、Python Challengeの問題自体の面白さに、再開することにしたのだ!。
    再開するにあたり、PL/SQLでは面倒な部分(例えば、画像処理とか)だけを、Java (Java stored procedure)で解くことにした。(場合によっては、Javaで解く部分がクイズの核心だったりするが・・・)




    java stored procedureを利用したことで、Level7は簡単に解けた。それなりにひねりの効いた問題だったが、java stored procedureを利用した箇所以外は、Level7以前の問題を解く中で利用してきた機能や関数で十分解ける問題だった。

    Level7でもいままで通り答えをそのまま書くつもりはなく、PL/SQL と Java そして Oracle10g をどのように利用して解いたのかという所だけをヒントとして書いておく。
    Level7は画像自体に答えが隠されている。(Level7のページに行けば、なんとも怪しい画像にお目にかかるので感のよい方はすぐに気づくと思う)。そこで問題になるのは、PL/SQL や Oracle10g で提供されているパッケージや機能では画像データをピクセル単位で扱うことは今のところできないということなのである。その問題を解決する手段として Java を利用することにした。

    注)2005/07/17時点では、MacOSX Tiger 10.4.1において、無理矢理動作するようにしたOracle10gではエラーが発生する。以下はすべて、MacOSX Panther 10.3.9のOracle10gで行った結果である。

    1.問題の画像をOracle10g の directoryオブジェクト以下に配置し、BFILE として扱う。

    (BFILEは、外部LOBと呼ばれ、Oracleデータベース内には格納されないLOBである。)
    BFILEへの参照(ロケータ)を取得するのは、PL/SQLだけで可能である。
    Level7では、directoryオブジェクトへの読み込み権限さえあれば問題ないが今後のために書き込み権限を付与している。(実行ログの書き出しに利用する場合を考えている)
    SQL> conn scott/tiger
    SQL> select table_name as "directory",privilege from user_tab_privs;

    directory PRIVILEGE
    ------------------------------ ----------------------------------------
    PYTHON_CHALLENGE_DIR WRITE
    PYTHON_CHALLENGE_DIR READ

    2.1で取得した BFILE から画像データとして読み込み怪しい部分の画像情報から答えの元になる情報を取り出す。

    (なぜ元になる情報なのか?。”ひねりの効いたクイズ”なので一筋縄では答えは導きだせないだけ!)
    ここで、Java (実際には Java stored procedure にするのだが)を利用する。


    Java stored procedure といっても通常の java プログラムとほとんど同じなのだが、Java stored procedureとして利用する場合にはいくつかの作法が必要でなのだ。methodは、staticにする必要がある点と、利用するJDBCドライバは通常は、Server-side thin ドライバを利用する点である。
    このドライバは、 thinドライバと同じ機能を持っているが接続先データベースがjava stored procedureが格納されているデータベースになる点が異なる。
    Java stored procedure が格納されているデータベース以外のデータベースに接続する必要がある場合には、通常のthinドライバを利用することになる。
    今回は、Java stored procedureの格納されているデータベースだけにしかアクセスしないのでServer-side thin ドライバを利用する。(単体テストの時は、thinドライバを利用し、テスト完了後に Server-side thinドライバへ切り替えた)
    尚、Java stored procedureの詳細は、OTNのオンラインマニュアルなどを参照してもらいたい。

    3.2で取得した”答えの元になる情報”から答えを取り出す。これは PL/SQL だけで可能である。

    以下に、上記1〜3の処理をそれぞれ関数として作成した PL/SQL パッケージ(level7パッケージ)のapiを示す。
  • getBFile() が、BFILEへのロケータを返す関数である。
  • getAnswer() が、java stored procedureである。画像データを読み込み”答えの元ネタ”を返す関数である。
  • getNextLevelUrl() が、Level8へのURLを返す関数である。
  • 注)本来であれば、getNextLevelUrl()だけを公開すればよいのであるがデバッグのために全関数を公開してある。
    SQL> 
    SQL> desc level7
    FUNCTION GETANSWER RETURNS VARCHAR2
    引数名    タイプ In/Out Default?
    ------------------------------ ----------------------- ------ --------
    DIRECTORYNAME VARCHAR2 IN
    FILENAME VARCHAR2 IN
    FUNCTION GETBFILE RETURNS BINARY FILE LOB
    引数名    タイプ In/Out Default?
    ------------------------------ ----------------------- ------ --------
    DIRECTORYNAME VARCHAR2 IN
    FILENAME VARCHAR2 IN
    FUNCTION GETNEXTLEVELURL RETURNS VARCHAR2
    引数名    タイプ In/Out Default?
    ------------------------------ ----------------------- ------ --------
    DIRECTORYNAME VARCHAR2 IN
    FILENAME VARCHAR2 IN

    後は作成した stored procedure を実行するだけである。今回は関数(スカラ関数)にしてあるので SQL から呼び出して実行している。また、java stored procedure に不慣れな方の為に手順も載せておく。

    1)OS上で、適当な位置にディレクトリを作成する。

    この時、oracle所有者が、ディレクリに対する書き込みおよび、読み込みパーミッションを持っているか確認すること。また、画像ファイルも配置しておく。尚、外部LOBや、directory オブジェクトにマッピングされるOS上のディレクトリが実在するかどうかは
    Oracle側では検証されない。利用する側で適切な関数などを呼び出してチェックする必要がある。

    2)SQL*Plus を起動し、1)で作成したOS上のパスを対象とする directory オブジェクトを作成する。

    3)2)で作成したdirectory オブジェクトへの read権限、 write権限を付与する。

    例)SQL> grant read, write on directory python_challenge_dir to scott;

    4)level7.getBfile() だけを作成しておく。

    5)level7.getAnswer() に対応づける Java program を作成する。

    java program の作成には、JDeveloper10g for MacOSX Preview Releaseを利用した。
    jev10gicon jdev10gpre1

    6)5)で作成した Java programを Oracle10g の scottスキーマへ配布する。

    loadjava コマンドを利用するか、JDeveloperを利用する。
    配布にも、JDeveloper10g for MacOSX Preview Releaseを利用した。
    jdev10gpre2

    7)level7パッケージを作成する。


    getAnswer()  の実装。6)で配布したclassを利用する java stored functionにする。

    getNextLevelUrl()  の実装。ここでは、これまでの問題でも利用したOracle10g新機能(Pythonなら re で判るはず!)を利用するのである。

    8)実行。 スカラ関数として利用できるようにしておいたのでSQLから実行した!

    ネタバレになるので、いつものように画像はぼかしてあります。 m(_ _)m
    level7_answer

    ということで、再開した PL/SQL で Python Challenge。  Level8 へつづく。

    海の日。梅雨も開けた湘南にて (どこでしょう?)

     

    CIMG3851
    CIMG3856

    | | | コメント (0) | トラックバック (0)

    2005年7月15日 (金)

    PL/SQL で Python Challenge 7 残念!

    ついに、その時がきたか〜。Level7の謎はそのページにある画像自体にある。PL/SQLでは無理そうだな〜。Pythonだと、20行以内で書けそうなのだが・・・。(クイズ自体は割と簡単なだと思う)ということで、PL/SQLではLevel6までということにしておこう。 (Pythonではやるかもしれない・・・・。)

    | | | コメント (0) | トラックバック (0)

    2005年7月14日 (木)

    PL/SQL で Python Challenge 6

    Level5で格闘したおかげで、Level6は簡単に解けてしまった。

    Python Challengeを解く鍵になる最初のヒントは、問題になっているhtmlのページ自体に隠されている。大抵の場合、htmlソースコードや画像などにあるのだが、それなりにひねってある。とうしても判らない場合は、Forumのヒントを見てみるといいだろう。Level6はその必要は全くないと思うが・・・
    Python用に考えられたプログラミングクイズなので、PL/SQLで解くには難しい場面や、アプローチを変える必要が出てくる。Level6は、ある形式のファイルから答えを導きださなければならないのだが、PL/SQLではそれを直接操作するパッケージなどは存在しない(作ればできると思うが、すごく大変でしょうね)。しかし、そのファイルの中身をそのままの形で見れば扱えなくもないということに気づくと思う。
    今回利用したパッケージは、UTL_FILEパッケージ、正規表現関数(Oracle10g新機能)、DBMS_OUTPUTパッケージ(結果の表示に利用)、連想配列(日本語のマニュアルでは結合配列とか書いてあるが、英文マニュアルを直訳すると”連想配列”となっている。結合配列より連想配列のほうが判りやすいと思うのだが・・・・)だけである。書き方によると思うが約80行程度だった。
    さて、Level 7 はPL/SQLで解けるだろうか? Levelが進むとPL/SQLにはキツくなるね〜やはり・・・・。頭の体操にはいいかもしれないけど・・・。
    Level5の時と同じように大事なところは隠している実行結果のスナップショット。。。見せないんだから載せなくてもいいのだが・・・・。
    level6

    | | | コメント (0) | トラックバック (0)

    2005年7月13日 (水)

    PL/SQL で Python Challenge 5 完結編

    ついにPL/SQLで、Python Chellenge Level5を 解いた!  PL/SQLで解くためのヒント(ヒントより解答に近いかもしれないが。。)

    謎は解けたが、PL/SQLでは、1つ問題になることがある。PL/SQLがオブジェクト指向的な拡張はされているが、オブジェクト指向言語ではないからだ。一時はあきらめかけたのだが、Pythonを使って解いてみると、オブジェクト指向言語でなくても解けそうな問題であることに気づく。

    そこで、Pythonのソースコードや、仕様を決めているPEPを見始める。(PEPは、javaでいえば、JSRみたいなもの)

    大きなヒントになってしまうが、PEP307のソースコードを探し始めたのである。その中で、RubyでPython Chellengeを解いている方のページを発見した。RubyでPEP307の一部(Level5を解くためだけに必要な部分)を実装したコードを発見したのだ。おおお〜〜〜。これをPL/SQLで実装すればいいじゃん!



    PL/SQLで解くためのヒントは、上記のコードを解析してPL/SQLで書き上げろ。しかない! 

    ポイントは、ANYDATA型及び、VARCHAR2型をネスト表を利用してユーザ定義型を作成する(ようするに、ANYDATA型とVARCHAR2型のコレクション型を定義するとうこと)。それらを、Rubyのコードにある@stackや@memoの代用として利用する。ANYDATA型のコレクションは、コレクションの各要素がANYDATA型になるのでどんな型でも格納できるのである。しがたって、ANYDATA型コレクションの要素にANYDATA型コレクションを格納し、さらに、その要素に。。。を繰り返すという操作が可能になる。最終的には、VARCHAR2型コレクションを格納してやれば今回の問題の80%はクリアできるはず。残りの部分は、UTL_FILEパッケージを利用してファイルを読み込む。結果もUTL_FILEで書き出せるが、SQL*Plusで確認したい方は、DBMS_OUTPUTパッケージを利用して表示させてもよい。(私はDBMS_OUTPUTパッケージを利用した。)その場合、set serveroutput on size xxxx に続けて、スペース表示を抑止しないオプションを付加することをお忘れなく!(あえて、オプションが何かは書きません! SQL*Plusのマニュアルを読んでくださいね)

    ちなみに、この問題、PL/SQLでは、stackなどのロジックを書く必要があるため、PythonやRubyのように10行から20行程度のコードでは解決できない。かなりベタに書いてしまったこともあり、stackおよび、PEP307の一部対応で180行程度、全体で200行程度になった。そこまでして、PL/SQLで解きたいか〜という声が聞こえてきそうだが、ANYDATA型の利用方法が見えてきたのは収穫だった。(^^

    以下のスナップショットではぼかしを入れてます。これを見せちゃったらつまらないからね〜。
    finished


    Python Challenge Level 6につづく。(PL/SQLだけでできるのかな?〜)

    | | | コメント (0) | トラックバック (0)

    2005年7月 5日 (火)

    PL/SQL で Python Challenge 5 謎は解けたが。 迷走。

    Level 5 謎は解けたものの、PL/SQLの新機能などや、Oracle8i以降で追加されたおオブジェクト指向拡張(ネスト表くらいしかまだ利用したことがない)を再度、読み返してみると、anydata型などが気になり始めた。最初は、単純にPL/SQL表でもいけるかと思っていたが、ネスト表やオブジェクト型などを利用したほうがいいのかもしれないと思い始める。。。ただいま、迷走中!
    どうせ、Oracle10gのPL/SQLでやるのなら、PL/SQLのオブジェクト指向機能機能拡張部分でも利用してみますか・・・・。

    | | | コメント (0) | トラックバック (0)

    2005年6月29日 (水)

    PL/SQL で  Python Challenge 5 謎は解けた!

    Python Challenge Level5をPL/SQLで解けるのか?。。。。とりあえずPythonでやってみたら、5〜7行程度だ!。 PL/SQLでは、該当する関数もパッケージも無いのだが、なんとかできそうなので意地でもPL/SQLで解いてやる〜〜〜。しかし10〜20行くらいではコーディングできないのは間違いない。

    Level 5を解く鍵になるモジュールと同じ機能を持たせれば、オブジェクト指向ではない言語でもなんとかできる問題になっていた。PL/SQLでこれを解くには、やはり、UTL_FILEパッケージ+Level 5を解くための鍵になるモジュール相当の自作パッケージ+DBMS_OUTPUTパッケージこれらを合わせればできると思う。。。では、結果にご期待ください。

    | | | コメント (0) | トラックバック (0)

    2005年6月28日 (火)

    PL/SQL で Python Challenge 5

    う〜〜〜ん。Python Challenge Level5 で、ついに終了か! (Python なら簡単。でも、PL/SQLではきついな〜〜)。 でも、もうちょい調べてみよう。

    | | | コメント (0) | トラックバック (0)

    PL/SQL で Python Challenge 4 その2

    Python Challenge Level4 を PL/SQLで解くためのヒント。

    前回、level4を解く前に、Python向けに書かれたクイズをPL/SQLでどう解くか考えていたが、やはり、utl_httpパッケージが利用できた。それと、Oracle10gから新たにサポートされた正規表現を扱う関数だ。

    Level4は、utl_httpパッケージとOracle10gからサポートされた正規表現を扱う関数(なにを使うかはご自分で考えてください)を利用すると解くことができますよ。書き方にもよると思いますが、20〜30行程度でプログラミングできると思います。

    (尚dbms_outputパッケージも少しは利用しますが、それを利用しない場合は、utl_fileパッケージでログ出力というのは、どのLevelのクイズでも利用すると思います。)



    次は、level5。(次のクイズもPL/SQLだけで解けるでしょうか?。。。。。)

    | | | コメント (0) | トラックバック (0)

    2005年6月25日 (土)

    PL/SQL で Python Challenge 4

    Python Challengeは、Pythonでプログラミングをして問題を解いていくことを前提として問題が用意されているわけで、PL/SQLではプログラミングが困難な問題があるかもしれない。(とりあえず、PL/SQL以外の言語を利用しなくても解けるようなら続けてみようと思う。) 

    Level3 も、 Oracle10g の PL/SQLなら簡単プログラミングできるが、Oracle9i ではかなり大変。 そして、Level4 も、 答えが見えてきが、PL/SQLでできるのか? と思ったわけである。そして、マニュアルを眺めていたら、 utl_httpパッケージ、 utl_urlパッケージ、場合によっては、 utl_tcpパッケージ(使わないと思うが)というパッケージを利用すれば、プログラミングできそうな感じがしてきた。(まだ、試してないが。。)  結果をお楽しみに。

    | | | コメント (0) | トラックバック (0)

    2005年6月24日 (金)

    PL/SQL で Python Challenge 3

    PL/SQL で Python Challenge Level3 を解くためのヒント。

    そういえば、Oracle JDeveloperが無償になるらしい。

    この問題を解くには、Oracle10gのPL/SQLを使え!。 (それ以前のリリースなら、Java Stored か、 Cなどを利用するべし。でもできることなら Oracle10g がいいよ。楽だから。)  Oracle10g で新機能として提供されている関数と、substr()関数。 それと、やはり、utl_fileパッケージと directoryオブジェクトを利用すると幸せになれるよ。(^^

    ちなみに、Oracle10g の PL/SQLなら 20行以内でコーディングできますよ。

    (睡魔に勝てず、Java Storedとか、PL/SQLから Cとか書いてしまったが、PL/SQL で Python Challengeなので、Oracle10g より前のPL/SQLでやるなら、20行以内ではすまないですけど、その気になれば書けなくもないので PL/SQLだけで解いてみてください。 (^^;;

    | | | コメント (0) | トラックバック (0)

    2005年6月22日 (水)

    PL/SQL で Python Challenge 2

    Python Challenge  level 2を PL/SQLで解くためのヒント。

    これも単純といえば単純なのだが、PL/SQLで 解くには、utl_fileパッケージと、translate() や replace() 関数などを駆使すると数十行で解けます。Oracle9i R2から、utl_fileパッケージでは、directory オブジェクトの利用が推奨されているので この際、directory オブジェクトの利用方法なども調べておくといいでしょうね。ちなみに、 Tigerにインストールして稼働中の Oracle10g でも問題なく解けます。

    雨のなかの、湘南(20050622どこでしょう?) より。
    私も購読してますが、なんか、こんな終わり方するメールマガジンありますね〜。ちょいとパクってしまいました。 m(_ _)m

    | | | コメント (0) | トラックバック (0)

    2005年6月 3日 (金)

    PL/SQL で Python Challenge

    先日、Zopeをやっている友人から、The Python Challenge というサイトがあるのを聞いた。Python 使って、クイズを解くと、次のクイズができるという構成になっているサイトだ。(ちなみに、友人はPython を使って解く という部分のコメントを読み飛ばしていたらしく、ExcelVBA でやっていたらしい。)。 ということで、Mac De Oracle のタイトルに相応しく?! PL/SQL でやり始めた次第である。。。。(MacOS X なので、Python は、デフォルトで利用できるのだが。。。。PL/SQLでやるのであった。。。)

    ここでは、答えを書くことはできないが、いやはや、やり始めると、これが意外に面白い。(PL/SQL Challengeってサイト があってもいいかもしれない。 ヒントなども PL/SQL向けにアレンジして。。。)

    ちょっとだけ、ヒントになりそうなことを。。。

    level0 簡単過ぎる! が最初だからね〜。 なので何も書かない

    level1 これも、案外簡単だし、プログラミングしなくても、根性で解いてしまう方がいるかもしれない。(笑) PL/SQLなら、translate() って関数を使うといいよね。

    と、これくらいにしておきましょう。興味のある方は、ご自分で解いて、プログラミングを楽しんでください。

    先日、インストールした、Oracle10g for Mac OS Xの PL/SQL を利用して解いている。 で現在、Level3.

    | | | コメント (0) | トラックバック (0)