This is my first windows batch file. The script basically goes through this basic flow of tasks, in order to update some specific SQL Server DBs to a new DB version:
- Backup all SQL server DBs that will be upgraded.
- Upgrade to the latest version and apply any last-minute patches to those DBs.
- Error handling is done when the process fails at any point, rolling back all DBs to their initial version (before the update).
I would appreciate your feedback on:
- What parts of the code could be improved in general (fix some bad practices, improve brittle code, etc).
- How to better organize (comments, separation of sub-sections, etc) the code and make it clearer (for easy maintenance).
- Any other aspect you think needs to be improved, specially regarding batch coding style and standarization.
Here is the full code:
:: MAIN SCRIPT OPTIONS
@echo off
setlocal EnableDelayedExpansion
:: ------------------------------------------------------------------------------------------------
:: PARAMETERS (ADJUST TO SPECIFIC ENVIRONMENT)
:: SQL Server: instance holding the DBs
set serverName=SERVER\SQLEXPRESS
:: SQL Server: user name and user password for performing the upgrade
set sqlUser=%1
set sqlPwd=%2
:: SQL Server: comma separated list of DBs in this server instance that should be upgraded
set sqlDBs=DB1,DB2
:: DB version: current version to be upgraded
set currentVersion=4.3.0
:: DB version: target version to upgrade to
set targetVersion=4.4.0
:: DB version: scripts (if multiple, quote and separate with commas) to run before applying the upgrade (leave blank if none is needed)
set prePatchScripts=
:: DB version: scripts (if multiple, quote and separate with commas) to run after applying the upgrade (leave blank if none is needed)
set postPatchScripts="Script1.sql,Script2.sql"
:: ------------------------------------------------------------------------------------------------
:: CALCULATED VARIABLES (DO NOT MODIFY)
:: Folders: scripts working directory
set workingDir=%~dp0
:: Folders: path to store the DB backups
set backupDir=%workingDir%Backups\
:: Folders: path where upgrade and patch scripts will be found
set scriptsDir=%workingDir%Scripts\
:: File names: name of the upgrade script to be applied
set updateScript=Update_SCM_%currentVersion%_a_%targetVersion%.sql
:: File names: name of the log file where messages will be written
set logFile=log.txt
:: ------------------------------------------------------------------------------------------------
:: BACK UP EVERY DB IN THE SERVER
call :echo_header "Backing up all DBs in the server"
for /d %%a in (%sqlDBs%) do (
echo Backing up database: %%a to %backupDir%%%a.bak
sqlcmd -b -S !serverName! -U !sqlUser! -P !sqlPwd! -Q "backup database [%%a] to Disk='!backupDir!%%a.bak' with init"
echo.
)
:: ------------------------------------------------------------------------------------------------
:: UPDATE AND PATCH EACH DB IN THE SERVER
call :echo_header "Starting DB update process"
for /d %%a in (%sqlDBs%) do (
if not [!prePatchScripts!]==[] call :run_script Pre-patching %%a !prePatchScripts!
if not errorlevel 1 call :run_script Updating %%a !updateScript!
if not errorlevel 1 if not [!postPatchScripts!]==[] call :run_script Post-patching %%a !postPatchScripts!
if errorlevel 1 goto :eof
echo.
)
:: ------------------------------------------------------------------------------------------------
:: END THE PROCESS SUCCESSFULLY
call :echo_header "SUCCESS: Update to %targetVersion% was successful!"
goto :eof
:: ------------------------------------------------------------------------------------------------
:: FUNCTION TO EXECUTE ONE SCRIPT ON ONE SPECIFIC DB
:: %1 the action to be performed on the DB (Updating, Pre-patching or Post-patching)
:: %2 the name of the database to execute the script on
:: %3 the name of the script(s) to be executed
:run_script
for /d %%b in (%~3) do (
echo %1 database %2 with script %%b
sqlcmd -b -S !serverName! -U !sqlUser! -P !sqlPwd! -d %2 -i !scriptsDir!%%b -o %logFile%
if errorlevel 1 (
echo Script failed on DB %2.
echo.
call :restore
exit /b 1
)
echo Script executed successfully.
)
exit /b 0
:: ------------------------------------------------------------------------------------------------
:: FUNCTION TO DISPLAY UPDATE STEP MESSAGES
:: %1 the text to be displayed as a header
:echo_header
echo ##########################################################################################
echo %~1
echo ##########################################################################################
echo.
goto :eof
:: ------------------------------------------------------------------------------------------------
:: PERFORM DB RESTORE PROCESS
:restore
call :echo_header "Starting DB restore process"
:: Restore DB backups
for /d %%a in (%sqlDBs%) do (
echo Restoring database: %%a from %backupDir%%%a.bak
sqlcmd -b -S !serverName! -U !sqlUser! -P !sqlPwd! -Q "alter database [%%a] set single_user with rollback immediate"
sqlcmd -b -S !serverName! -U !sqlUser! -P !sqlPwd! -Q "restore database [%%a] from Disk='!backupDir!%%a.bak' with replace"
echo.
)
call :echo_header "ERROR: Update to %targetVersion% failed. See log for details."