rm /blog

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【ORACLE】default付のテーブル項目追加中にSELECTするとかえってこない

テーブルにdefault指定付きの項目を追加(alter table tabne_name add column_name column_type default default_value)すると、
”テーブル項目追加中(alter文実行中)”はそのテーブルに対するSELECTが待たされるらしい。

alterが終われば解放されてSELECTも通るようになる。

テーブルに項目を追加するわけだから、
追加される前(alter文実行中も「追加」が完了する前という意味では「追加される前」)に発行するselect文には
当然追加対象のテーブル項目を含めることはできないので、
追加項目を見ないselectと、これからテーブルに項目を追加するalterの両者は、一見すると無関係に見える。
だけどどうも影響を受けてしまうようだ。



たとえば下記のようなテーブルがあるとする
CREATE TABLE TEST_TABLE ( 
	TEST_CHAR					CHAR(47)
) 


①このテーブルに対して下記の構文でテーブル項目(default指定付き)を追加する
alter table TEST_TABLE add TEST_NUMBER number default 0;
②このalter文が実行されてる最中に下記のようなSELECT文を同じテーブルに対して発行する
SELECT TEST_CHAR
FROM TEST_TABLE
WHERE ROWNUM <= 1000;
①⇒②の順に実行した場合、
少なくとも①が終わるまでは②は結果を返すことはできない。

これは①の内部的な進捗具合に依存しない-というのは、
③alter実行前に例えば未commitの下記のような更新処理が走っていたとき
update TEST_TABLE
set    TEST_CHAR = substr(TEST_CHAR,1,20)
where  rownum <= 1000;
③⇒①⇒②の順に実行した場合、
①はalterが発行されはするものの、③がそれよりも前にupdateを実行済みのため、
テーブルロックされて先には進めず、実体としてalter tableの処理は何も進んでいない。
にも関わらずやはり②は同じ理由で止められる。



これがdefault指定なしだと、どうもそういうことはないらしい。
④つまり下記のようなalter文
alter table TEST_TABLE add TEST_NUMBER number;
であるが、
これに関しては例えば③⇒④の順だったとして③が未commitであろうとも無関係に実行・かつ完了できる。

これを確認するため、
簡単な実験をして確認したのでその結果も載せておこう。
「実験」の内容は、UNIX系マシン上でalterとselectをバックグラウンド実行させて、
それぞれの完了タイミングを観察する簡易なものだ。

(A)alter文実行sh(11_alter_table_exec.sh)
#!/bin/sh

echo "`date` ALTER TABLE START."
sqlplus TESTUSER/TESTPASS@TESTDB @10_alter_table_add_column.sql 
echo "`date` ALTER TABLE FINISH."

exit 0

(B)alter文sql(10_alter_table_add_column.sql )
alter table TEST_TABLE add TEST_NUMBER number;
exit;



(C)select文実行sh(21_select_table_exec.sh)
#!/bin/sh

echo "`date` SELECT START."
sqlplus TESTUSER/TESTPASS@TESTDB @20_select_table_rownum100.sql
echo "`date` SELECT FINISH."

exit 0

(D)select文sql(20_select_table_rownum100.sql)
set linesize 3000;
set pagesize 0;
select test_char from test_table where rownum <= 100;
exit;



(E)実験用メインsh(00_test_alter_table_and_select.sh)
#!/bin/sh

echo "`date` Start."

sh ./11_alter_table_exec.sh &
sh ./21_select_table_exec.sh &

exit 0



(E)は(A)と(C)を順に実行(バックグラウンドで投げっぱなし)してすぐ終わる。
(A)は(B)を、(C)は(D)を内部的に実行して、それぞれ完了タイミングでそれを標準出力して知らせる仕組み。
この(E)を実行して、selectとalterが並走するか実験する。
alter⇒selectまでの極僅かな時間でalterが終わってしまう可能性もあるので、
alter先のテーブル「TEST_TABLE」にはとりあえず100万件のレコードを用意しておく。
(ただ、10万件でやっても100万件でやっても、default指定がないalterは60ミリ秒程度で終わってしまったので、
 default指定がない場合のテーブル項目追加がalter先のデータ件数に依存するかどうかは個人的にはわかっていない。
 正直気休め程度にしかならないかもしれない。)


これで実験したところ、概略すると以下のようになった。
2014年  9月 24日 水曜日 21:40:35 JST Start.
…
2014年  9月 24日 水曜日 21:40:35 JST ALTER TABLE START. ←ALTERの開始
2014年  9月 24日 水曜日 21:40:35 JST SELECT START.      ←SELECTの開始
…
100行が選択されました。


表が変更されました。
…
2014年  9月 24日 水曜日 21:40:35 JST SELECT FINISH.    ←SELECTの終了
2014年  9月 24日 水曜日 21:40:35 JST ALTER TABLE FINISH.←ALTERの終了

というわけで、ALTERの実行中にSELECTが実行できることが確認できた。
※ただこの程度だとOSが処理する際の標準出力へのフラッシュのタイミング誤差のような気も正直する…
 ⇒ALTERの終了とSELECTの終了が前後している可能性も否定できないような気もする
 まあ実行直後にはALTERとSELECTのsqlplus接続開始文字列が混在して標準出力されていたので、
 終了地点でそれが見られないということはSELECT終了⇒ALTER終了の前後関係は間違ってないとみてよいかもしれない。


default指定があるとテーブルに対する変更操作が入るという理由から、これがupdateと競合して止められる理由はなんとなく理解できる。
でもselectと競合するのはなんとなく解せぬ。
少なくとも俺の直観的な感覚とははずれてる。
oracle的にはそうでもないのかな…?一般常識なの?)
update(やdeleteやinsert)もselectも同じDMLだからという括りなのだろうか。
よくわからん。