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

生成表數據 INSERT 腳本的通用存儲過程 (支持標識列、NULL、二進制、大值類型)

     編輯:關於SqlServer

功能定位

將指定表的所有數據導出為 `INSERT INTO ... VALUES (...), (...), ...` 批量腳本,支持跨環境遷移、版本發布種子數據、測試數據快照。優於 SSMS「生成腳本」之處:純 T-SQL、可參數化調用、可集成到部署管線、支持大值類型分塊。

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

CREATE PROCEDURE dbo.usp_GenerateInsertScript
    @SchemaName     SYSNAME = 'dbo',
    @TableName      SYSNAME,
    @BatchSize      INT = 1000,           -- 每批 VALUES 行數
    @IncludeIdentity BIT = 1,             -- 是否包含 IDENTITY 列 (需目標表 SET IDENTITY_INSERT ON)
    @WhereClause    NVARCHAR(MAX) = NULL, -- 可選過濾條件
    @Script         NVARCHAR(MAX) OUTPUT  -- 返回腳本
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @FullName NVARCHAR(517) = QUOTENAME(@SchemaName) + '.' + QUOTENAME(@TableName);
    DECLARE @Cols NVARCHAR(MAX), @ColList NVARCHAR(MAX), @Sql NVARCHAR(MAX);
    DECLARE @HasIdentity BIT, @IdentityCol SYSNAME;

    -- 1. 檢查表存在
    IF OBJECT_ID(@FullName, 'U') IS NULL
    BEGIN
        RAISERROR('Table %s not found', 16, 1, @FullName);
        RETURN -1;
    END

    -- 2. 收集列信息 (排除 timestamp、計算列、RowGuidCol 可選)
    SELECT @Cols = COALESCE(@Cols + N', ', N'') + QUOTENAME(c.name),
           @ColList = COALESCE(@ColList + N', ', N'') +
               CASE
                   WHEN c.system_type_id IN (34, 35, 99, 241, 242) THEN  -- image/text/ntext/xml/CLR UDT
                       N'CONVERT(NVARCHAR(MAX), ' + QUOTENAME(c.name) + N', 1)'  -- 簡化演示,實際需分塊
                   WHEN c.system_type_id IN (165, 167, 173, 175, 231, 239) THEN -- char/varchar/binary/varbinary/nchar/nvarchar
                       N'''''''' + REPLACE(CONVERT(NVARCHAR(MAX), ' + QUOTENAME(c.name) + N'), N'''''', N'''''''''') + N'''''''
                   WHEN c.system_type_id IN (40, 41, 42, 43, 58, 59, 60, 61, 62, 106, 108, 122, 127) THEN -- 日期時間類
                       N'''''''' + CONVERT(NVARCHAR(MAX), ' + QUOTENAME(c.name) + N', 126) + N'''''''  -- ISO8601
                   WHEN c.system_type_id IN (36, 48, 52, 56, 63, 104, 127) THEN -- 數值類
                       N'CONVERT(NVARCHAR(MAX), ' + QUOTENAME(c.name) + N')'
                   WHEN c.system_type_id = 104 THEN -- bit
                       N'CASE ' + QUOTENAME(c.name) + N' WHEN 1 THEN ''1'' ELSE ''0'' END'
                   ELSE N'NULL' -- 未知類型保守處理
               END
    FROM sys.columns c
    JOIN sys.types t ON c.user_type_id = t.user_type_id
    WHERE c.object_id = OBJECT_ID(@FullName)
      AND c.is_computed = 0
      AND t.name NOT IN ('timestamp', 'rowversion')
    ORDER BY c.column_id;

    -- 3. 標識列處理
    SELECT @HasIdentity = 1, @IdentityCol = QUOTENAME(name)
    FROM sys.columns
    WHERE object_id = OBJECT_ID(@FullName) AND is_identity = 1;

    IF @HasIdentity = 1 AND @IncludeIdentity = 0
    BEGIN
        -- 從列列表移除標識列
        SET @Cols = REPLACE(@Cols, @IdentityCol + N', ', N'');
        SET @Cols = REPLACE(@Cols, N', ' + @IdentityCol, N'');
        SET @Cols = REPLACE(@Cols, @IdentityCol, N'');
        -- 同步移除 @ColList 對應部分 (簡化:此處略過,實際需同步解析)
    END

    -- 4. 構建動態查詢 (分批輸出避免腳本過大)
    SET @Sql = N'DECLARE @Batch INT = 0, @RowNum INT = 0, @Script NVARCHAR(MAX) = N'''';
                 SELECT @RowNum = COUNT(*) FROM ' + @FullName +
                 CASE WHEN ISNULL(@WhereClause, N'') = N'' THEN N'' ELSE N' WHERE ' + @WhereClause END + N';
                 WHILE @Batch * ' + CAST(@BatchSize AS NVARCHAR(10)) + N' < @RowNum
                 BEGIN
                     SELECT @Script = @Script +
                         N''INSERT INTO ' + @FullName + N' (' + @Cols + N') VALUES '' +
                         STUFF((
                             SELECT N'', ('' + ' + @ColList + N' + N'')''
                             FROM (
                                 SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RN
                                 FROM ' + @FullName +
                                 CASE WHEN ISNULL(@WhereClause, N'') = N'' THEN N'' ELSE N' WHERE ' + @WhereClause END + N'
                             ) t
                             WHERE t.RN BETWEEN @Batch * ' + CAST(@BatchSize AS NVARCHAR(10)) + N' + 1 AND (@Batch + 1) * ' + CAST(@BatchSize AS NVARCHAR(10)) + N'
                             FOR XML PATH(N''''), TYPE
                         ).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 2, N'''') + N'';'' + CHAR(13) + CHAR(10);
                     SET @Batch = @Batch + 1;
                 END
                 SELECT @Script;';

    EXEC sp_executesql @Sql, N'@Script NVARCHAR(MAX) OUTPUT', @Script OUTPUT;
END;
GO

調用示例

DECLARE @Script NVARCHAR(MAX);
EXEC dbo.usp_GenerateInsertScript
    @SchemaName = 'dbo',
    @TableName = 'ConfigSettings',
    @BatchSize = 500,
    @IncludeIdentity = 1,
    @WhereClause = 'Env = ''Prod''',
    @Script = @Script OUTPUT;

-- 結果輸出到文件或打印
PRINT @Script;  -- SSMS 結果窗口可能截斷,建議 sqlcmd -o 或 PowerShell Out-File

大值類型 (MAX/FILESTREAM) 特殊處理

上述腳本對 `VARCHAR(MAX)`/`VARBINARY(MAX)`/`XML` 做簡化 `CONVERT`,實際生產建議:

  1. 導出時按 8000 字符分塊,生成 `UPDATETEXT` / `.WRITE()` 追加腳本。
  2. 或使用 `bcp` 原生導出格式文件 (`.fmt`),二進制精確、性能最高。
  3. 或集成 **SSIS Script Component** / **SqlPackage.exe** (DACFx) 進行專業級數據部署。

替代方案對比

工具/方法優點缺點適用場景
SSMS Generate ScriptsGUI 易用、支持架構+數據、可選擇對象大表極慢、易 OOM、難自動化小表、手工一次性遷移
bcp out / in極快、原生二進制、支持並行、格式文件可復用需命令行、格式文件維護、不含 INSERT 語句大數據量遷移、ETL 管線
本存儲過程純 T-SQL、可參數化、可嵌入部署腳本、支持 WHERE 過濾大值類型處理複雜、極大表腳本巨大種子數據、配置表、中小業務表、CI/CD 管線
SqlPackage / DACFx專業級、狀態比對、增量發布、支持 Azure SQL學習曲線、需 .NET 環境項目級數據庫版本化部署

擴展建議

  • 加入 `@OrderByClause` 參數,保證導出順序確定性 (利於版本對比)。
  • 加入 `@DisableTriggers`/`@DisableConstraints` 開關,生成腳本頭尾自動包含 `ALTER TABLE ... NOCHECK CONSTRAINT ALL` / `DISABLE TRIGGER ALL`。
  • 集成到 **Flyway / Liquibase / DbUp** 遷移腳本中,實現「架構遷移 + 基線數據」一體化部署。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved