ALTER PROCEDURE dbo.DynamicBulkUpInsert
    @JsonData NVARCHAR(MAX)
AS
BEGIN
    SET NOCOUNT ON;
    
    -- 创建临时表存储解析数据
    CREATE TABLE #TableData (
        TableID INT IDENTITY(1,1) PRIMARY KEY,
        TableName SYSNAME NOT NULL,
        Records NVARCHAR(MAX) NOT NULL
    );
 
    -- 使用更健壮的JSON解析方法
    DECLARE
        @Start INT = 1,
        @TableStart INT,
        @TableEnd INT,
        @JsonStart INT,
        @JsonEnd INT,
        @TableName NVARCHAR(128),
        @Records NVARCHAR(MAX);
 
    WHILE @Start < LEN(@JsonData)
    BEGIN
        -- 查找表名起始位置
        SET @TableStart = CHARINDEX('"TableName":"', @JsonData, @Start);
        IF @TableStart = 0 BREAK;
        
        -- 提取表名
        SET @TableStart = @TableStart + LEN('"TableName":"');
        SET @TableEnd = CHARINDEX('"', @JsonData, @TableStart);
        SET @TableName = SUBSTRING(@JsonData, @TableStart, @TableEnd - @TableStart);
        
        -- 查找记录起始位置
        SET @JsonStart = CHARINDEX('"INSJson":', @JsonData, @TableEnd);
        IF @JsonStart = 0 
        BEGIN
            SET @Start = @TableEnd + 1;
            CONTINUE;
        END
        
        -- 确定JSON对象边界
        SET @JsonStart = @JsonStart + LEN('"INSJson":');
        DECLARE @BracketCount INT = 0;
        DECLARE @Pos INT = @JsonStart;
        
        WHILE @Pos <= LEN(@JsonData)
        BEGIN
            DECLARE @Char NCHAR(1) = SUBSTRING(@JsonData, @Pos, 1);
            
            IF @Char = N'[' OR @Char = N'{'
                SET @BracketCount = @BracketCount + 1;
            ELSE IF @Char = N']' OR @Char = N'}'
                SET @BracketCount = @BracketCount - 1;
                
            IF @BracketCount = 0
            BEGIN
                SET @JsonEnd = @Pos;
                BREAK;
            END
            
            SET @Pos = @Pos + 1;
        END
        
        IF @BracketCount <> 0 SET @JsonEnd = LEN(@JsonData);
        
        -- 提取记录 (保留JSON数组格式)
        SET @Records = SUBSTRING(@JsonData, @JsonStart, @JsonEnd - @JsonStart + 1);
        SET @Start = @JsonEnd + 1;
        
        -- 统一表名小写处理
        SET @TableName = LOWER(@TableName);
        
        INSERT INTO #TableData (TableName, Records)
        VALUES (@TableName, @Records);
    END;
 
    -- 使用游标处理每个表
    DECLARE @FullSql NVARCHAR(MAX) = N'';
    DECLARE @TableID INT, @MaxTableID INT;
    SELECT @MaxTableID = MAX(TableID) FROM #TableData;
    SET @TableID = 1;
 
    WHILE @TableID <= @MaxTableID
    BEGIN
        DECLARE @CurrentRecords NVARCHAR(MAX);
        SELECT @TableName = TableName, @CurrentRecords = Records
        FROM #TableData WHERE TableID = @TableID;
 
        -- 检查表是否存在
        IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE name = @TableName)
        BEGIN
            SET @TableID += 1;
            CONTINUE;
        END
 
        -- 获取主键 (兼容多列主键)
        DECLARE @PrimaryKeys NVARCHAR(MAX) = '';
        SELECT @PrimaryKeys = @PrimaryKeys + QUOTENAME(c.name) + ','
        FROM sys.index_columns ic
        JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
        JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
        WHERE i.is_primary_key = 1 AND OBJECT_NAME(ic.object_id) = @TableName
        ORDER BY ic.key_ordinal;
        
        IF LEN(@PrimaryKeys) > 0
            SET @PrimaryKeys = LEFT(@PrimaryKeys, LEN(@PrimaryKeys) - 1);
        
        IF @PrimaryKeys = ''
        BEGIN
            RAISERROR('Table %s has no primary key, cannot perform Upsert', 16, 1, @TableName);
            SET @TableID += 1;
            CONTINUE;
        END
 
        -- 创建临时表
        DECLARE @TempTable NVARCHAR(128) = N'#Temp_' + CAST(@TableID AS NVARCHAR(10));
        
        -- 获取所有列定义
        DECLARE @Columns NVARCHAR(MAX) = '';
        SELECT @Columns = @Columns + 
            ', ' + QUOTENAME(name) + ' NVARCHAR(MAX)'
        FROM sys.columns
        WHERE object_id = OBJECT_ID(@TableName)
        ORDER BY column_id;
        
        -- 修正:移除非键列列表的逗号
        IF LEN(@Columns) > 0 SET @Columns = STUFF(@Columns, 1, 2, '');
        
        DECLARE @CreateTempSql NVARCHAR(MAX) = N'
        CREATE TABLE ' + @TempTable + N' (
            ' + @Columns + N'
        );';
 
        -- 使用XML解析JSON数据
        DECLARE @InsertTempSql NVARCHAR(MAX) = N'
        DECLARE @RecordsXml XML = (SELECT CAST(''<root><row '' + 
            REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
                SUBSTRING(''' + REPLACE(@CurrentRecords, '''', '''''') + ''', 2, LEN(''' + REPLACE(@CurrentRecords, '''', '''''') + ''')-2),
                ''"'', ''''), '':'', ''="''), '','', ''" /><row ''), ''{'', ''''), ''}'', '''') + 
            ''" /></root>'' AS XML);
        
        INSERT INTO ' + @TempTable + N'
        SELECT ';
 
        -- 为每列生成CASE表达式
        DECLARE @ColumnList NVARCHAR(MAX) = '';
        SELECT @ColumnList = @ColumnList + 
            ', MAX(CASE WHEN x.v.value(''local-name(.)'', ''NVARCHAR(128)'') = ''' + name + ''' 
                THEN x.v.value(''.'', ''NVARCHAR(MAX)'') END) AS ' + QUOTENAME(name)
        FROM sys.columns
        WHERE object_id = OBJECT_ID(@TableName)
        ORDER BY column_id;
        
        -- 修正:移除非键列列表的逗号
        IF LEN(@ColumnList) > 0 SET @ColumnList = STUFF(@ColumnList, 1, 2, '');
        
        SET @InsertTempSql = @InsertTempSql + @ColumnList + N'
        FROM @RecordsXml.nodes(''//row'') r(x)
        CROSS APPLY (SELECT x.query(''./@*'')) AS a(x)
        CROSS APPLY x.nodes(''./@*'') AS x(v)
        GROUP BY r.x;';
 
        -- 生成MERGE语句
        DECLARE @MergeSql NVARCHAR(MAX) = N'
        MERGE ' + QUOTENAME(@TableName) + N' AS Target
        USING ' + @TempTable + N' AS Source
        ON (';
 
        -- 生成ON条件(多列主键)
        DECLARE @OnCondition NVARCHAR(MAX) = '';
        SELECT @OnCondition = @OnCondition + 
            'Target.' + QUOTENAME(value) + ' = Source.' + QUOTENAME(value) + ' AND '
        FROM STRING_SPLIT(@PrimaryKeys, ',');
        
        IF LEN(@OnCondition) > 0 
            SET @OnCondition = LEFT(@OnCondition, LEN(@OnCondition) - 4); -- 移除末尾的' AND '
        
        SET @MergeSql = @MergeSql + @OnCondition + N')
        WHEN MATCHED THEN
            UPDATE SET ';
 
        -- 生成UPDATE SET子句(排除主键和标识列)
        DECLARE @UpdateSet NVARCHAR(MAX) = '';
        SELECT @UpdateSet = @UpdateSet + 
            ', ' + QUOTENAME(c.name) + ' = Source.' + QUOTENAME(c.name)
        FROM sys.columns c
        WHERE object_id = OBJECT_ID(@TableName) 
          AND is_identity = 0
          AND NOT EXISTS (
            SELECT 1 
            FROM STRING_SPLIT(@PrimaryKeys, ',') 
            WHERE value = c.name
          )
        ORDER BY c.column_id;
        
        IF LEN(@UpdateSet) > 0 
            SET @UpdateSet = STUFF(@UpdateSet, 1, 2, ''); -- 移除开头的逗号和空格
        
        SET @MergeSql = @MergeSql + @UpdateSet + N'
        WHEN NOT MATCHED THEN
            INSERT (';
 
        -- 生成INSERT列列表(排除标识列)
        DECLARE @InsertColumns NVARCHAR(MAX) = '';
        SELECT @InsertColumns = @InsertColumns + 
            ', ' + QUOTENAME(name)
        FROM sys.columns
        WHERE object_id = OBJECT_ID(@TableName) 
          AND is_identity = 0
        ORDER BY column_id;
        
        IF LEN(@InsertColumns) > 0 
            SET @InsertColumns = STUFF(@InsertColumns, 1, 2, ''); -- 移除开头的逗号和空格
        
        SET @MergeSql = @MergeSql + @InsertColumns + N')
            VALUES (';
 
        -- 生成VALUES列表(排除标识列)
        DECLARE @ValuesList NVARCHAR(MAX) = '';
        SELECT @ValuesList = @ValuesList + 
            ', Source.' + QUOTENAME(name)
        FROM sys.columns
        WHERE object_id = OBJECT_ID(@TableName) 
          AND is_identity = 0
        ORDER BY column_id;
        
        IF LEN(@ValuesList) > 0 
            SET @ValuesList = STUFF(@ValuesList, 1, 2, ''); -- 移除开头的逗号和空格
        
        SET @MergeSql = @MergeSql + @ValuesList + N');';
 
        -- 组合所有SQL
        SET @FullSql = @FullSql + 
            @CreateTempSql + 
            @InsertTempSql + 
            @MergeSql + 
            N' DROP TABLE ' + @TempTable + N';';
 
        SET @TableID += 1;
    END;
 
    -- 执行动态SQL
    IF @FullSql <> N''
    BEGIN
        EXEC sp_executesql @FullSql;
    END
 
    DROP TABLE #TableData;
END;