首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 高效批量生成 INSERT 腳本存儲過程:支援單行/多行模式、自動處理各類資料型別

     編輯:關於SqlServer

專案背景

開發/測試/遷移場景常需將現有表資料導出為 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] 完整限定名稱

存儲過程完整代碼 (SQL Server 2005+)

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');

1. 單行模式輸出 (適合小量資料、跨版本相容)

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')

單行模式執行結果

2. 多行模式輸出 (適合大量資料、高效批次執行)

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。

3. 帶條件篩選

EXEC dbo.usp_GetInsertSQL @chvnTable = N'UserLoginInfo', @chvnWhere = N'Name = ''zhang''', @bitIsSingleRow = 0;

進階擴展建議

  1. 支援 IDENTITY_INSERT:若需保留自增 ID,自動在腳本首尾加上 SET IDENTITY_INSERT ... ON/OFF。
  2. 大物件 (LOB) 處理:varchar(max)/varbinary(max) 輸出時截斷或輸出 CONVERT 風格,避免腳本過大。
  3. 輸出至檔案:結合 xp_cmdshell + bcp 或 CLR 直接寫入 .sql 檔案,實現全自動化備份/遷移管線。
  4. 異構遷移:擴充 @TargetDialect 參數,輸出 MySQL/PostgreSQL/Oracle 相容語法。

總結

usp_GetInsertSQL 以 **動態 SQL + 系統目錄視圖** 實現了通用、健壯、高效的資料腳本化功能。雙模式設計兼顧相容與效能,型別轉換邏輯覆蓋 99% 常見場景。建議部署至所有開發/測試/生產實例的 master 或工具資料庫,作為 DBA/開發人員的標準工具箱組件。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved