【バッチスクリプト/SQL Server】バッチスクリプトからSQLファイルを実行


いつもお世話になっております。
RfromLです。

今回はSQLのスクリプトファイルをバッチスクリプトから実行する方法についてです。


1.はじめに

バッチスクリプトからSQLコマンドを実行する方法としては「【バッチスクリプト/SQL Server】テーブルトランケート処理」で使用したSQLCMDを使用します。

例としてSQLで処理する内容はSQLの基本である以下の3操作をおこなうものとします。

・select
・update
・delete

2.環境情報

今回使用した環境は以下のバージョンとなっています。

OSWindows11 Home 64ビット
DBMSSQL Server 2019
統合環境SQL Server Management Studio v18.10
SQLCMD15.0.2000.5 NT

3.使用コマンド

バッチスクリプトからSQLのスクリプトファイルを実行するには「SQLCMD」を使用します。

以下、SQLCMDコマンドのヘルプ(コマンド「SQLCMD /?」の実行結果)より

Microsoft (R) SQL Server コマンド ライン ツール
バージョン 15.0.2000.5 NT
Copyright (C) 2019 Microsoft Corporation. All rights reserved.

使用法: Sqlcmd            [-U ログイン ID]          [-P パスワード]
  [-S サーバー]        [-H ホスト名]              [-E セキュリティ接続]
  [-N 暗号化接続]      [-C サーバー証明書を信頼]
  [-d データベース名の使用]  [-l ログイン タイムアウト] [-t クエリ タイムアウト]
  [-h ヘッダー]        [-s 列の区切り文字]        [-w 表示幅]
  [-a パケット サイズ] [-e 入力エコー]            [-I 識別子を引用符で区切る]
  [-c コマンド終了]    [-L[c] サーバー一覧[クリーン アウトプット]]
  [-q "コマンドライン クエリ"]          [-Q "コマンドライン クエリ" と終了]
  [-m エラー レベル]   [-V 重大度レベル]          [-W 余分なスペースを削除]
  [-u ユニコード出力]  [-r[0|1] stderr へメッセージを出力]
  [-i 入力ファイル]    [-o 出力ファイル]          [-z パスワード変更]
  [-f <codepage> | i:<codepage>[,o:<codepage>]] [-Z パスワード変更と終了]
  [-k[1|2] 制御文字の削除[置き換え]]
  [-y 可変長型の表示幅]
  [-Y 固定長型の表示幅]
  [-p[1] 統計情報の出力[コロン区切り形式]]
  [-R クライアントの地域設定を使用]
  [-K アプリケーション インテント]
  [- M マルチサブネット フェールオーバー]
  [-b エラー時にバッチを中止]
  [-v var = "value"...][-A 専用管理者接続]
  [-X[1] コマンド、スタートアップ スクリプト、環境変数を無効化 [終了]]
  [-x 変数の代入を無効化]
  [-j 未加工エラー メッセージを印刷]
  [-g 列暗号化を有効化]
  [-G 認証に Azure Active Directory を使用]
  [-? 構文の要約を表示]

より詳細な情報はマイクロソフト社の「sqlcmd を使用してスクリプト ファイルから T-SQL を実行する」を参照してください。

上記ヘルプより、今回は以下のオプションを使用します。

No.オプション内容
1-Sサーバー名の指定
2-dデータベース名の指定
3-Uユーザー名の指定
4-Pパスワードの指定
5-i入力ファイルの指定
6-s列の区切り文字を指定
7-W余分なスペースを削除
8-hヘッダーの行数を指定、ヘッダーを出力しない場合は「-h -1」を指定
9-o出力ファイルの指定
10-bエラー時にバッチを中止

このうちNo.6~9のオプションは、select文を実行して結果を出力する場合にのみ指定します。


オプションを指定して実行する際の構文は以下の通りです。
select文を実行する場合と、update文・delete文を実行する場合の2パターンあります。

[ 構文①「select文を実行してファイル出力する場合」 ]

SQLCMD -S サーバー名 -d データベース名 -U ユーザー名 -P パスワード -i 実行するSQLファイル -s , -W -h -1 -o 出力ファイル -b >> ログファイル

上記コマンドでは、区切り文字にカンマを指定しています。


[ 構文②「update文、delete文を実行する場合(ファイル出力が不要な場合)」 ]

SQLCMD -S サーバー名 -d データベース名 -U ユーザー名 -P パスワード -i 実行するSQLファイル -b >> ログファイル

この構文を基にバッチスクリプトを作成します。


4.コード内容

今回作成したスクリプトファイルは以下の4つです。

No.ファイル名内容
1SqlExcecBat.batSQLファイルを実行するバッチスクリプト
2Sql1_Select.sqlselect文
3Sql2_Update.sqlupdate文
4Sql3_Delete.sqldelete文

各スクリプトファイルのコード内容は以下の通りです。


[ SqlExcecBat.bat ]

@REM ------------------------------------------------------------
@REM 初期処理
@REM ------------------------------------------------------------
@REM 実行バッチファイルディレクトリ設定
SET CMD_PATH=%~dp0

@REM 処理ID設定(バッチファイル名)
SET BATCH_ID=%~n0

@REM ログファイルディレクトリ設定
SET LOGDIR=%CMD_PATH%\log

@REM ログファイル設定
SET LOGFILE=%LOGDIR%\%BATCH_ID%.%DATE:~8,2%.log

@rem ------------------------------------------------------------
@rem - ファイルディレクトリ設定
@rem ------------------------------------------------------------
@rem 出力ファイルディレクトリ
set ODIR=%CMD_PATH%OUT

@rem ------------------------------------------------------------
@rem - ファイルID設定
@rem ------------------------------------------------------------
@rem 出力ファイルID
set OFILEID001=OUTFILE.txt

@rem ------------------------------------------------------------
@rem - ファイル設定
@rem ------------------------------------------------------------
@rem 出力ファイル
set OFILE001=%ODIR%\%OFILEID001%

@REM ------------------------------------------------------------
@REM SQL Server ログイン情報設定
@REM ------------------------------------------------------------
SET DB=※データベース名
SET SVR=※サーバー名
SET USR=※ユーザー名
SET PWD=※パスワード

@REM ------------------------------------------------------------
@REM 処理開始メッセージ
@REM ------------------------------------------------------------
@REM 処理開始ログ
CALL :TIMEGET
ECHO %BATCH_ID% %YYYY%年%MM%月%DD%日 %HH%時%MI%分%SS%秒 処理開始
ECHO %BATCH_ID% %YYYY%年%MM%月%DD%日 %HH%時%MI%分%SS%秒 処理開始 >> %LOGFILE%

@REM ------------------------------------------------------------
@REM SQLCMD : 価格情報テーブル 抽出処理
@REM ------------------------------------------------------------
SET PROCNAME=価格情報テーブル 抽出処理

SET SQLFILE=Sql1_Select.sql

SQLCMD -S %SVR% -d %DB% -U %USR% -P %PWD% -i %SQLFILE% -s , -W -h -1 -o %OFILE001% -b >> %LOGFILE%
IF NOT %ERRORLEVEL% == 0 GOTO ABEND

@REM ------------------------------------------------------------
@REM SQLCMD : 価格情報テーブル 更新処理
@REM ------------------------------------------------------------
SET PROCNAME=価格情報テーブル 更新処理

SET SQLFILE=Sql2_Update.sql

SQLCMD -S %SVR% -d %DB% -U %USR% -P %PWD% -i %SQLFILE% -b>> %LOGFILE%
IF NOT %ERRORLEVEL% == 0 GOTO ABEND

@REM ------------------------------------------------------------
@REM SQLCMD : 価格情報テーブル 削除処理
@REM ------------------------------------------------------------
SET PROCNAME=価格情報テーブル 削除処理

SET SQLFILE=Sql3_Delete.sql

SQLCMD -S %SVR% -d %DB% -U %USR% -P %PWD% -i %SQLFILE% -b >> %LOGFILE%
IF NOT %ERRORLEVEL% == 0 GOTO ABEND

@REM ------------------------------------------------------------
@REM 正常終了処理
@REM ------------------------------------------------------------
:END
CALL :TIMEGET
ECHO %BATCH_ID% %YYYY%年%MM%月%DD%日 %HH%時%MI%分%SS%秒 正常終了
ECHO %BATCH_ID% %YYYY%年%MM%月%DD%日 %HH%時%MI%分%SS%秒 正常終了 >> %LOGFILE%
ECHO.>> %LOGFILE%

EXIT /B 0

@REM ------------------------------------------------------------
@REM 異常終了処理
@REM ------------------------------------------------------------
:ABEND

ECHO エラーコード:%ERRORLEVEL%
ECHO エラーコード:%ERRORLEVEL% >> %LOGFILE%

CALL :TIMEGET
ECHO %BATCH_ID% %YYYY%年%MM%月%DD%日 %HH%時%MI%分%SS%秒 異常終了
ECHO %BATCH_ID% %YYYY%年%MM%月%DD%日 %HH%時%MI%分%SS%秒 異常終了 >> %LOGFILE%
ECHO.>> %LOGFILE%

EXIT /B 9

@REM ------------------------------------------------------------
@REM 日時情報取得サブルーチン
@REM ------------------------------------------------------------
:TIMEGET

@REM システム日付取得(YYYY=年,MM=月,DD=日,WK=曜日)
SET YYYY=%DATE:~0,4%
SET MM=%DATE:~5,2%
SET DD=%DATE:~8,2%
SET WK=%DATE:~0,1%

@REM システム時刻取得(HH=時)
@REM ※HH部分(時間部分)の1文字目をチェックして
@REM   空白の場合は頭ゼロ埋めでセット
SET HHCHK=%TIME:~0,1%

IF "%HHCHK%"==" " (
  SET HH=0%TIME:~1,1%
) ELSE (
  SET HH=%TIME:~0,2%
)

@REM システム時刻取得(MI=分,SS=秒)
SET MI=%TIME:~3,2%
SET SS=%TIME:~6,2%

SET DATETIME="%YYYY%%MM%%DD%_%HH%%MI%%SS%"

※SQL Server ログイン情報設定の以下項目はマスキングしています。
・データベース名
・サーバー名
・ユーザー名
・パスワード



[ Sql1_Select.sql ]

set nocount on;

SELECT *
FROM   価格情報;

set nocount off;

※補足
「set nocount on;」は、SQL文を実行した際に表示される「(5 行処理されました)」のメッセージを表示しない設定を有効にするコードです。
これが表示されると、出力ファイルにも同様のメッセージを出力してしまうので、ここではselect文を実行する前にメッセージ非表示設定を有効にしています。
そして、select文を実行した後に「set nocount off;」でメッセージ非表示設定を無効にしています。



[ Sql2_Update.sql ]

UPDATE 価格情報
set    基準年月日 = 20250411;




[ Sql3_Delete.sql ]

DELETE 価格情報
WHERE  銘柄コード = '9997'

5.テスト環境

テスト用に用意した環境(ディレクトリ構成、テーブル、テーブルデータ)の情報です。

5-1.ディレクトリ構成

作成したバッチスクリプトは以下のディレクトリ構成で実行します。

5-2.テーブルレイアウト

テスト用に作成したテーブルは「価格情報」テーブルです。

[価格情報]テーブルレイアウト

No.項目名NULLKEY
1銘柄コードcharNG1
2価格numericNG
3利回りnumericNG
4NAVnumericNG
5NAV倍率numericNG
6時価総額numericNG
7出来高numericNG
8基準年月日charNG2

[テーブルクリエイト文]

CREATE TABLE [dbo].[価格情報](
  [銘柄コード] [char](4) NOT NULL,
  [価格] [numeric](18, 0) NOT NULL,
  [利回り] [numeric](9, 2) NOT NULL,
  [NAV] [numeric](12, 0) NOT NULL,
  [NAV倍率] [numeric](9, 2) NOT NULL,
  [時価総額] [numeric](12, 0) NOT NULL,
  [出来高] [numeric](12, 0) NOT NULL,
  [基準年月日] [char](8) NOT NULL,
PRIMARY KEY CLUSTERED
(
  [銘柄コード] ASC,[基準年月日] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

5-3.テーブルデータ

テスト用テーブル「価格情報」テーブルには以下のデータを投入しておきます。

[投入データ]

銘柄コード価格利回り1口NAVNAV倍率時価総額出来高基準年月日
8991992005.401232680.8046336188520250327
89921545003.421288711.2010531041720250327
99971180005.551234950.9641532105120250327
99981167005.611234950.9441075158520250327
9999993005.401232680.8146383334420250327

[データインサート文]

insert into 価格情報 values(8991,99200,5.40,123268,0.80,46336,1885,20250327);
insert into 価格情報 values(8992,154500,3.42,128871,1.20,105310,417,20250327);
insert into 価格情報 values(9997,118000,5.55,123495,0.96,41532,1051,20250327);
insert into 価格情報 values(9998,116700,5.61,123495,0.94,41075,1585,20250327);
insert into 価格情報 values(9999,99300,5.40,123268,0.81,46383,3344,20250327);

6.機能説明

①開始処理
・処理開始メッセージを以下の書式で標準出力とログファイルに出力する。
[スクリプトID YYYY年MM月DD日 hh時mm分ss秒 処理開始]


②価格情報テーブル select処理Sql1_Select.sql
・SQLCMDでselect文を実行し、実行結果をCSVファイルとして出力する。
・SQLCMDの結果ステータスが0の場合、正常終了処理を行う。
・SQLCMDの結果ステータスが0以外の場合、異常終了処理を行う。


③価格情報テーブル update処理Sql2_Update.sql
・SQLCMDでupdate文を実行し、価格情報テーブル全件に対し基準年月日を「20250411」に更新する。
・SQLCMDの結果ステータスが0の場合、正常終了処理を行う。
・SQLCMDの結果ステータスが0以外の場合、異常終了処理を行う。


④価格情報テーブル delete処理Sql3_Delete.sql
・SQLCMDでdelete文を実行し、価格情報テーブルに対し銘柄コードが「9997」のレコードを削除する。
・SQLCMDの結果ステータスが0の場合、正常終了処理を行う。
・SQLCMDの結果ステータスが0以外の場合、異常終了処理を行う。


⑤正常終了処理
・正常終了メッセージを以下の書式で標準出力とログファイルに出力し処理を終了する。
[スクリプトID YYYY年MM月DD日 hh時mm分ss秒 正常終了]


⑥異常終了処理
・エラーコードを標準出力とログファイルに出力する。
・異常終了メッセージを以下の書式で標準出力とログファイルに出力し処理を終了する。
[スクリプトID YYYY年MM月DD日 hh時mm分ss秒 異常終了]


7.実行結果確認

①処理対象のテーブルが作成され、データが投入されていることを確認する。

②バッチスクリプトを実行

③ログファイルを確認し、処理が正常終了していることを確認

④ファイル出力先のディレクトリにファイルが作成され、[価格情報]テーブルの内容がCSV形式で出力されていることを確認

⑤処理対象のテーブル「価格情報」テーブルをselectし、Update、deleteが行われていることを確認
・基準年月日が「20250411」に更新されていることを確認
・銘柄コードが「9997」のレコードが削除されていることを確認


8.おわりに

バッチスクリプトからSQLコマンドを実行する方法としては、SQLCMDの-qオプションの”コマンドラインクエリ”でバッチスクリプト内に直書きすることでもできますが、今回のようにSQL文をスクリプトファイルとして外出しすることもできます。

ひとつのバッチスクリプトで複数の処理を実行していた場合、バッチスクリプト内にSQL文を直書きしているとひとつのSQL文だけを修正しても、バッチスクリプトに修正を加えていることにもなるので、バッチスクリプト内の他の処理への無影響確認が必要になります。

SQL文をスクリプトファイルで外だししておくとSQL文に修正が入った場合にスクリプトファイルだけ修正してバッチスクリプト側は修正無しですむので、影響テストの範囲が若干絞れます。
クエリ実行の結果比較だけでいいよとか、とはいえこの場合も結局バッチスクリプトからの稼働確認は必要ですが。

あとはよく使うSQL文を別だししておくと色んなバッチスクリプトから流用ができるとかもありますね。それもストアド(ストアドプロシージャ)に登録しちゃったほうが良くない?とかあるんですが。

(-qオプションでのクエリ実行は、記述クエリの文字数に制限があったような気がしてましたが、検索してもそんな情報出てきませんでした。。。記憶違いか、別のソフトとかと勘違いしてるのかなぁ?)

以上です。
宜しくお願い致します。


タイトルとURLをコピーしました