【A5M2cmd + WinMerge】データ抽出＆比較検証を自動化しよう

## 前書き
以前、「リプレース対応前後の旧システム・新システムにおいて、アウトプットテーブルの内容が完全一致するかどうか検証したい」といった機会がありました。

**大量のテーブル ＆ 大量のデータ** を比較するので時間がかかる… 
**単純比較するだけ** なのでそこまで手間をかけたくない…

という思いから、自動比較検証スクリプトを作成しました。どなたかのお役に立てれば幸いです。

## この記事で紹介しているスクリプト
batファイルを実行するだけでデータの比較検証ができるスクリプトを作ります。
👇処理イメージは以下の通りです。

1. A5M2cmdで、比較対象のデータを取得し、CSVファイルに出力する
1. WinMergeコマンドで、出力したCSVファイルを比較する
1. WinMerge比較結果をHTML形式のレポートに吐き出す

## 用意するもの
* WinMerge
* A5M2
* A5M2cmd

## サンプルコード全量
<details><summary>＜👇①実行対象のbat＞</summary>

``` bat
@echo off

rem フォルダ名やファイル名に使用するジョブ名を設定
set JOB_NAME=TEST

rem ログファイルのパス設定
set LOG_FOLDER=log
set yyyyMMdd=%date:/=%
set hhmmss=%time::=%
set hhmmss=%hhmmss:~0,6%
set hhmmss=%hhmmss: =0%
set logfile=%yyyyMMdd%\%LOG_FOLDER%\%JOB_NAME%_%yyyyMMdd%_%hhmmss%.log

rem エクスポートフォルダ設定
set NEW_CSV_FOLDER=%yyyyMMdd%\%JOB_NAME%_NEW
set OLD_CSV_FOLDER=%yyyyMMdd%\%JOB_NAME%_OLD

rem 比較結果フォルダ設定
set RESULT_FOLDER=%yyyyMMdd%\%JOB_NAME%_RESULT

rem 各種フォルダ作成
mkdir %yyyyMMdd%
mkdir %yyyyMMdd%\%LOG_FOLDER%
mkdir %NEW_CSV_FOLDER%
mkdir %OLD_CSV_FOLDER%
mkdir %RESULT_FOLDER%

rem 新旧のデータ取得
rem （補足）比較したいテーブル名をテキストファイルに列挙しておく
for /f "tokens=* delims=" %%i in (%JOB_NAME%_比較対象テーブル.txt) do (
  @echo %%i
  call 99_output_with_select.bat %%i           | findstr "." >> "%logfile%"
)

rem CSVファイルを出力したフォルダを丸ごと比較検証（後続で比較検証バッチの実行結果（ERRORLEVEL）を取得するため、findstrコマンドは実行しない。 ）
call 99_compare_new_and_old_folders.bat  >> "%logfile%"

rem 標準出力されたERRORLEVELを取得
echo %ERRORLEVEL%
set RETURN_CODE=%ERRORLEVEL%
pause

rem エクスポートフォルダをzip圧縮
powershell compress-archive -Force %NEW_CSV_FOLDER%/* %NEW_CSV_FOLDER%.zip
powershell compress-archive -Force %OLD_CSV_FOLDER%/* %OLD_CSV_FOLDER%.zip

rem エクスポートフォルダを削除
rmdir /s /q %NEW_CSV_FOLDER%
rmdir /s /q %OLD_CSV_FOLDER%

exit /b %RETURN_CODE%
```

</details>

<details><summary>＜👇②データ取得 ＆ CSVファイル出力をする「99_output_with_select.bat」＞</summary>

```bat
@echo off

rem 【説明】新・現行からSELECT文でデータを取得し、CSVファイルに出力する。

rem 【前提①】[NEW_SQL]フォルダに新システムから比較検証データを取得するSQL（テーブル名.sql）を作成しておくこと。
rem 【前提②】[OLD_SQL]フォルダに現行システムから比較検証データを取得するSQL（テーブル名.sql）を作成しておくこと。

rem DB接続文字列取得(NEW_CONNECTION/OLD_CONNECTION)
call connection_string.bat

@echo on

rem ---------------------------------------------------------------------------
rem 検証対象のテーブル名取得
set TARGET_TABLE=%1

rem SQL文格納先パス設定
set NEW_SQL_PATH=NEW_SQL\%TARGET_TABLE%.sql
set OLD_SQL_PATH=OLD_SQL\%TARGET_TABLE%.sql

rem 新システムのデータ取得
A5M2cmd "/Connect=%NEW_CONNECTION%" ^
/RunSQL ^
/FileName=%NEW_SQL_PATH% ^
/FilePattern=%NEW_CSV_FOLDER%\%TARGET_TABLE%.csv

rem 現行システムのデータ取得
A5M2cmd "/Connect=%OLD_CONNECTION%" ^
/RunSQL ^
/FileName=%OLD_SQL_PATH% ^
/FilePattern=%OLD_CSV_FOLDER%\%TARGET_TABLE%.csv
rem ---------------------------------------------------------------------------

@echo off
```

</details>

<details><summary>＜👇上のbatで呼び出していた「connection_string.bat」＞</summary>

```bat
rem ※ご自分のDB環境に合わせて記載を修正してください。

rem 新システムの接続文字列
set NEW_CONNECTION=__ConnectionType=Internal;ProviderName=SQL Server;ServerName=[サーバ名];Database=[データベース名];Authentication=OS;SavePassword=False;MaxDatabaseSize=128;MaxBufferSize=640;PacketSize=4096

rem 現行システムの接続文字列
set OLD_CONNECTION=__ConnectionType=Internal;ProviderName=Oracle;Direct=True;ServerName=[サーバ名];Port=[ポート番号];Database=[データベース名];SavePassword=False;UserName=[ユーザー名];Password=[パスワード]
```

</details>

<details><summary>＜👇③WinMergeによる比較を行う「99_compare_new_and_old_folders.bat」＞</summary>

```bat
@echo off

rem 【説明】WinMergeコマンドによりフォルダごと比較する。
rem 比較結果は[yyyymmdd/ジョブ名_RESULT/ジョブ名_フォルダ比較.html]に出力する。

@echo on

rem --------------------------------------------------------------------------- 
rem WinMergeコマンドで新旧フォルダ比較
WinMergeU %NEW_CSV_FOLDER% %OLD_CSV_FOLDER% ^
  /minimize ^
  /noninteractive ^
  /wl ^
  /wr ^
  /u ^
  /ignorews ^
  /enableexitcode ^
  /cfg Settings/IgnoreSpace=2 ^
  /cfg Settings/DirViewExpandSubdirs=1 ^
  /cfg ReportFiles/ReportType=2 ^
  /cfg ReportFiles/IncludeFileCmpReport=1 ^
  /or "%RESULT_FOLDER%\%JOB_NAME%_フォルダ比較.html"

rem フォルダ比較結果を確認（0: 同一, 1: 差異あり, 2: エラー）
echo 比較結果：%ERRORLEVEL%
rem ---------------------------------------------------------------------------
@echo off
```

</details>

## 実行結果イメージ
①の、実行対象batをキックすると、以下のようなフォルダ構成でCSVファイル・HTMLレポートなどが出力されます。
```
[20250101]（実行日）
　┣ [JBXXX_RESULT]（WinMerge比較結果）
　┃ ┣ [JBXXX_フォルダ比較.files]
　┃ ┗ [JBXXX_フォルダ比較.html]
　┣ [log]
　┃ ┗ [JBXXX_20250101_123456.log]
　┣ [JBXXX_NEW.zip]（比較対象データCSVファイルをzip圧縮したもの）
　┗ [JBXXX_OLD.zip]（同上）
```

## 解説
### ①実行対象batの解説
序盤はCSVファイルやWinMergeレポートファイルのファイル名や、出力先フォルダを設定・作成しているだけです。
```bat
@echo off

rem フォルダ名やファイル名に使用するジョブ名を設定
set JOB_NAME=TEST

（中略）

rem 各種フォルダ作成
mkdir %yyyyMMdd%
mkdir %yyyyMMdd%\%LOG_FOLDER%
mkdir %NEW_CSV_FOLDER%
mkdir %OLD_CSV_FOLDER%
mkdir %RESULT_FOLDER%
```
-----
ここで、②の「99_output_with_select.bat」（データ取得bat）を呼び出しています。
「99_output_with_select.bat」の実行結果はログファイルへ出力するようにしています。

ちなみに、「比較対象テーブル.txt」に記載したテーブル名を「99_output_with_select.bat」に渡していますが、特にテーブル物理名である必要はありません。
（「99_output_with_select.bat」内では「引数.sql」を実行して「引数.csv」に出力する…みたいな処理をしているだけ）
```bat
rem 新旧のデータ取得
rem （補足）比較したいテーブル名をテキストファイルに列挙しておく
for /f "tokens=* delims=" %%i in (%JOB_NAME%_比較対象テーブル.txt) do (
  @echo %%i
  call 99_output_with_select.bat %%i           | findstr "." >> "%logfile%"
)
```
--------

ここで③の「99_compare_new_and_old_folders.bat」（データ比較bat）を呼び出しています。
こちらも「99_compare_new_and_old_folders.bat」の実行結果はログファイルへ出力するようにしています。

ちなみに、私はこのスクリプトをJP1より実行していました。
WinMergeの比較結果（0: 同一, 1: 差異あり, 2: エラー）に応じて、JP1のジョブを「正常終了・警告終了・異常終了」に遷移させたかったので、データ比較結果を「RETURN_CODE」に設定して返却するようにしています。
```bat
rem CSVファイルを出力したフォルダを丸ごと比較検証（後続で比較検証バッチの実行結果（ERRORLEVEL）を取得するため、findstrコマンドは実行しない。 ）
call 99_compare_new_and_old_folders.bat  >> "%logfile%"

rem 標準出力されたERRORLEVELを取得
echo %ERRORLEVEL%
set RETURN_CODE=%ERRORLEVEL%
pause
```
---
最後に、CSVファイルを格納したフォルダをzip圧縮 ＆ 圧縮前のフォルダを削除しています。
データ量が膨大でCSVファイルが激重になってしまったのでこの処理を加えました。
```bat
rem エクスポートフォルダをzip圧縮
powershell compress-archive -Force %NEW_CSV_FOLDER%/* %NEW_CSV_FOLDER%.zip
powershell compress-archive -Force %OLD_CSV_FOLDER%/* %OLD_CSV_FOLDER%.zip

rem エクスポートフォルダを削除
rmdir /s /q %NEW_CSV_FOLDER%
rmdir /s /q %OLD_CSV_FOLDER%

exit /b %RETURN_CODE%
```
-----
### ②データ取得bat（99_output_with_select.bat）の解説
記載した通りですが、「NEW_SQL」フォルダと「OLD_SQL」フォルダ内に予めSELECT文を記載したSQLファイルを作成しておく必要があります。
また、「connection_string.bat」で、A5M2cmdよりDB接続する際に必要となるDB接続情報を定義しておく必要があります。
```bat
@echo off

rem 【説明】新・現行からSELECT文でデータを取得し、CSVファイルに出力する。

rem 【前提①】[NEW_SQL]フォルダに新システムから比較検証データを取得するSQL（テーブル名.sql）を作成しておくこと。
rem 【前提②】[OLD_SQL]フォルダに現行システムから比較検証データを取得するSQL（テーブル名.sql）を作成しておくこと。

rem DB接続文字列取得(NEW_CONNECTION/OLD_CONNECTION)
call connection_string.bat
```
---
「`@echo on`」以降の処理がログファイルへ出力されます。
①実行対象batから指定された引数（テーブル物理名）より、実行対象のSQLファイルを設定します。
```bat
@echo on

rem ---------------------------------------------------------------------------
rem 検証対象のテーブル名取得
set TARGET_TABLE=%1

rem SQL文格納先パス設定
set NEW_SQL_PATH=NEW_SQL\%TARGET_TABLE%.sql
set OLD_SQL_PATH=OLD_SQL\%TARGET_TABLE%.sql
```
---

A5M2cmdを使用し、DBからデータを取得 ＆ CSVファイルへ出力しています。
`/FileName`で実行対象のSQLファイルを指定、`/FilePattern`で取得結果の出力先を指定しています。（もう一方のデータ取得も同様。）
```bat
rem 新システムのデータ取得
A5M2cmd "/Connect=%NEW_CONNECTION%" ^
/RunSQL ^
/FileName=%NEW_SQL_PATH% ^
/FilePattern=%NEW_CSV_FOLDER%\%TARGET_TABLE%.csv
```
---
### ③データ比較bat（99_compare_new_and_old_folders.bat）の解説
②データ取得batで出力したCSVファイルが格納されているフォルダを比較します。
```bat
@echo off

rem 【説明】WinMergeコマンドによりフォルダごと比較する。
rem 比較結果は[yyyymmdd/ジョブ名_RESULT/ジョブ名_フォルダ比較.html]に出力する。
```
---

こちらも「`@echo on`」以降の処理がログファイルへ出力されます。
WinMergeコマンドオプションの意味合いについては、コード内に説明を追記しました。
```bat
@echo on

rem --------------------------------------------------------------------------- 
rem WinMergeコマンドで新旧フォルダ比較
WinMergeU %NEW_CSV_FOLDER% %OLD_CSV_FOLDER% ^
  /minimize ^ ⇒最小化状態でWinMergeを開始
  /noninteractive ^ ⇒比較終了後にWinMergeを閉じる
  /wl ^ ⇒読み取り専用で開く（左）
  /wr ^ ⇒読み取り専用で開く（右）
  /u ^ ⇒パスを最近使用した項目に追加しない
  /ignorews ^ ⇒空白を無視する
  /enableexitcode ^ ⇒比較結果を終了コードに設定（0: 同一, 1: 差異あり, 2: エラー）
  /cfg Settings/IgnoreSpace=2 ^ ⇒空白「すべて無視」に設定
  /cfg Settings/DirViewExpandSubdirs=1 ^ ⇒自動的にサブフォルダーを展開する
  /cfg ReportFiles/ReportType=2 ^ ⇒「シンプルなHTML形式」でレポート出力
  /cfg ReportFiles/IncludeFileCmpReport=1 ^ ⇒ファイル比較レポートを含める
  /or "%RESULT_FOLDER%\%JOB_NAME%_フォルダ比較.html" ⇒レポート出力先

rem フォルダ比較結果を確認（0: 同一, 1: 差異あり, 2: エラー）
echo 比較結果：%ERRORLEVEL%
```

👇参考にさせていただいたサイト

https://manual.winmerge.org/jp/Command_line.html

https://needtec.sakura.ne.jp/wod07672/2020/03/29/winmerge%E3%81%AE%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3/

https://qiita.com/nkmrtkhd/items/11ff0a9c9872a3faf234


ちなみに「`/cfg Settings/etc...`」や「`/cfg ReportFiles/etc...`」などの値は、WinMergeの設定ファイルを出力して、実際の設定値を見ながら指定してみてもいいと思います。

👇WinMergeの設定を開き、「エクスポート」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3483700/8b47d0b2-bca1-4293-892c-70c0fa67b930.png)

👇設定ファイルの中身
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3483700/471da29b-2715-419b-a1d6-a137d4779e35.png)

## あとがき
この自動比較検証スクリプトを作成したおかげで、「新システムと旧システムの実行が終わったタイミングに、JP1から検証スクリプトをキックするように設定しておくだけで、あとは放置でOK」な状況を作り出すことができました。単純作業はどんどん自動化して幸せになりたいですね。

ただ、こちらのスクリプトは私が社会人3年目の時に作ったものなので、改善の余地はたくさんあると思います。ご助言などあればコメントをお願いいたします。
ここまで読んで下さりありがとうございました。