お手軽な「夜間バッチ」をあなたに

1.やっと便利になったコンピュータ

バッチでない「バッチ」

 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」並みの便利さを手にした、というところだと思います。

2.バックアップはどうしていますか

何のためのバックアップか

 ところで、皆さんはデータベースのバックアップはどうしておられますか。整備にお金をかけたデータベースサーバー、あるいは、大事であるがゆえに保守・運用を外部委託しているデータベースサーバーの場合は、いかにも丈夫なディスクとテープバックアップ、高価な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のタイプライブラリの利用も同様に前稿を参照してください。

3.もっとお手軽なバックアップ

いろいろな状況下でのレコードセット保存

 バックアップを取るべきタイミングが、定時バッチの中での特定のタイミングである場合もあります。そのような場合には、もっと安直な手法として、レコードセットそのものをいきなりバイナリファイル(「*.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送信できるようになったわけですから。

 

最終更新日:2000年9月12日

>> システム開発のページへ >>戻る

Copyright© 1996-2000 Yumenokunisha All Rights Rserved