本文內容為 **SQL Server (T-SQL) 存儲過程**,使用 `bcp` 工具與 `xp_cmdshell` 實現二進制大對象 (BLOB) 導入導出,**非 MySQL 技術**。MySQL 對應功能請使用 `SELECT ... INTO OUTFILE` / `LOAD DATA INFILE` 或應用層流式處理。
CREATE PROCEDURE dbo.p_binaryIO
@servename VARCHAR(30), -- 服務器名稱
@username VARCHAR(30), -- 用戶名
@password VARCHAR(30), -- 密碼
@tbname VARCHAR(500), -- 數據庫..表名 (如 db..tbl)
@fdname VARCHAR(30), -- 目標字段名 (image/text/ntext)
@fname VARCHAR(1000), -- 文件完整路徑 (導出目標/導入源)
@tj VARCHAR(1000) = '', -- WHERE 條件 (導入時含字段需加表前綴)
@isout BIT = 1 -- 1=導出 (默認), 0=導入
AS
BEGIN
SET NOCOUNT ON;
DECLARE @fname_in VARCHAR(1000), -- bcp 格式文件
@fsize VARCHAR(20), -- 文件大小
@m_tbname VARCHAR(50), -- 臨時表名
@sql VARCHAR(8000);
-- 1. 獲取導入文件大小 (導入模式)
IF @isout = 0 BEGIN
CREATE TABLE #tb (
[可選名] VARCHAR(20), [大小] INT,
[創建日期] VARCHAR(10), [創建時間] VARCHAR(20),
[上次寫操作日期] VARCHAR(10), [上次寫操作時間] VARCHAR(20),
[上次訪問日期] VARCHAR(10), [上次訪問時間] VARCHAR(20), [特性] INT
);
INSERT INTO #tb EXEC master..xp_getfiledetails @fname;
SELECT @fsize = [大小] FROM #tb;
DROP TABLE #tb;
IF @fsize IS NULL BEGIN
PRINT '文件未找到'; RETURN;
END
END ELSE SET @fsize = '0';
-- 2. 生成 bcp 格式文件 (描述字段結構)
SET @m_tbname = '[##temp' + CAST(NEWID() AS VARCHAR(40)) + ']';
SET @sql = 'SELECT * INTO ' + @m_tbname + ' FROM (
SELECT NULL AS 類型
UNION ALL SELECT 0 AS 前綴
UNION ALL SELECT ' + @fsize + ' AS 長度
UNION ALL SELECT NULL AS 結束
UNION ALL SELECT NULL AS 格式
) a';
EXEC(@sql);
SET @fname_in = @fname + '_temp';
SET @sql = 'bcp "' + @m_tbname + '" out "' + @fname_in + '" /S"' + @servename + '"'
+ CASE WHEN ISNULL(@username,'')='' THEN '' ELSE ' /U"' + @username + '"' END
+ ' /P"' + ISNULL(@password,'') + '" /c';
EXEC master..xp_cmdshell @sql;
DROP TABLE @m_tbname; -- 清理臨時表
-- 3. 執行導出或導入
IF @isout = 1 BEGIN
-- 導出: 查詢單行字段內容導出至文件
SET @sql = 'bcp "SELECT TOP 1 ' + @fdname + ' FROM ' + @tbname
+ CASE WHEN ISNULL(@tj,'')='' THEN '' ELSE ' WHERE ' + @tj END
+ '" QUERYOUT "' + @fname + '" /S"' + @servename + '"'
+ CASE WHEN ISNULL(@username,'')='' THEN '' ELSE ' /U"' + @username + '"' END
+ ' /P"' + ISNULL(@password,'') + '" /i"' + @fname_in + '"';
EXEC master..xp_cmdshell @sql;
END ELSE BEGIN
-- 導入: 建立臨時表 -> bcp in -> 更新目標表
SET @sql = 'SELECT TOP 0 ' + @fdname + ' INTO ' + @m_tbname + ' FROM ' + @tbname;
EXEC(@sql);
SET @sql = 'bcp "' + @m_tbname + '" IN "' + @fname + '" /S"' + @servename + '"'
+ CASE WHEN ISNULL(@username,'')='' THEN '' ELSE ' /U"' + @username + '"' END
+ ' /P"' + ISNULL(@password,'') + '" /i"' + @fname_in + '"';
EXEC master..xp_cmdshell @sql;
SET @sql = 'UPDATE ' + @tbname + ' SET ' + @fdname + ' = b.' + @fdname
+ ' FROM ' + @tbname + ' a, ' + @m_tbname + ' b'
+ CASE WHEN ISNULL(@tj,'')='' THEN '' ELSE ' WHERE ' + @tj END;
EXEC(@sql);
DROP TABLE @m_tbname;
END
-- 4. 清理格式文件
SET @sql = 'DEL ' + @fname_in;
EXEC master..xp_cmdshell @sql;
END;
GO
-- 導出: 將 db..tb 表中符合條件的 img 字段導出到文件
EXEC p_binaryIO 'SERVER1', 'sa', 'pwd', 'mydb..mytable', 'img', 'C:\export\img.dat', 'id=100', 1;
-- 導入: 將文件導入到符合條件的行 (覆蓋模式)
EXEC p_binaryIO 'SERVER1', 'sa', 'pwd', 'mydb..mytable', 'img', 'C:\import\img.dat', 'id=100', 0;
| 組件 | 作用 |
|---|---|
bcp (Bulk Copy Program) | SQL Server 命令行批量複製工具,高性能數據移動 |
xp_cmdshell | 擴展存儲過程,在 SQL Server 進程內執行 OS 命令 (需啟用) |
| 格式文件 (.fmt / 此處為臨時表生成) | 描述數據文件結構:字段類型、長度、終止符、前綴長度等 |
| `image` / `text` / `ntext` | SQL Server 舊版大字段類型 (已棄用,現用 `varbinary(max)`/`varchar(max)`/`nvarchar(max)`) |
| 場景 | SQL Server (BCP) | MySQL |
|---|---|---|
| 導出 BLOB 到文件 | `bcp QUERYOUT` + 格式文件 | `SELECT blob_col INTO DUMPFILE '/path/file' FROM t WHERE id=1;` (單行單文件) |
| 批量導出 | 同上 | 應用層分批 `SELECT` 寫文件、或 `mysqldump --tab` (僅文本) |
| 導入文件到 BLOB | `bcp IN` + 臨時表 UPDATE | `LOAD_FILE('/path/file')` 在 INSERT/UPDATE 中使用 (需 `secure_file_priv`、文件大小限制) |
| 大文件流式處理 | BCP 原生流式 | 應用層分片寫入、或 UDF (如文中 176693/176694) |
此存儲過程展示了 SQL Server 早期處理 BLOB 導入導出的經典模式:**bcp + 格式文件 + xp_cmdshell + 臨時表中轉**。雖能工作,但屬於**舊技術棧**,現代架構應遷移至更安全、可維護、跨平台的方案。MySQL 用戶請參考應用層流式處理或 UDF 方案 (文章 176693/176694)。