背景・目的
仕事でシステム開発を行う際、DBの更新値の比較作業を行うことが多々あります。
(モジュールの修正前後、環境設定の違い、etcetc・・・)
そんなときは、比較したいレコードをそれぞれテキスト出力してDiffツールで比較を行うということをやっています。
使っているDBMSは「SQL Server」です。SQL Serverの場合、付属の統合管理ツール「Management Studio」の「結果をファイルに出力」機能でテキスト出力が可能です。
しかし、一々GUI上でSQLを編集して、ボタンを押して、保存先とファイル名を指定して・・・とやるのはあまり効率の良い作業になるとは言えません。テーブルごとに出力するにはその分操作しないといけないですしね。
そこで、効率よく一括で欲しいテーブルのレコードをテキスト出力したいなぁ、と思いPoweShellスクリプトで組んで使用しています。
仕様
そんなリッチなものではなく、
- 任意の数のsqlファイルを実行して同名でテキスト出力する(csv形式)
- SQL内の任意の数のパラメータを実行時の標準入力から指定可能
という単純なもの。
実行イメージ

実装
全体構成

↑のようなフォルダ構成で使用します。
| output | 結果が出力されるフォルダ |
| sql | 発行したいsqlファイルを置くフォルダ |
| work | 作業フォルダ |
| output.ps1 | スクリプト本体 |
| run.bat | スクリプトを実行するためのバッチ |
| settings.json | 設定ファイル |
settings.json
DBの接続情報や設定は、作業の都度変えたいので編集しやすいように外部ファイル化
PowerShellだとjsonは一発で読み込めてむちゃくちゃ使い勝手がいいですね。構造もシンプルで編集しやすいですし。
{
"server": "SERVERNAME\\INSTANCENAME",
"database": "DATABASENAME",
"user": "",
"password": "",
"formatstrings": 1
}
○設定項目
| キー | 設定値 |
|---|---|
| server | 対象のSQLServer(マシン名\インスタンス名) |
| user | SQL Server 認証のユーザーID ※Windows認証の場合、空白(“”) |
| password | SQL Server 認証のパスワード |
| database | 対象のデータベース |
| formatstrings | 発行するクエリ内で入力で与えたいパラメータの数 |
output.ps1
function Build-SqlCmd() {
$cmd = "sqlcmd"
$cmd += " -S " + (Add-Quote $settings.server)
if( [string]::IsNullOrEmpty($settings.user) ) {
$cmd += " -E"
} else {
$cmd += " -U " + (Add-Quote $settings.user)
$cmd += " -P " + (Add-Quote $settings.password)
}
$cmd += " -d " + (Add-Quote $settings.database)
$cmd += " -i " + (Add-Quote $workPath)
$cmd += " -s,"
$cmd += " -y 0"
$cmd += " -o " + (Add-Quote $outputPath)
return $cmd
}
function Add-Quote($str) {
return '"' + $str + '"'
}
Set-Location -Path $PSScriptRoot
$workFolder = ".\work"
$outputFolder = ".\output"
$settings = Get-Content .\settings.json -Raw | ConvertFrom-Json
$formats = New-Object System.Collections.ArrayList
if($settings.formatstrings -ge 1) {
for ($i=0; $i -lt $settings.formatstrings; $i++) {
$inp = Read-Host "{$i}"
[void]$formats.Add($inp)
}
}
$scripts = Get-ChildItem -Path .\sql -Include *.sql -recurse
foreach( $script in $scripts ) {
$workPath = Join-Path $workFolder $script.Name
$outputPath = Join-Path $outputFolder ($script.BaseName + ".csv")
Copy-Item -Path $script.FullName -Destination $workPath
(Get-Content -Encoding default $workPath) |
ForEach-Object {
$line = $_
for ($i=0; $i -lt $formats.Count; $i++) {
$line = $line -replace "\{$i\}", $formats[$i]
}
$line
} | Out-File -Encoding default $workPath
$sqlCmd = Build-SqlCmd
CMD /C $sqlCmd
}
やっつけですが、シンプルに以下の処理を行います。
①パラメータを入力させる
※設定ファイルのformatstringsの数分
②sqlフォルダ内のsqlファイルをすべて取得する
※直下のみ
<②で取得したファイル数分ループ>
③クエリ内の置換対象文字列を①で置換する
※ファイルのコピーをworkフォルダに作成後、中身を置換
④SQLCMDでSQLを発行し、結果をCSVファイルとして出力
※outputフォルダに元sqlファイルと同名で出力
</②で取得したファイル数分ループ>
sqlファイルは文字コードはSJISの想定。
PowerShellでSQL Serverにクエリを発行してテキスト出力する方法は、
- SQLCMD
- Invoke-Sqlcmd
- 自作(.NETのライブラリを使用)
などがあるみたい。
Invole-Sqlcmdは、Install-Moduleが必要なので外部のネットワークに接続できないコンピュータでは使えなそうでした。また自作は時間がかかりそうだったので、SQLCMDを使用しています。
ただ、SQLCMDだとヘッダーの出力に難があり、ヘッダーは出力なしとしています。
何かいい方法ないものか…
おわりに
ちなみに更新前後の比較だけであれば、「A5:SQL Mk-2」というフリーのSQLクライアントが便利です。比較結果をHTML出力やExcel出力できたりするのでエビデンス作成にも便利。
他にも世の中には便利なフリーソフトが溢れているのでしょうけど、会社では基本的にフリーソフトは無条件では使用できず申請が必要になりますよね。そんなときPowerShellやバッチが使えると代替できてとても便利です。勉強せねば・・・
世の中のイケイケなシステム屋さんって、どんな方法でやっているのでしょう。気になる〰。

