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送信できるようになったわけですから。