3

我正在尝试创建一个包含merge语句的存储过程。我希望该merge语句能够使用该变量@TargetTable作为目标,但它要求我提供一个表变量。这是我的代码:

CREATE PROCEDURE dbo.mergetable
(
    @TargetTable nvarchar(255)
)
AS

SET NOCOUNT ON
BEGIN
MERGE INTO  @TargetTable AS t
USING dbo.SOURCE_TABLE AS s    
        ON t.name = s.name    
    WHEN MATCHED AND (t.record != s.record) THEN
        --Row exists and data is different
        UPDATE SET t.record= s.record
    WHEN NOT MATCHED BY TARGET THEN 
        --Row exists in source but not in target
        INSERT (name, record)
        VALUES (s.name, s.record)
    WHEN NOT MATCHED BY SOURCE THEN 
        --Row exists in target but not in source
       DELETE
        OUTPUT $action as ACTION, 
    DELETED.name AS Targetname, 
DELETED.record AS Targetrecord, 
INSERTED.name AS Sourcename, 
INSERTED.record AS Sourcerecord, 

SELECT @@ROWCOUNT; 
END

我尝试通过@TargetTable作为数据之一传递来使用表变量,并认为可以@TargetTable从临时表中使用,但我不知道如何编写代码

    DECLARE @temp TABLE(temp varchar(50));
    INSERT @temp VALUES(@TargetTable)

我只看到了说明目标表而不是变量的示例。

有没有办法做到这一点?

提前致谢

4

3 回答 3

2

我最近遇到了同样的问题,并编写了一个存储过程来自动创建 MERGE 语句并触发 sp_executesql 以获得结果。源表上的 CTE 的原因是在我的最终工作存储过程中,我链接到一个日志表以进行增量负载处理。我还删除了 Source Delete 语句,因为我的源使用了软删除。随意添加回来。

以下是针对 AW 的博客文章和 SP 的链接。 使用动态 T-SQL 创建合并语句

/* 
==============================================================================
Author:		    Tommy Swift
Name:           spDynamicMerge
Create date:    5/18/2015
Description:	Stored Procedure to Create MERGE Statements from Source Table
                joining back to target tables on PK columns for CRUD statement
                comparisons
Parameters:     @schemaName - Default = 'dbo' 
				@tableName to be Merged.  
				Schema required if table schema name is other than 'dbo'
Assumptions:    - The parameter table exists on both the Source and Target 
                    and PK's are the same on both DB tables.
                - PK columns will be used to determine record existence.
                - SP resides on the Target database where the filtered list
                    of columns per table occur.  This ensures that only the
                    columns used in the Target are evaluated.
==============================================================================
*/

CREATE PROCEDURE [dbo].[spDynamicMerge]
	@schemaName VARCHAR(100) = 'dbo',
	@tableName VARCHAR(8000)
AS
BEGIN TRANSACTION	
	SET NOCOUNT ON;
	BEGIN TRY
    
    DECLARE  @pkColumnsCompare VARCHAR(8000)            
            ,@nonPKColumnsTarget VARCHAR(8000)
            ,@nonPKColumnsSource VARCHAR(8000)
            ,@nonPKColumnsCompare VARCHAR(8000)
            ,@columnListingSource VARCHAR(8000)
            ,@columnListingTarget VARCHAR(8000)
            ,@sqlCommand NVARCHAR(4000)

    
    --Get list of PK columns for Insert determination
    SELECT @pkColumnsCompare = COALESCE(@pkColumnsCompare + ' AND ', '') + 'Target.' + c.name + ' = ' + 'Source.' + c.name           
	FROM sys.indexes i 
        INNER JOIN sys.index_columns ic 
            ON ic.object_id = i.object_id 
				AND i.index_id = ic.index_id 
        INNER JOIN sys.columns c
            ON ic.object_id = c.object_id
                AND ic.column_id = c.column_id  
        INNER JOIN sys.tables t
            ON t.object_id = c.object_id     
		INNER JOIN sys.schemas s
			on s.schema_id = t.schema_id 
    WHERE i.is_primary_key = 1
		AND s.name + '.' + t.name = @schemaName + '.' + @tableName

    
	--Get List of non-PK columns for Updates
    SELECT @nonPKColumnsTarget = COALESCE(@nonPKColumnsTarget + ', ', '') + 'Target.' + c.name
        ,  @nonPKColumnsSource = COALESCE(@nonPKColumnsSource + ', ', '') + 'Source.' + c.name
        ,  @nonPKColumnsCompare = COALESCE(@nonPKColumnsCompare + ', ', '') + 'Target.' + c.name + ' = ' + 'Source.' + c.name
    FROM 
    (SELECT DISTINCT c.name
    FROM sys.tables t
        INNER JOIN sys.schemas s
			on s.schema_id = t.schema_id
		LEFT JOIN sys.columns c
            ON t.object_id = c.object_id  
        LEFT JOIN sys.indexes i
            ON i.object_id = c.object_id    
        LEFT JOIN sys.index_columns ic 
            ON ic.object_id = i.object_id 
                AND ic.column_id = c.column_id  
    WHERE ic.object_id IS NULL AND
        s.name + '.' + t.name = @schemaName + '.' + @tableName         
    ) c

    
    -- Create comma delimited column listing
    SELECT @columnListingTarget = COALESCE(@columnListingTarget + ', ', '') + c.name
        , @columnListingSource = COALESCE(@columnListingSource + ', ', '') + 'Source.'+ c.name    
    FROM 
    (SELECT DISTINCT c.name
    FROM sys.tables t
		INNER JOIN sys.schemas s
			on s.schema_id = t.schema_id
        INNER JOIN sys.columns c
            ON t.object_id = c.object_id      
    WHERE s.name + '.' + t.name = @schemaName + '.' + @tableName         
    ) c

    --select @pkColumnsCompare, @nonPKColumnsTarget, @nonPKColumnsSource, @nonPKColumnsCompare, @columnListingTarget, @columnListingSource

    SELECT @sqlCommand = 
	'WITH temp AS ' + CHAR(13) + CHAR(10) + 
	'(' + CHAR(13) + CHAR(10) +
	' SELECT * FROM AdventureWorks2012.' + @schemaName + '.' + @tableName + ' WITH(NOLOCK) ' + CHAR(13) + CHAR(10) +		
	') ' + CHAR(13) + CHAR(10) +
	'MERGE DataPatternsStage.' + @schemaName + '.' + @tableName  + ' AS Target ' + CHAR(13) + CHAR(10) +
     'USING temp AS Source ' + CHAR(13) + CHAR(10) +
        'ON ' + @pkColumnsCompare + CHAR(13) + CHAR(10) +
    ' WHEN MATCHED THEN ' + CHAR(13) + CHAR(10) +
       'UPDATE SET ' + @nonPKColumnsCompare + CHAR(13) + CHAR(10) +
    ' WHEN NOT MATCHED BY TARGET ' + CHAR(13) + CHAR(10) +
    'THEN ' + CHAR(13) + CHAR(10) +
       'INSERT (' + @columnListingTarget + ') ' + CHAR(13) + CHAR(10) +
       'VALUES (' + @columnListingSource + '); '

    --select @sqlCommand
    
    EXECUTE sp_executesql @sqlCommand

	END TRY

	BEGIN CATCH
		IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;

		DECLARE @ErrorMessage NVARCHAR(4000);
		DECLARE @ErrorSeverity INT;
		DECLARE @ErrorState INT;

		SELECT 
			@ErrorMessage = ERROR_MESSAGE(),
			@ErrorSeverity = ERROR_SEVERITY(),
			@ErrorState = ERROR_STATE();

		RAISERROR (@ErrorMessage, 
				   @ErrorSeverity,
				   @ErrorState
				   );

	END CATCH;

IF @@TRANCOUNT > 0
    COMMIT TRANSACTION;

GO

于 2015-05-21T16:38:18.570 回答
1

谢谢marc_s的回复。我尝试使用动态 SQL 来解决这个问题,我发现这个网站有很好的教程,用于在存储过程中编写动态 SQL。[1] http://www.codeproject.com/Articles/20815/Building-Dynamic-SQL-In-a-Stored-Procedure

我尝试了这两种方法,但使用 sp_executesql 对我不起作用。使用 sp_executesql 给我一个错误,即未声明表变量 @TargetTbl。

这对我有用:

CREATE PROCEDURE [dbo].[Update_Table]
(
    @TargetTbl          NVARCHAR(100)
)
AS
SET NOCOUNT ON
DECLARE @SQLquery   NVARCHAR(4000)
SET @SQLquery = 'MERGE INTO ' + @TargetTbl + ' AS t '+
    'USING dbo.SOURCE_TABLE AS s ON t.name = s.name '+
    'WHEN MATCHED AND (t.record != s.record) '+
    'THEN UPDATE SET t.record = s.record '+
    'WHEN NOT MATCHED '+
    'THEN INSERT VALUES (s.name, s.record) '+
    'WHEN NOT MATCHED BY SOURCE THEN DELETE '+
    'OUTPUT $action as ACTION,'+ 
    'DELETED.name AS Targetname,'+ 
    'DELETED.record AS Targetrecord,'+ 
    'INSERTED.name AS Sourcename,'+ 
    'INSERTED.record AS Sourcerecord;'

IF(@@ERROR = 0)     
    EXECUTE(@SQLquery)
ELSE
GoTo ErrorHandler

Set NoCount OFF
Return(0)

ErrorHandler:
    Return(@@ERROR)

每个字符串都必须在 NVARCHAR 中才能使代码正常工作

于 2013-06-06T07:33:39.213 回答
0

我对 Tommy Swift 的回答进行了改进,以在不同的数据库中使用相同的脚本

请享用

/* 
==============================================================================
Author:         Tommy Swift & Krisnamourt
Name:           spDynamicMerge
Create date:    5/18/2015
Update date:    15/11/2018
Description:    Stored Procedure to Create MERGE Statements from Source Table
                joining back to target tables on PK columns for CRUD statement
                comparisons
Parameters:     @schemaName - Default = 'dbo' 
                @sourceDb source database
                @targetDb target database
                @sourceTb sourde table
                @targetTb to be Merged
                Schema required if table schema name is other than 'dbo'
Assumptions:    - The parameter table exists on both the Source and Target 
                    and PK's are the same on both DB tables.
                - PK columns will be used to determine record existence.
                - SP resides on the Target database where the filtered list
                    of columns per table occur.  This ensures that only the
                    columns used in the Target are evaluated.
==============================================================================
*/

create PROCEDURE [dbo].[spDynamicMerge]
    @sourceDb VARCHAR(100) ,
    @targetDb VARCHAR(100) ,
    @schemaName VARCHAR(100) = 'dbo',
    @sourceTb VARCHAR(8000),
    @targetTb VARCHAR(8000)
AS
BEGIN TRANSACTION   
    SET NOCOUNT ON;
    BEGIN TRY

    DECLARE  @pkColumnsCompare VARCHAR(8000)            
            ,@nonPKColumnsTarget VARCHAR(8000)
            ,@nonPKColumnsSource VARCHAR(8000)
            ,@nonPKColumnsCompare VARCHAR(8000)
            ,@columnListingSource VARCHAR(8000)
            ,@columnListingTarget VARCHAR(8000)
            ,@sqlCommand NVARCHAR(4000)

    create table #columns (name varchar(400))

    insert into #columns
        exec ('select c.name from '+@targetDb+'.sys.indexes i 
        INNER JOIN '+@targetDb+'.sys.index_columns ic 
            ON ic.object_id = i.object_id 
                AND i.index_id = ic.index_id 
        INNER JOIN '+@targetDb+'.sys.columns c
            ON ic.object_id = c.object_id
                AND ic.column_id = c.column_id  
        INNER JOIN '+@targetDb+'.sys.tables t
            ON t.object_id = c.object_id     
        INNER JOIN '+@targetDb+'.sys.schemas s
            on s.schema_id = t.schema_id 
            WHERE i.is_primary_key = 1
            AND s.name + ''.'' + t.name = '''+@schemaName+'.'+@targetTb+'''')
    select *from #columns
    --Get list of PK columns for Insert determination
    SELECT @pkColumnsCompare = COALESCE(@pkColumnsCompare + ' AND ', '') + 'Target.' + name + ' = ' + 'Source.' + name           
    FROM #columns

    truncate table #columns
    insert into #columns
        exec ('SELECT DISTINCT c.name
    FROM '+@sourceDb+'.sys.tables t
        INNER JOIN '+@sourceDb+'.sys.schemas s
            on s.schema_id = t.schema_id
        LEFT JOIN '+@sourceDb+'.sys.columns c
            ON t.object_id = c.object_id  
        LEFT JOIN '+@sourceDb+'.sys.indexes i
            ON i.object_id = c.object_id    
        LEFT JOIN '+@sourceDb+'.sys.index_columns ic 
            ON ic.object_id = i.object_id 
                AND ic.column_id = c.column_id  
    WHERE ic.object_id IS NULL AND
        s.name + ''.'' + t.name ='''+@schemaName+'.'+@sourceTb+'''')

    --Get List of non-PK columns for Updates
    SELECT @nonPKColumnsTarget = COALESCE(@nonPKColumnsTarget + ', ', '') + 'Target.' + name
        ,  @nonPKColumnsSource = COALESCE(@nonPKColumnsSource + ', ', '') + 'Source.' + name
        ,  @nonPKColumnsCompare = COALESCE(@nonPKColumnsCompare + ', ', '') + 'Target.' + name + ' = ' + 'Source.' + name
    FROM #columns

    truncate table #columns
    insert into #columns
    exec ('SELECT DISTINCT c.name
    FROM '+@sourceDb+'.sys.tables t
        INNER JOIN '+@sourceDb+'.sys.schemas s
            on s.schema_id = t.schema_id
        INNER JOIN  '+@sourceDb+'.sys.columns c
            ON t.object_id = c.object_id      
    WHERE s.name + ''.'' + t.name ='''+@schemaName+'.'+@sourceTb+'''')


    -- Create comma delimited column listing
    SELECT @columnListingTarget = COALESCE(@columnListingTarget + ', ', '') + name
        , @columnListingSource = COALESCE(@columnListingSource + ', ', '') + 'Source.'+ name    
    FROM #columns


    --select @pkColumnsCompare, @nonPKColumnsTarget, @nonPKColumnsSource, @nonPKColumnsCompare, @columnListingTarget, @columnListingSource

    SELECT @sqlCommand = 
    'WITH temp AS ' + CHAR(13) + CHAR(10) + 
    '(' + CHAR(13) + CHAR(10) +
    ' SELECT * FROM '+@sourceDb+'.' + @schemaName + '.' + @sourceTb + ' WITH(NOLOCK) ' + CHAR(13) + CHAR(10) +      
    ') ' + CHAR(13) + CHAR(10) +
    'MERGE '+@targetDb+'.' + @schemaName + '.' + @targetTb  + ' AS Target ' + CHAR(13) + CHAR(10) +
     'USING temp AS Source ' + CHAR(13) + CHAR(10) +
        'ON ' + @pkColumnsCompare + CHAR(13) + CHAR(10) +
    ' WHEN MATCHED THEN ' + CHAR(13) + CHAR(10) +
       'UPDATE SET ' + @nonPKColumnsCompare + CHAR(13) + CHAR(10) +
    ' WHEN NOT MATCHED BY TARGET ' + CHAR(13) + CHAR(10) +
    'THEN ' + CHAR(13) + CHAR(10) +
       'INSERT (' + @columnListingTarget + ') ' + CHAR(13) + CHAR(10) +
       'VALUES (' + @columnListingSource + '); '

    --select @sqlCommand

    EXECUTE sp_executesql @sqlCommand

    drop table #columns

    END TRY

    BEGIN CATCH
        IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;

        DECLARE @ErrorMessage NVARCHAR(4000);
        DECLARE @ErrorSeverity INT;
        DECLARE @ErrorState INT;

        SELECT 
            @ErrorMessage = ERROR_MESSAGE(),
            @ErrorSeverity = ERROR_SEVERITY(),
            @ErrorState = ERROR_STATE();

        RAISERROR (@ErrorMessage, 
                   @ErrorSeverity,
                   @ErrorState
                   );

    END CATCH;

IF @@TRANCOUNT > 0
    COMMIT TRANSACTION;

GO
于 2018-11-15T17:24:57.107 回答