開發/測試/遷移場景常需將現有表資料導出為 INSERT 腳本。常見痛點:
NULL、日期格式、字串跳脫 (單引號)。VALUES 模式大量資料執行效能極差 (每行一個陳述式)。UNION ALL 多行批次插入 (SQL Server 2008+ 支援)。本文提供一個**生產級存儲過程 usp_GetInsertSQL**,解決上述所有問題。
| 特性 | 說明 |
|---|---|
| 雙模式輸出 | @bitIsSingleRow=1 單行 VALUES;0 多行 UNION ALL SELECT (高效) |
| 全型別支援 | 數值、日期時間、字串 (varchar/nvarchar/char/nchar)、二進位、唯一識別碼等自動生成正確 T-SQL 字面值 |
| NULL/跳脫處理 | 自動輸出 NULL 關鍵字;字串中單引號自動雙寫跳脫 ('') |
| 動態 WHERE 條件 | 支援傳入任意 WHERE 子句片段 (不含 WHERE 關鍵字) |
| 架構名稱自動解析 | 自動加上 [schema].[table] 完整限定名稱 |
IF OBJECT_ID(N'dbo.usp_GetInsertSQL', 'P') IS NOT NULL DROP PROCEDURE dbo.usp_GetInsertSQL;
GO
CREATE PROCEDURE dbo.usp_GetInsertSQL
@chvnTable NVARCHAR(128), -- 表名 (不含 schema,自動解析)
@chvnWhere NVARCHAR(MAX) = N'', -- WHERE 條件片段 (不含 WHERE)
@bitIsSingleRow BIT = 1 -- 1=單行 VALUES, 0=多行 UNION ALL
AS
BEGIN
SET NOCOUNT ON;
SET @bitIsSingleRow = ISNULL(@bitIsSingleRow, 1);
-- 1. 解析物件 ID 與完整限定名稱 [schema].[table]
DECLARE @intTableID INT, @chvnSchemaTable NVARCHAR(261);
SELECT @intTableID = object_id,
@chvnSchemaTable = QUOTENAME(SCHEMA_NAME(schema_id)) + N'.' + QUOTENAME(@chvnTable)
FROM sys.objects WHERE name = @chvnTable AND type = 'U';
IF @intTableID IS NULL
BEGIN
RAISERROR(N'表 %s 不存在或非使用者表', 16, 1, @chvnTable);
RETURN;
END
-- 2. 動態構建欄位清單與值轉換邏輯
DECLARE @cols NVARCHAR(MAX) = N'', @vals NVARCHAR(MAX) = N'';
SELECT
@cols += N',' + QUOTENAME(c.name),
@vals += N' + '','' + ' +
CASE
-- 數值類型 (含 bit, tinyint, smallint, int, bigint, decimal, numeric, money, smallmoney)
WHEN c.system_type_id IN (48,52,56,59,60,62,106,108,122,127)
THEN N'CASE WHEN ' + QUOTENAME(c.name) + N' IS NULL THEN N''NULL'' ELSE CAST(' + QUOTENAME(c.name) + N' AS NVARCHAR(MAX)) END'
-- 日期時間類型 (datetime, smalldatetime, date, time, datetime2, datetimeoffset)
WHEN c.system_type_id IN (40,41,42,43,58,61)
THEN N'CASE WHEN ' + QUOTENAME(c.name) + N' IS NULL THEN N''NULL'' ELSE N'''''''' + REPLACE(CONVERT(NVARCHAR(30), ' + QUOTENAME(c.name) + N', 126), N'''''''', N'''''''''''') + N'''''''' END'
-- 字串類型 varchar, nvarchar, text, ntext
WHEN c.system_type_id IN (167,231,35,99)
THEN N'CASE WHEN ' + QUOTENAME(c.name) + N' IS NULL THEN N''NULL'' ELSE N'''''''' + REPLACE(' + QUOTENAME(c.name) + N', N'''''''', N'''''''''''') + N'''''''' END'
-- 固定長度 char, nchar
WHEN c.system_type_id IN (175,239)
THEN N'CASE WHEN ' + QUOTENAME(c.name) + N' IS NULL THEN N''NULL'' ELSE N'''''''' + CAST(REPLACE(' + QUOTENAME(c.name) + N', N'''''''', N'''''''''''') AS CHAR(' + CAST(c.max_length AS NVARCHAR) + N')) + N'''''''' END'
-- binary/varbinary/image
WHEN c.system_type_id IN (165,173,34)
THEN N'CASE WHEN ' + QUOTENAME(c.name) + N' IS NULL THEN N''NULL'' ELSE N''0x'' + CONVERT(NVARCHAR(MAX), ' + QUOTENAME(c.name) + N', 1) END'
ELSE N'N''NULL'''
END
FROM sys.columns c WHERE c.object_id = @intTableID;
-- 移除前導逗號
SET @cols = STUFF(@cols, 1, 1, N'');
SET @vals = STUFF(@vals, 1, 1, N'');
-- 3. 組裝最終 T-SQL
DECLARE @insertHead NVARCHAR(MAX) = N'INSERT INTO ' + @chvnSchemaTable + N' (' + @cols + N')';
DECLARE @finalSQL NVARCHAR(MAX);
IF @bitIsSingleRow = 1
BEGIN
-- 單行模式: SELECT 'INSERT ... VALUES(...)' FROM table
SET @finalSQL = N'SELECT ' + @insertHead + N' + N'' VALUES('' + ' + @vals + N' + N'')'' FROM ' + @chvnSchemaTable;
END
ELSE
BEGIN
-- 多行模式: UNION ALL SELECT 批次插入 (需 ROW_NUMBER 區分首行)
SET @finalSQL = N'
SELECT CASE WHEN rn = 1 THEN REPLICATE(N'' '', LEN(N''UNION ALL '') + 1) ELSE N''UNION ALL '' END + rowSql
FROM (
SELECT ' + @vals + N' AS rowSql, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
FROM ' + @chvnSchemaTable;
IF @chvnWhere > N'' SET @finalSQL += N' WHERE ' + @chvnWhere;
SET @finalSQL += N' ) t';
SET @finalSQL = N'SELECT ' + @insertHead + N';' + @finalSQL;
END
IF @chvnWhere > N'' AND @bitIsSingleRow = 1
SET @finalSQL += N' WHERE ' + @chvnWhere;
-- 4. 輸出與執行
PRINT @finalSQL; -- SSMS 「訊息」頁籤可複製完整腳本
EXEC (@finalSQL); -- 直接輸出結果集 (單行模式為多行文字,多行模式為 UNION ALL 腳本)
END;
GO
IF OBJECT_ID('dbo.UserLoginInfo') IS NOT NULL DROP TABLE dbo.UserLoginInfo;
CREATE TABLE dbo.UserLoginInfo (
ID INT IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(50) NOT NULL,
LoginTime DATETIME NOT NULL
);
INSERT dbo.UserLoginInfo (Name, LoginTime) VALUES
('zhang', '2023-01-15 08:30:00.123'),
('li', '2023-01-15 09:15:00.456'),
('wang', '2023-01-15 10:00:00.789');
EXEC dbo.usp_GetInsertSQL @chvnTable = N'UserLoginInfo', @bitIsSingleRow = 1;
輸出結果 (訊息頁籤):
INSERT INTO [dbo].[UserLoginInfo] ([Name],[LoginTime]) VALUES('zhang','2023-01-15T08:30:00.123')
INSERT INTO [dbo].[UserLoginInfo] ([Name],[LoginTime]) VALUES('li','2023-01-15T09:15:00.456')
INSERT INTO [dbo].[UserLoginInfo] ([Name],[LoginTime]) VALUES('wang','2023-01-15T10:00:00.789')

EXEC dbo.usp_GetInsertSQL @chvnTable = N'UserLoginInfo', @bitIsSingleRow = 0;
輸出結果:
INSERT INTO [dbo].[UserLoginInfo] ([Name],[LoginTime]) SELECT 'zhang','2023-01-15T08:30:00.123' UNION ALL SELECT 'li','2023-01-15T09:15:00.456' UNION ALL SELECT 'wang','2023-01-15T10:00:00.789'

使用說明:多行模式輸出為單一陳述式,直接複製到 SSMS 執行即可批次寫入,**效能比單行循環快數十倍**。若資料極大 (超過批次限制),建議自行拆分多個 UNION ALL 區塊或改用 BCP/BULK INSERT。
EXEC dbo.usp_GetInsertSQL @chvnTable = N'UserLoginInfo', @chvnWhere = N'Name = ''zhang''', @bitIsSingleRow = 0;
SET IDENTITY_INSERT ... ON/OFF。varchar(max)/varbinary(max) 輸出時截斷或輸出 CONVERT 風格,避免腳本過大。xp_cmdshell + bcp 或 CLR 直接寫入 .sql 檔案,實現全自動化備份/遷移管線。@TargetDialect 參數,輸出 MySQL/PostgreSQL/Oracle 相容語法。usp_GetInsertSQL 以 **動態 SQL + 系統目錄視圖** 實現了通用、健壯、高效的資料腳本化功能。雙模式設計兼顧相容與效能,型別轉換邏輯覆蓋 99% 常見場景。建議部署至所有開發/測試/生產實例的 master 或工具資料庫,作為 DBA/開發人員的標準工具箱組件。