BCP (Bulk Copy Program) 是 SQL Server 高效能大量複製工具,支援 **image、text、ntext** 等大型物件(LOB)欄位的檔案級匯出入。本文提供一個封裝良好的預存程序 p_binaryIO,實現單指令雙向操作,並解析關鍵邏輯與呼叫範例。
CREATE PROC p_binaryIO
@servename VARCHAR(30), -- 伺服器名稱
@username VARCHAR(30), -- 登入帳號
@password VARCHAR(30), -- 密碼
@tbname VARCHAR(500), -- 資料庫..資料表名
@fdname VARCHAR(30), -- 目標欄位名
@fname VARCHAR(1000), -- 完整檔案路徑(處理中會產生 .bak)
@tj VARCHAR(1000)='', -- WHERE 條件(匯入時若含目標欄位需加表前綴)
@isout BIT=1 -- 1=匯出(預設), 0=匯入
AS
BEGIN
SET NOCOUNT ON;
DECLARE @fname_in VARCHAR(1000), -- 格式檔路徑
@fsize VARCHAR(20), -- 檔案大小
@m_tbname VARCHAR(50), -- 暫存格式表名
@sql NVARCHAR(MAX);
/*--- 取得匯入檔案大小 ---*/
IF @isout = 1 SET @fsize = '0';
ELSE BEGIN
CREATE TABLE #tb (fname VARCHAR(20), fsize INT, ...);
INSERT #tb EXEC master..xp_getfiledetails @fname;
SELECT @fsize = fsize FROM #tb;
DROP TABLE #tb;
IF @fsize IS NULL BEGIN PRINT '檔案不存在'; RETURN; END
END
/*--- 產生 BCP 格式檔 ---*/
SET @m_tbname = N'[##temp' + CAST(NEWID() AS VARCHAR(40)) + N']';
SET @sql = N'SELECT * INTO ' + @m_tbname + N' FROM (
SELECT NULL AS [類型]
UNION ALL SELECT 0 AS [前綴]
UNION ALL SELECT ' + @fsize + N' AS [長度]
UNION ALL SELECT NULL AS [結束]
UNION ALL SELECT NULL AS [格式]
) a';
EXEC(@sql);
SET @fname_in = @fname + N'_temp';
SET @sql = N'bcp "' + @m_tbname + N'" out "' + @fname_in + N'" /S"' + @servename + N'"' +
CASE WHEN ISNULL(@username,'')='' THEN N'' ELSE N' /U"' + @username + N'"' END +
N' /P"' + ISNULL(@password,'') + N'" /c';
EXEC master..xp_cmdshell @sql;
SET @sql = N'DROP TABLE ' + @m_tbname; EXEC(@sql);
/*--- 匯出 ---*/
IF @isout = 1 BEGIN
SET @sql = N'bcp "SELECT TOP 1 ' + @fdname + N' FROM ' + @tbname +
CASE WHEN ISNULL(@tj,'')='' THEN N'' ELSE N' WHERE ' + @tj END +
N'" QUERYOUT "' + @fname + N'" /S"' + @servename + N'"' +
CASE WHEN ISNULL(@username,'')='' THEN N'' ELSE N' /U"' + @username + N'"' END +
N' /P"' + ISNULL(@password,'') + N'" /i"' + @fname_in + N'"';
EXEC master..xp_cmdshell @sql;
END
/*--- 匯入 ---*/
ELSE BEGIN
SET @sql = N'SELECT TOP 0 ' + @fdname + N' INTO ' + @m_tbname + N' FROM ' + @tbname; EXEC(@sql);
SET @sql = N'bcp "' + @m_tbname + N'" IN "' + @fname + N'" /S"' + @servename + N'"' +
CASE WHEN ISNULL(@username,'')='' THEN N'' ELSE N' /U"' + @username + N'"' END +
N' /P"' + ISNULL(@password,'') + N'" /i"' + @fname_in + N'"';
EXEC master..xp_cmdshell @sql;
SET @sql = N'UPDATE ' + @tbname + N' SET ' + @fdname + N'=b.' + @fdname +
N' FROM ' + @tbname + N' a, ' + @m_tbname + N' b' +
CASE WHEN ISNULL(@tj,'')='' THEN N'' ELSE N' WHERE ' + @tj END;
EXEC(@sql);
SET @sql = N'DROP TABLE ' + @m_tbname; EXEC(@sql);
END
/*--- 清理格式檔 ---*/
SET @sql = N'DEL ' + @fname_in; EXEC master..xp_cmdshell @sql;
END
EXEC p_binaryIO
'MyServer', 'sa', 'pwd',
'MyDB.dbo.Documents', 'FileContent',
'C:\Export\doc_001.pdf', '', 1;
EXEC p_binaryIO
'MyServer', 'sa', 'pwd',
'MyDB.dbo.Documents', 'FileContent',
'C:\Import\doc_001.pdf', 'DocID=100', 0;
bcp ... out ... /c 產生字元模式格式檔。bcp.exe,需啟用 sp_configure 'xp_cmdshell', 1,且執行帳號具備 OS 層檔案讀寫權限。UPDATE ... FROM 將暫存表欄位值回寫目標表符合條件的列。@tj 條件將套用於**所有符合列**,匯入會覆蓋多筆記錄,使用時務必確認條件精確度。| 方案 | 優點 | 缺點 |
|---|---|---|
| BCP / p_binaryIO | 無需應用程式、極高吞吐、支援舊版 SQL Server | 需 xp_cmdshell、OS 層權限、除錯困難 |
| SqlClient SqlBulkCopy (C#) | Managed 程式碼、交易支援、型別安全 | 需編寫應用程式 |
| FILESTREAM / FileTable | 資料庫管理檔案、Transactional NTFS、T-SQL 存取 | 需啟用檔案流、較複雜部署 |
| VARBINARY(MAX) + 應用層讀寫 | 簡單、跨平台、ORM 友善 | 大檔案記憶體壓力、備份體積大 |
BCP 仍是大批量二進位資料遷移、異構匯出入的利器,善用 p_binaryIO 封裝可大幅降低操作複雜度。