同村チェッカーについて技術的な話4
さて技術的な話その4です。
バックナンバーはこちら。
同村チェッカーについて技術的な話 - Portal:siro
同村チェッカーについて技術的な話2 - Portal:siro
同村チェッカーについて技術的な話3 - Portal:siro
今回からは自動挿入の話に入ります。
今までは、エピローグURLの解析に重点置いてましたが、
そもそもエピローグURLの入力はどうしてるの?というところから。
最初の実装ではエピローグのURLを僕が手動で入力していたんですが、
そんな面倒くさいこと逐一やってられないので自動化しました。
何をやっているのか、先に概要だけ説明しますと、
- 現在どの村まで登録済かDBにアクセスして確認
- 終了済みの村一覧(例:http://crazy-crazy.sakura.ne.jp/crazy/sow.cgi?cmd=oldlog )から未登録の村番号を抽出
- 抽出した村のエピローグURLを特定・解析・登録
やってることはこれだけです。というわけでまずは抽出までの部分をご説明します。
<?php // それぞれ最新村のIDを抽出 // 但し、IDにgapが生じている場合は、gap以降の村を取り直す。 $db_conn = db_conn(); if($db_conn !== false){ $first_vil; $result = mysql_query("SELECT dv1.vil_server,MIN(dv1.vil_no+1) as vil_no FROM douson_vil as dv1 WHERE (dv1.vil_no+1) NOT IN (SELECT dv2.vil_no FROM douson_vil as dv2 WHERE dv1.vil_server = dv2.vil_server) GROUP BY dv1.vil_server"); while($row = mysql_fetch_assoc($result)){ $first_vil[$row['vil_server']] = $row['vil_no']; } // 村一覧のURL等の配列を生成(現行稼動州のみ) $array_giji; // 標準 // 2011/12/03 標準鯖長期停止のためコメントアウト /* $array_giji[] = array( 'url' => "http://utage.sytes.net/wolf/sow.cgi?cmd=oldlog", 'server' => "Wolf", 'first_vil' => $first_vil['標準']); */ // 陰謀 $array_giji[] = array( 'url' => "http://cabala.halfmoon.jp/cafe/sow.cgi?cmd=oldlog", 'server' => "Cafe", 'first_vil' => $first_vil['陰謀(陰謀の苑・Cabala Cafe)']); // 大乱闘AS // 2012/01/21 大乱闘AS鯖長期停止のためコメントアウト /* $array_giji[] = array( 'url' => "http://jinro.jksy.org/~nanakorobi/sow.cgi?cmd=oldlog", 'server' => "AS", 'first_vil' => $first_vil['大乱闘AS']); */ // RPBPr $array_giji[] = array( 'url' => "http://perjury.rulez.jp/sow.cgi?cmd=oldlog", 'server' => "RPBPr", 'first_vil' => $first_vil['RPBPr']); // RPBx $array_giji[] = array( 'url' => "http://xebec.x0.to/xebec/sow.cgi?cmd=oldlog", 'server' => "RPBx", 'first_vil' => $first_vil['RPBx']); // RPBc $array_giji[] = array( 'url' => "http://crazy-crazy.sakura.ne.jp/crazy/sow.cgi?cmd=oldlog", 'server' => "RPBc", 'first_vil' => $first_vil['RPBc']); // 似顔絵人狼 // 2011/12/03 似顔絵鯖長期停止のためコメントアウト /* $array_giji[] = array( 'url' => "http://utage.sytes.net/pan/sow.cgi?cmd=oldlog", 'server' => "Pan", 'first_vil' => $first_vil['似顔絵人狼']); */ foreach($array_giji as $giji){ insert_vil_all($giji['url'],$giji['server'],$giji['first_vil'],999); } echo "auto insert done."; } ?>
まずこちらが、1日に1回くらいの頻度で手動実行しているphpです。
え?そこ手動なの?って感じではありますが、echoとかガンガン吐いてるのを見てわかるように、実行結果は一応目で見て確認しています。
もちろんcron書けば1日に1回くらいの自動実行など容易にできるのですが、エラーが見てわかるようにとか、色々な理由で現在も手動実行を続けています。
番号抽出の部分はここ。見づらいのでSQL部分だけ抽出します。
SELECT dv1.vil_server,MIN(dv1.vil_no+1) as vil_no FROM douson_vil as dv1 WHERE (dv1.vil_no+1) NOT IN (SELECT dv2.vil_no FROM douson_vil as dv2 WHERE dv1.vil_server = dv2.vil_server) GROUP BY dv1.vil_server
未登録の村なら、単純にvil_no(村番号)のmaxを取ってくればいいというわけでもなく、
建った順と村が終了する順が一致しなかったりして、gapが生じたりすることが稀によくあります。
なのでここでは「追加済みのもっとも大きな村番号+1の値を州単位で取得、ただしgapが生じている場合はgapが生じている村番号」を取得しています。
と言っても解りづらいのでもう少し説明します。
FROMとGROUP BYについては割愛。ここでエイリアスdv1を付け、検索結果をサーバー単位で纏めているだけです。
次にWHERE句。副問い合わせごとまいります。
WHERE (dv1.vil_no+1) NOT IN (SELECT dv2.vil_no FROM douson_vil as dv2 WHERE dv1.vil_server = dv2.vil_server)
例を見ながら解説していきます。
こんな感じで、4と6の間にgapが生じた状態で村が登録されているとします。
10 9 8 7 6 4 // 5がない 3 2 1
村番号を1つ抽出してきます。無論、別サーバの村番号と比較しても意味がないので、ちゃんと副問い合わせ内でvil_serverを条件に含めておきます。
つまり書き下すと、
WHERE (dv1.vil_no+1) NOT IN (1,2,3,4,6,7,8,9,10)
となります。
dv1.vil_noも当然(1,2,3,4,6,7,8,9,10)なわけです。
これらをNOT INで比較していきます*1。
dv1.vil_no=1の時、dv1.vil_no+1=2→NOT INの中に2は「ある」ので抽出結果はfalse。
dv1.vil_no=2の時、dv1.vil_no+1=3→NOT INの中に3は「ある」ので抽出結果はfalse。
dv1.vil_no=3の時、dv1.vil_no+1=4→NOT INの中に4は「ある」ので抽出結果はfalse。
dv1.vil_no=4の時、dv1.vil_no+1=5→NOT INの中に5は「ない」ので抽出結果はtrue。
……ちょっと省略します。
dv1.vil_no=9の時、dv1.vil_no+1=10→NOT INの中に10は「ある」ので抽出結果はfalse。
dv1.vil_no=10の時、dv1.vil_no+1=11→NOT INの中に11は「ない」ので抽出結果はtrue。
つまり、わかりやすく書き下すとこうなります(GROUP BY省略)
SELECT dv1.vil_server,MIN(dv1.vil_no+1) as vil_no FROM douson_vil as dv1 WHERE dv1.vil_no+1 = 5 OR dv1.vil_no+1 = 11
これの「MIN」(今まで最大最大言ってましたが必要なのはMINなのです!実際ヤヤコシイ!)を取得するとつまりこの例の場合は「5」が抽出されることになります。
5番の村はありません、ここから登録すればオッケー☆となります。
ちなみにgapが無い場合は、上の例で言うと11のみが抽出されることになりますので、11村から登録すればいいというわけです。オッケー☆
複数gapが発生してる場合はどーすんのってのがありますが、その時も最小から取ります。
先述したとおり、既に登録されている村の場合はskipする機能がありますので、問題なく。
それに基本的に1日1回動かしているので、複数gapが発生することもそもそも稀です。
あとは、array_gijiに各サーバ毎の連想配列を作り、
その配列を実際に解析する関数へと投げています。
実際難しくない。
解析の関数はまた後日解説します。bye☆
*1:よく考えたらNOT EXISTSでもよいのですが今回はまあいいです