OSと標準的なツール類を使ったバッチ処理については、大方のUNIXユーザーなら、コマンドラインで縦横無尽にコンピュータ操作することに慣れ親しんでいますので、そのコマンドをスクリプトファイルに保存し、実行権限を与え、crontabに登録すればそれで済むだけ話なのですが、VBScripstが成長するまでは、Windowsユーザーにとってバッチ処理は面倒なことでした。
MS-DOS以来、Windowsの世界で「バッチ」と言えば、cmd.exe等のコマンドインタープリタを使った「*.bat」や「*.cmd」でのスクリプトのことを指しますが、そこでは、ほとんどがShellを通じた不自由な外部コマンド(UNIXに比べれば貧弱な数)の呼び出しに過ぎず、今日的に望まれるバッチ処理を実現するには、「バッチファイル」で使う外部コマンドをわざわざプログラミングしてコンパイルしなければなりませんでした。
しかも、一般業務系でのスタンダード言語であるVisual Basicは、WindowsAPIを使わないと標準入出力をサポートしませんので、実際は「バッチファイル」内で使うというよりは、その実行ファイルですべてを制御するように書き、タスクスケジューラに登録する、というのが基本パターンではなかったか、と思います。
さらには、そのタスクスケジューラがcrontab相当の使い勝手を確立したのは、Windows2000になってからのことでした。
では、今日的な一般業務系システムの運用管理で、最低限必要とされるバッチ処理はどんなものか、というと、やはりデータベース処理とFTPなどのネットワーク上でのホスト間ファイル転送ではないか、と思います。
このうち、FTPコマンドは、ほぼUNIXコマンドの移植コマンドなので、もともとバッチ処理用に成熟したコマンドですから問題ないとしても、データベース処理となると話は難しくなります。
具体的には、例えば、RDBMS内のデータ整理を夜間に行う、というものであれば、それぞれのRDBMSが提供するコマンドライン用ツールなどを用いれば良いのですが、実際にはOSといろいろとお話をしたり、他のホストとやりとりする場合も多く、RDBMS専用ツールの世界に閉じたものでは限界があります。
そこで、近年、ようやく出揃ってきた各種のオブジェクトに対する操作の発達により、データベースやネットワークの処理手続きを実現するわけですが、これをVisual Basicプログラミングから、VBScriptの成長により、今度はコンパイルをせずにスクリプトファイルの実行、そしてさらには、WSHでコマンドラインインターフェースとパッケージ化、というように、言わば逆順で「UNIX」並みの便利さを手にした、というところだと思います。
ところで、皆さんはデータベースのバックアップはどうしておられますか。整備にお金をかけたデータベースサーバー、あるいは、大事であるがゆえに保守・運用を外部委託しているデータベースサーバーの場合は、いかにも丈夫なディスクとテープバックアップ、高価なRDBMSに添付されている専用のバックアップツールで重装備されているので、私の場合に限らず、とりあえずはその壮観さを眺めながら「安心」を買っているはずです。
しかし、いったんトラブルが生じ、復旧に一刻を争う場合にはどうでしょうか。そうした重装備マシンが何台もあるような、とってもお金持ちの企業ならともかく、フツーの事業所の場合は、やはり大変なことになります。
災害訓練同様、このような事態への対処は、日頃の訓練に慣れていないと、仕組みをただ「わかっているだけ」というのでは何の役にも立ちません。いくらお金をかけていても、恐れていた事態が発生したときに、復旧に数時間、数日かかってしまっては、大変な損失になるわけで、こうした時間との勝負の部分で、システム運用管理者や保守・運用委託業者の能力は評価されるべきです。
それでは、「そういう事態になったとき、あなたならどうする」、その答えが実は一番有益なのではないか、と思います。
サーバーの復旧には、ハードの復旧はともかく、OSとRDBMSの復旧にとてつもなく膨大な時間を要します。その場合、最も現実的な復旧は、サーバー専用機であれ、安いデスクトップであれ、何でも良いから、とにかく動くマシンを使って代替データベースサービスを立ち上げることです。
その際、どのようにして早急にデータベースの復元を行うか、という観点から言うと、専用ツールを使ったテープバックアップからの復旧では、すべての環境がもとどおりになっていなければならず、大変、手間取ります。また、RDBMSのデータベースの構築を忠実に再現するための膨大なSQLコマンドを整理して保管しているケースは稀です。
実際、多くのSEたちは、手作業でのデータバックアップとリストアに、*.mdbファイルとアクセスのVBAインターフェースを使っているはすです。このVBAでも、RDBMS依存のデータベース接続のCOMや、データベースオブジェクトとして、DAOやRDOなどの世代のオブジェクト操作をしている場合には、少し辛くなります。
速度や安定性、操作性にまだまだ不満はあるにしても、ODBCでいつでもデータベースの接続先を変更できるようにし、さらにADOでデータアクセスするように、すべての稼動アプリを統一していたならば、かなりのスピードでの復旧が可能となるはずです。
先ほど、「多くのSEたち」の話をしましたが、アクセスのVBAインターフェースを使ってデータバックアップとリストアをするメリットは、やはり編集と実行時コンパイル、デバッグの統合開発環境を備えているからだと思います。
けれども、アクセスのVBAプログラムは所詮「*.mdb」ファイルの中にあるので、実行ファイルとして用いることはできません。そこで、特にデータバックアップとリストアのプログラムは、多少のパフォーマンスは犠牲にしても、Visual BasicやVBAで書いたコードから型宣言をすべて削除し、VBScriptファイルとして保存し、常日頃からテープバックアップとは別に「*.mdb」ファイル等への書き込みバッチとして定時実行させるようにしておくと良いでしょう。
そのファイルをさらに安全な媒体(この場合、ファイル退避なのでテープ媒体でも構わない)にバックアップしておけば、対処としては万全に近づきます。
言うまでもなく、データベース再構築のためのすべてのSQLコマンドの整理と、データのバックアップとリストアについて、絶えず最新の内容のVBScriptファイルで保持し続ける、ということが重要であることは言うまでもありません。
'BackupTable.vbs
const strDSN = "DSN=ora_svr;UID=scott;PWD=tiger"
const strNewDSN = "DSN=access_svr;UID=user;PWD=password"
Main
Sub Main()
On Error Resume Next
WriteLineTextContent ".\BackupTable.log", "Start Time : " & Now
DeleteData "TaskMaster"
BackupTaskMaster
WriteLineTextContent ".\BackupTable.log", "Finish Time : " & Now
End Sub
Sub DeleteData(strTableName)
If strTableName = "" Then Exit Sub
Set objCon = CreateObject("ADODB.Connection")
objCon.Open strNewDSN
Set objCmd = CreateObject("ADODB.Command")
objCmd.ActiveConnection = objCon
strSQL = "DELETE FROM " & strTableName
objCmd.CommandText = strSQL
objCmd.CommandType = adCmdText
objCmd.CommandTimeout = 30
objCmd.Execute "", "", adCmdText
objCon.Close
Set objCmd = Nothing
Set objCon = Nothing
End Sub
Sub BackupTaskMaster()
Set objCon = CreateObject("ADODB.Connection")
objCon.Open strDSN
Set objNewCon = CreateObject("ADODB.Connection")
objNewCon.Open strNewDSN
Set objCmd = CreateObject("ADODB.Command")
objCmd.ActiveConnection = objCon
Set objNewCmd = CreateObject("ADODB.Command")
objNewCmd.ActiveConnection = objNewCon
Set objRS = CreateObject("ADODB.Recordset")
objRS.CursorLocation = adUseClient
strSQL = "SELECT * FROM TaskMaster"
objCmd.CommandText = strSQL
objCmd.CommandType = adCmdText
objCmd.CommandTimeout = 30
objRS.Open objCmd
Do While Not objRS.EOF
strSQL = "INSERT INTO TaskMaster ("
strSQL = strSQL & "ID, "
strSQL = strSQL & "UserID, "
strSQL = strSQL & "Task, "
strSQL = strSQL & "Done"
strSQL = strSQL & ") VALUES ("
strSQL = strSQL & objRS("ID") & ", "
strSQL = strSQL & objRS("UserID") & ", "
strSQL = strSQL & "'" & objRS("Task") & "'" & ", "
strSQL = strSQL & objRS("Done")
strSQL = strSQL & ")"
objNewCmd.CommandText = strSQL
objNewCmd.CommandType = adCmdText
objNewCmd.CommandTimeout = 30
objNewCmd.Execute "", "", adCmdText
objRS.MoveNext
Loop
objRS.Close
objCon.Close
objNewCon.Close
Set objRS = Nothing
Set objCmd = Nothing
Set objNewCmd = Nothing
Set objCon = Nothing
Set objNewCon = Nothing
End Sub
この例では、単に、データバックアップするだけの例ですが、この他に、CRETATE TABLEのセットと、RESTOREのセットを用意しておくわけです。
'CreateTable.vbs
const strDSN = "DSN=ora_svr;UID=scott;PWD=tiger"
Main
Sub Main()
strSQL = "CREATE TABLE TaskMaster ("
strSQL = strSQL & "ID NUMBER(8), "
strSQL = strSQL & "UserID NUMBER(8), "
strSQL = strSQL & "Task VARCHAR2(255), "
strSQL = strSQL & "Done NUMBER(3), "
strSQL = strSQL & "CONSTRAINT Task_002 PRIMARY KEY (ID), "
strSQL = strSQL & "CONSTRAINT Task_003 UNIQUE (ID), "
strSQL = strSQL & "CONSTRAINT Task_001 CHECK (ID IS NOT NULL)"
strSQL = strSQL & ")"
'CreateTable strSQL
End Sub
Sub CreateTable(strSQL)
If strSQL = "" Then Exit Sub
Set objCon = CreateObject("ADODB.Connection")
objCon.Open strDSN
Set objCmd = CreateObject("ADODB.Command")
objCmd.ActiveConnection = objCon
objCmd.CommandText = strSQL
objCmd.CommandType = adCmdText
objCmd.CommandTimeout = 30
objCmd.Execute "", "", adCmdText
objCon.Close
Set objCmd = Nothing
Set objCon = Nothing
End Sub
なお、これらの例で、わざわざ「Main」プロシージャを作っているのは、いつでもVisual Basicと往ったり来たりできるようにするためです。また、バイナリデータのバックアップの場合は、SQL文ではなく、前稿で説明したレコードセットに対するAddNewメソッドの利用になります。ADOのタイプライブラリの利用も同様に前稿を参照してください。
バックアップを取るべきタイミングが、定時バッチの中での特定のタイミングである場合もあります。そのような場合には、もっと安直な手法として、レコードセットそのものをいきなりバイナリファイル(「*.adtg」)として保存するためのRecordsetオブジェクトに対するSaveメソッドを使います。
'BackupRS.vbs
const strDSN = "DSN=ora_svr;UID=scott;PWD=tiger"
Main
Sub Main()
On Error Resume Next
Set objWSH = Wscript.CreateObject("Wscript.Shell")
objWSH.Run "cmd.exe /c del .\*.adtg", SW_HIDE
Set objWSH = Nothing
SaveRS "TaskMaster"
End Sub
Sub SaveRS(strTableName)
If strTableName = "" Then Exit Sub
Set objCon = CreateObject("ADODB.Connection")
objCon.Open strDSN
Set objCmd = CreateObject("ADODB.Command")
objCmd.ActiveConnection = objCon
Set objRS = CreateObject("ADODB.Recordset")
objRS.CursorLocation = adUseClient
strSQL = "SELECT * FROM " & strTableName
objCmd.CommandText = strSQL
objCmd.CommandType = adCmdText
objCmd.CommandTimeout = 30
objRS.Open objCmd
objRS.Save ".\" & strTableName & ".adtg", adPersistADTG
objRS.Close
objCon.Close
Set objRS = Nothing
Set objCmd = Nothing
Set objCon = Nothing
End Sub
この例では、まず、カレントフォルダにある「*.adtg」ファイルを削除し(この例では、WSHオブジェクトでcmd.exeの内部コマンドを使っていますが、FileSystemObjectのFileオブジェクトの操作で結構です)、それから、レコードセットをそのままSaveメソッドでファイルに吐き出させています。
このようにレコードセットの物理的ファイルを、マイクロソフトでは「レコードセットの永続化」と呼んでいますが、ファイルサイズは、「*.mdb」とほぼ、同サイズとなります。
そして、反対に物理的ファイルからレコードセットを復元するには、次のようにします。
Set objRS = CreateObject("ADODB.Recordset")
objRS.Open ".\TaskMaster.adtg", "Provider=MSPersist", , , adCmdFile
このスクリプトは、もちろん「SELECT * FROM table」でテーブルを丸ごとバックアップする目的でも利用できますから、様々な場面で活用できます。
こうして生成されたファイルをFTPでファイルバックアップ用マシンに送信するか、あるいは、スクリプトが動く環境(Windows2000等)さえあれば、ODBC接続により、どんなネットワークパソコンでもホストそのものをバックアップ媒体として活用できます。
本稿はあくまで「お手軽」な処理なので、本格的なシステム構築の際に用いる場合ではなく、ちょっとしたシステムのちょっとしたバックアップが目的、ということになりますが、実際の現場では、こうした「ちょっとした」システムがたくさんあって、それらがすべて重装備のRDBMSサーバーに同居していたら、全体の復旧は大変な労力になるので、そういうシステム類は軽い管理体系の中で運用管理しておいた方が良いのでは、というお話です。
マイクロソフトのスクリプトは、様々なウイルスに用いられて出鼻をくじかれ続けていますが、UNIXからの移行を進めるうえで、避けて通れない管理運用ツールとして整備されてきたものだと思います。
例えば異常な処理結果についての通知は、ローカルディスク内のログにしか吐き出せなかったものを、「CDONTS.NewMail」オブジェクトで簡単にsmtp送信できるようになったわけですから。