前言

SQLCMD 是隨 SQL Server 一起安裝的命令列工具,日常用途涵蓋執行維護腳本、匯入匯出資料、串接 Windows 排程器自動跑批次作業。比起 SSMS 的 GUI,它的優勢在於可以寫成批次檔、進版控、在沒有桌面環境的伺服器上執行。

SQLCMD 功能概觀 SQLCMD 資料匯入匯出 執行SQL指令 資料庫備份 自動化排程

SQLCMD 基本語法

常用旗標一覽:

1
2
3
4
5
6
7
8
9
10
11
sqlcmd
-S <伺服器名稱> -- 指定 SQL Server 執行個體(可含 \InstanceName 或 ,Port)
-U <使用者名稱> -- SQL Server 登入帳號
-P <密碼> -- 對應密碼
-E -- 使用 Windows 整合驗證(省略 -U/-P,優先選此)
-d <資料庫名稱> -- 連線後預設切換到這個資料庫
-i <輸入檔案> -- 執行指定的 SQL 指令檔
-o <輸出檔案> -- 把查詢結果寫到檔案
-Q "<查詢>" -- 執行單行查詢後自動結束
-v 變數=值 -- 定義腳本變數(在腳本內以 $(變數名) 引用)
-r -- 將錯誤訊息輸出到 stderr(預設是 stdout)

Windows 整合驗證(-E)

在 Windows 網域環境下,-E 比帳密更常用也更安全——執行帳號的 Windows 身份直接對應到 SQL Server 登入,不需要在指令或環境變數裡放密碼:

1
sqlcmd -S ServerName -E -d MyDB -i "D:\scripts\maintenance.sql"

若伺服器設定了具名執行個體或非預設埠:

1
2
sqlcmd -S ServerName\SQLEXPRESS -E -d MyDB -Q "SELECT @@VERSION"
sqlcmd -S ServerName,1433 -E -d MyDB -Q "SELECT @@VERSION"

主要應用場景

1. 執行 SQL 指令檔

-i 一次只接受一個輸入檔,重複寫 -i file1 -i file2 後面的會蓋掉前面的,只有最後一個生效。要依序執行多個腳本,有兩種做法:

做法一:用 :r 在主腳本內 include 其他檔案

建一支 run_all.sql,用 :r 逐一引入:

1
2
3
4
-- run_all.sql
:r D:\scripts\step1_schema.sql
:r D:\scripts\step2_data.sql
:r D:\scripts\step3_index.sql

然後只呼叫一次 sqlcmd:

1
sqlcmd -S ServerName -E -d MyDB -i "D:\scripts\run_all.sql" -o "D:\logs\run.log"

做法二:在批次檔裡分次呼叫

1
2
3
4
5
6
@echo off
sqlcmd -S ServerName -E -d MyDB -i "D:\scripts\step1_schema.sql"
IF %ERRORLEVEL% NEQ 0 ( echo step1 失敗 & exit /b 1 )

sqlcmd -S ServerName -E -d MyDB -i "D:\scripts\step2_data.sql"
IF %ERRORLEVEL% NEQ 0 ( echo step2 失敗 & exit /b 1 )

2. 資料匯入

執行外部 SQL 腳本把資料匯入目標資料庫(常見於還原純文字備份或跑遷移腳本):

1
sqlcmd -S 192.168.0.3 -E -d TargetDB -i "D:\backup.sql"

若伺服器只開 SQL Server 驗證:

1
sqlcmd -S 192.168.0.3 -U DBAdmin -P MyPassword -d TargetDB -i "D:\backup.sql"

-r 旗標的用途是把錯誤訊息導向 stderr(以便在 shell 管線中分離正常輸出與錯誤),跟「匯入」本身無直接關係,加不加不影響匯入行為。

3. 查詢結果輸出

-Q-o 把查詢結果寫入檔案:

1
sqlcmd -S ServerName -E -d SourceDB -Q "SELECT * FROM Users" -o "D:\export.txt"

注意:-o 輸出的是 sqlcmd 預設的純文字表格格式(含欄位對齊空白),不是 CSV。若要後續用程式處理這份輸出,建議改用 -s"," 設定欄位分隔符加 -W 去掉尾端空白:

1
sqlcmd -S ServerName -E -d SourceDB -Q "SET NOCOUNT ON; SELECT col1, col2 FROM Users" -s"," -W -o "D:\export.csv"

若資料量大或格式需求複雜,bcp 工具是更適合的選擇。

4. 資料庫備份

透過 SQLCMD 執行 BACKUP 語句:

1
2
3
4
5
:: 完整備份
sqlcmd -S ServerName -E -Q "BACKUP DATABASE MyDB TO DISK='D:\Backups\MyDB.bak'"

:: 差異備份
sqlcmd -S ServerName -E -Q "BACKUP DATABASE MyDB TO DISK='D:\Backups\MyDB_Diff.bak' WITH DIFFERENTIAL"

5. 自動化排程(批次檔)

結合 Windows 工作排程器:

1
2
3
4
5
6
@echo off
sqlcmd -S ServerName -E -d MyDB -i "D:\DailyMaintenance.sql" -o "D:\logs\daily.log"
IF %ERRORLEVEL% NEQ 0 (
echo [ERROR] 維護腳本執行失敗,結束碼:%ERRORLEVEL%
exit /b 1
)

進階技巧

變數使用

SQLCMD 腳本變數($(變數名))只在腳本內容或 -Q 的查詢字串中做替換,不適用於 -S-U-P 這些連線參數——那些位置由 shell 展開,不是 SQLCMD 處理。

-v 定義變數,在腳本或查詢字串內引用:

1
2
3
4
5
:: 腳本內使用(maintenance.sql 內寫 $(DB))
sqlcmd -S MyServer -E -v DB="MyDatabase" -i maintenance.sql

:: 在 -Q 查詢字串內直接引用
sqlcmd -S MyServer -E -v DB="MyDatabase" -Q "SELECT COUNT(*) FROM $(DB).dbo.Users"

注意:變數值直接做字串替換,沒有任何 quoting 或逸出。若變數值來自外部輸入,攻擊者可以注入任意 SQL。僅在受控的管理腳本中使用 -v,不要把外部輸入直接當作變數值帶入。

若要讓伺服器名稱也可替換,用 shell 變數帶入連線參數(批次檔的 %SERVER%、PowerShell 的 $env:SERVER);或在腳本內用 :connect $(MyServer) 語法。

錯誤處理

SQLCMD 有兩套獨立的機制,用途不同:

  • SQLCMD 端:on error exit 讓 sqlcmd 遇到批次錯誤時立即結束並回傳非零結束碼。
  • T-SQL 層BEGIN TRY / BEGIN CATCH 在伺服器端攔截執行錯誤。

:setvar:on error 等 SQLCMD 命令在解析階段就無條件處理,跟 T-SQL 執行流程無關——把 :setvar 寫進 BEGIN CATCH 裡不會讓它變成「只在錯誤時執行」。

實務上兩者分開各司其職:

1
2
3
4
5
6
:: 批次檔層:判斷 sqlcmd 結束碼
sqlcmd -S MyServer -E -i "D:\scripts\task.sql"
IF %ERRORLEVEL% NEQ 0 (
echo [ERROR] SQLCMD 執行失敗,結束碼:%ERRORLEVEL%
exit /b 1
)
1
2
3
4
5
6
7
8
9
10
11
12
13
-- task.sql:T-SQL 層的錯誤處理
BEGIN TRY
SELECT 1 / 1;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage,
ERROR_SEVERITY() AS Severity;
-- THROW 讓 sqlcmd 偵測到錯誤並回傳非零結束碼
THROW;
END CATCH
GO

安全注意事項

密碼管理:避免把密碼直接寫在指令裡(容易出現在 shell history 和批次檔裡)。有兩種常見替代方式:

1
2
3
4
:: 方式一:用 SQLCMDPASSWORD 環境變數
set SQLCMDPASSWORD=MyPassword
sqlcmd -S ServerName -U DBAdmin -d MyDB -i "D:\scripts\task.sql"
set SQLCMDPASSWORD=
1
2
:: 方式二(Windows 網域環境首選):-E 整合驗證,完全不需要密碼
sqlcmd -S ServerName -E -d MyDB -i "D:\scripts\task.sql"

其他要點:

  • 大型檔案匯入時建議用批次處理,避免單一交易過大導致記錄檔爆增
  • SQLCMD 執行帳號應遵循最小權限原則,不要用 sa 跑日常維護腳本
  • 務必加入錯誤處理與記錄機制,方便排除問題