¡Hola, chicos!

En esta publicación, me gustaría demostrarle cómo usar el comando FUSIONAR, disponible desde SQL Server 2008, para realizar comandos INSERTAR y ACTUALIZAR entre dos tablas en una sola declaración.

Para los ejemplos de esta publicación, usaré una base que crearé usando el siguiente script:

IF (OBJECT_ID('dbo.Venda') IS NOT NULL) DROP TABLE dbo.Venda
CREATE TABLE dbo.Venda (
    Id_Venda INT IDENTITY(1,1) NOT NULL,
    Dt_Venda DATE NOT NULL,
    Id_Produto INT NOT NULL,
    Quantidade INT NOT NULL,
    Valor NUMERIC(9, 2) NOT NULL
)

INSERT INTO dbo.Venda
(
    Dt_Venda,
    Id_Produto,
    Quantidade,
    Valor
)
VALUES ( '2018-09-21', 1, 2, 25.99 ), ( '2018-09-21', 2, 3, 29.99 ), ( '2018-09-21', 1, 1, 15.99 ),
( '2018-09-22', 1, 2, 25.99 ), ( '2018-09-22', 2, 1, 10.00 ), ( '2018-09-22', 9, 1, 35.99 ), ( '2018-09-22', 5, 3, 20.00 ),
( '2018-09-22', 3, 2, 25.87 ), ( '2018-09-22', 2, 1, 11.25 ), ( '2018-09-22', 1, 1, 21.90 ), ( '2018-09-22', 4, 3, 29.99 )


IF (OBJECT_ID('dbo.Dim_Venda') IS NOT NULL) DROP TABLE dbo.Dim_Venda
CREATE TABLE dbo.Dim_Venda (
    Id_Venda INT NOT NULL,
    Dt_Venda DATE NOT NULL,
    Id_Produto INT NOT NULL,
    Quantidade INT NOT NULL,
    Valor NUMERIC(9, 2) NOT NULL
)

INSERT INTO dbo.Dim_Venda
(
    Id_Venda,
    Dt_Venda,
    Id_Produto,
    Quantidade,
    Valor
)
SELECT 
    Id_Venda,
    Dt_Venda,
    Id_Produto,
    Quantidade,
    Valor
FROM
    dbo.Venda
WHERE
    Dt_Venda = '2018-09-21'

Y nuestras tablas de origen y destino se ven así:

Introducción

Una actividad muy común en el día a día de los profesionales que trabajan con datos, especialmente aquellos que trabajan con ETL, la tarea de actualizar una tabla a partir de datos de otra tabla, suele acabar generando 2 operaciones distintas para actualizar los datos:

Método 1: Insertar/Actualizar

  • Actividad 1: Actualizar los registros que existen en ambas tablas
  • Actividad 2: Insertar registros que solo existen en la tabla fuente

Veamos cómo haríamos esto:

-- Atividade 1: Atualizar os registros que existem nas duas tabelas
UPDATE A
SET
    A.Dt_Venda = B.Dt_Venda,
    A.Id_Produto = B.Id_Produto,
    A.Quantidade = B.Quantidade,
    A.Valor = B.Valor 
FROM
    dbo.Dim_Venda A
    JOIN dbo.Venda B ON A.Id_Produto = B.Id_Venda


-- Atividade 2: Inserir os registros que só existem na tabela de origem
INSERT INTO dbo.Dim_Venda
(
    Id_Venda,
    Dt_Venda,
    Id_Produto,
    Quantidade,
    Valor
)
SELECT
    A.Id_Venda, 
    A.Dt_Venda,
    A.Id_Produto,
    A.Quantidade,
    A.Valor
FROM
    dbo.Venda A
    LEFT JOIN dbo.Dim_Venda B ON A.Id_Venda = B.Id_Venda
WHERE
    B.Id_Venda IS NULL


SELECT * FROM dbo.Dim_Venda
SELECT * FROM dbo.Venda

¿Y cómo quedaron los datos de la tabla?

Método 2: Eliminar/Insertar

  • Actividad 1: Elimina los registros que existen en ambas tablas
  • Actividad 2: Insertar registros de la tabla fuente

Que podemos entender cómo funciona a través del siguiente script:

-- Atividade 1: Apaga os registros que existem nas duas tabelas
DELETE A
FROM
    dbo.Dim_Venda A
    JOIN dbo.Venda B ON A.Id_Produto = B.Id_Venda


-- Atividade 2: Inserir os registros que só existem na tabela de origem
INSERT INTO dbo.Dim_Venda
(
    Id_Venda,
    Dt_Venda,
    Id_Produto,
    Quantidade,
    Valor
)
SELECT 
    A.Id_Venda,
    A.Dt_Venda,
    A.Id_Produto,
    A.Quantidade,
    A.Valor
FROM
    dbo.Venda A
    LEFT JOIN dbo.Dim_Venda B ON A.Id_Venda = B.Id_Venda
WHERE
    B.Id_Venda IS NULL


SELECT * FROM dbo.Dim_Venda
SELECT * FROM dbo.Venda

Resultado:

Usando el comando FUSIONAR para INSERTAR + ACTUALIZAR + ELIMINAR

Hasta ahora no he presentado nada nuevo, ¿verdad? Los comandos BORRAR/INSERT y ACTUALIZAR/INSERT son muy comunes en la vida diaria de los profesionales de datos.

Sin embargo, para solucionar este problema, a partir de SQL Server 2008 podemos utilizar el comando MERGE, que nos permite realizar esta misma actividad en tan solo 1 declaración. Su funcionamiento es sencillo: Una o más columnas de las tablas involucradas se consideran claves (identificadores) por lo que si el valor de la clave existe en la tabla de destino, los valores se actualizarán según la tabla de origen. Si este identificador no existe, este registro se insertará en la tabla de destino.

Su sintaxis funciona así:

MERGE 
    dbo.Dim_Venda AS Destino
USING 
    dbo.Venda AS Origem ON (Origem.Id_Venda = Destino.Id_Venda)

-- Registro existe nas 2 tabelas
WHEN MATCHED THEN
    UPDATE SET 
        Destino.Dt_Venda = Origem.Dt_Venda,
        Destino.Id_Produto = Origem.Id_Produto,
        Destino.Quantidade = Origem.Quantidade,
        Destino.Valor = Origem.Valor

-- Registro não existe no destino. Vamos inserir.
WHEN NOT MATCHED THEN
    INSERT
    VALUES(Origem.Id_Venda, Origem.Dt_Venda, Origem.Id_Produto, Origem.Quantidade, Origem.Valor);

Resultado:

Controlar la cláusula NOT MATCHED en el origen y el destino

También podemos controlar cuando un registro no existe en destino o en origen, y tomar decisiones diferentes para estas 2 situaciones:

MERGE 
    dbo.Dim_Venda AS Destino
USING 
    dbo.Venda AS Origem ON (Origem.Id_Venda = Destino.Id_Venda)

-- Registro existe nas 2 tabelas
WHEN MATCHED THEN
    UPDATE SET 
        Destino.Dt_Venda = Origem.Dt_Venda,
        Destino.Id_Produto = Origem.Id_Produto,
        Destino.Quantidade = Origem.Quantidade,
        Destino.Valor = Origem.Valor

-- Registro não existe na origem, apenas no destino. Vamos apagar o dado da origem.
WHEN NOT MATCHED BY SOURCE THEN
    DELETE

-- Registro não existe no destino. Vamos inserir.
WHEN NOT MATCHED BY TARGET THEN
    INSERT
    VALUES(Origem.Id_Venda, Origem.Dt_Venda, Origem.Id_Produto, Origem.Quantidade, Origem.Valor);

Actualización de valores específicos, a través de filtros específicos

Puede ser necesario actualizar sólo la cantidad y el valor, y en los casos en que sean diferentes:

MERGE 
    dbo.Dim_Venda AS Destino
USING 
    dbo.Venda AS Origem ON (Origem.Id_Venda = Destino.Id_Venda)

-- Registro existe nas 2 tabelas
WHEN MATCHED AND (Origem.Quantidade <> Destino.Quantidade OR Origem.Valor <> Destino.Valor) THEN
    UPDATE SET 
        Destino.Quantidade = Origem.Quantidade,
        Destino.Valor = Origem.Valor

-- Registro não existe no destino. Vamos inserir.
WHEN NOT MATCHED THEN
    INSERT
    VALUES(Origem.Id_Venda, Origem.Dt_Venda, Origem.Id_Produto, Origem.Quantidade, Origem.Valor);

Mostrando una salida con todos los cambios realizados por MERGE

¿Qué pasa si queremos recopilar los resultados de MERGE y mostrar las operaciones realizadas en la pantalla? ¡Solo usa SALIDA!

MERGE 
    dbo.Dim_Venda AS Destino
USING 
    dbo.Venda AS Origem ON (Origem.Id_Venda = Destino.Id_Venda)

-- Registro existe nas 2 tabelas
WHEN MATCHED THEN
    UPDATE SET 
        Destino.Dt_Venda = Origem.Dt_Venda,
        Destino.Id_Produto = Origem.Id_Produto,
        Destino.Quantidade = Origem.Quantidade,
        Destino.Valor = Origem.Valor
    
-- Registro não existe na origem, apenas no destino. Vamos apagar o dado da origem.
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
    
-- Registro não existe no destino. Vamos inserir.
WHEN NOT MATCHED BY TARGET THEN
    INSERT
    VALUES(Origem.Id_Venda, Origem.Dt_Venda, Origem.Id_Produto, Origem.Quantidade, Origem.Valor)

OUTPUT $action, 
    Deleted.Id_Venda,
    Deleted.Dt_Venda,
    Deleted.Id_Produto,
    Deleted.Quantidade,
    Deleted.Valor, 
    Inserted.Id_Venda,
    Inserted.Dt_Venda,
    Inserted.Id_Produto,
    Inserted.Quantidade,
    Inserted.Valor;

Resultado:

Almacenamiento de la salida MERGE en una tabla

¿Le gustaría guardar los registros en una tabla para consultar los datos?

CREATE TABLE dbo.Dim_Venda_Log
(
    [Id_Log] BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    [Acao] NVARCHAR(10),
    [Inserted_Id_Venda] INT,
    [Inserted_Dt_Venda] DATE,
    [Inserted_Id_Produto] INT,
    [Inserted_Quantidade] INT,
    [Inserted_Valor] DECIMAL(9, 2),
    [Deleted_Id_Venda] INT,
    [Deleted_Dt_Venda] DATE,
    [Deleted_Id_Produto] INT,
    [Deleted_Quantidade] INT,
    [Deleted_Valor] DECIMAL(9, 2)
) WITH(DATA_COMPRESSION=PAGE)
    

MERGE 
    dbo.Dim_Venda AS Destino
USING 
    dbo.Venda AS Origem ON (Origem.Id_Venda = Destino.Id_Venda)

-- Registro existe nas 2 tabelas
WHEN MATCHED THEN
    UPDATE SET 
        Destino.Dt_Venda = Origem.Dt_Venda,
        Destino.Id_Produto = Origem.Id_Produto,
        Destino.Quantidade = Origem.Quantidade,
        Destino.Valor = Origem.Valor
    
-- Registro não existe na origem, apenas no destino. Vamos apagar o dado da origem.
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
    
-- Registro não existe no destino. Vamos inserir.
WHEN NOT MATCHED BY TARGET THEN
    INSERT
    VALUES(Origem.Id_Venda, Origem.Dt_Venda, Origem.Id_Produto, Origem.Quantidade, Origem.Valor)

OUTPUT 
    $action, 
    Inserted.*,
    Deleted.*
    INTO dbo.Dim_Venda_Log;


SELECT * FROM dbo.Dim_Venda_Log

Usar consultas como datos de origen

MERGE también nos permite unir datos de consultas en lugar de tablas fijas, y usar JOINS, si es necesario.

MERGE 
    dbo.Dim_Venda AS Destino
USING 
    (SELECT Id_Venda, Dt_Venda, Id_Produto, Quantidade, Valor FROM dbo.Venda WHERE Dt_Venda = '2018-09-22') AS Origem ON (Origem.Id_Venda = Destino.Id_Venda)

-- Registro existe nas 2 tabelas
WHEN MATCHED THEN
    UPDATE SET 
        Destino.Dt_Venda = Origem.Dt_Venda,
        Destino.Id_Produto = Origem.Id_Produto,
        Destino.Quantidade = Origem.Quantidade,
        Destino.Valor = Origem.Valor
    
-- Registro não existe na origem, apenas no destino. Vamos apagar o dado da origem.
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
    
-- Registro não existe no destino. Vamos inserir.
WHEN NOT MATCHED BY TARGET THEN
    INSERT
    VALUES(Origem.Id_Venda, Origem.Dt_Venda, Origem.Id_Produto, Origem.Quantidade, Origem.Valor)

OUTPUT 
    $action, 
    Inserted.*,
    Deleted.*;

Resultado: vea que, como solo incorporé los datos del 22/09 a MERGE, los datos del 21/09 que se cargaron manualmente antes de MERGE se eliminaron de la tabla de destino, ya que no existían en la fuente.

Usando CTE con JOIN para generar datos desde la fuente

Merge también le permite usar CTE para generar el conjunto de resultados de la fuente de datos y usar JOINS, si es necesario.

;WITH cteVenda AS (
    SELECT 
        A.Id_Venda,
        A.Dt_Venda,
        A.Id_Produto,
        A.Quantidade,
        A.Valor,
        B.Id_Erro,
        B.Dt_Erro,
        B.Ds_Erro
    FROM
        dbo.Venda A
        LEFT JOIN Consultoria.dbo.Log_Erro B ON A.Id_Produto = B.Id_Erro
    WHERE
        A.Dt_Venda = '2018-09-21'
)
MERGE 
    dbo.Dim_Venda AS Destino
USING 
    cteVenda AS Origem ON (Origem.Id_Venda = Destino.Id_Venda)

-- Registro existe nas 2 tabelas
WHEN MATCHED THEN
    UPDATE SET 
        Destino.Dt_Venda = Origem.Dt_Venda,
        Destino.Id_Produto = Origem.Id_Produto,
        Destino.Quantidade = Origem.Quantidade,
        Destino.Valor = Origem.Valor
    
-- Registro não existe na origem, apenas no destino. Vamos apagar o dado da origem.
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
    
-- Registro não existe no destino. Vamos inserir.
WHEN NOT MATCHED BY TARGET THEN
    INSERT
    VALUES(Origem.Id_Venda, Origem.Dt_Venda, Origem.Id_Produto, Origem.Quantidade, Origem.Valor)

OUTPUT 
    $action, 
    Inserted.*,
    Deleted.*;

Resultado:

Precauciones al utilizar MERGE

Ahora que he demostrado varias formas de usar MERGE en SQL Server, me gustaría advertirle sobre la existencia de algunos errores al usar MERGE en SQL Server 2008, que van desde resultados incorrectos al usar OUTPUT hasta errores graves como el que se muestra a continuación:

Mensaje 0, Nivel 11, Estado 0, Línea 0
Se produjo un error grave en el comando actual. Los resultados, si los hubiera, deben descartarse.
Mensaje 0, Nivel 20, Estado 0, Línea 0
Se produjo un error grave en el comando actual. Los resultados, si los hubiera, deben descartarse.

Si está utilizando la versión 2008 (AÚN), actualice su SQL con la última versión de SP y Cumulative Update disponible y lea atentamente los artículos que pondré a disposición a continuación, ya que tienen varios escenarios que pueden causar dolores de cabeza al usar MERGE en SQL Server 2008 (en las versiones 2012+ estos errores ya no existen):

Usando el procedimiento almacenado stpETL_Upsert

Para facilitar el uso de MERGE, especialmente en tablas con muchas columnas, compartiré con ustedes el Procedimiento Almacenado stpETL_Upsert, creado originalmente por Murilo Mielke y adaptado por mí, que tiene como objetivo facilitar el uso de MERGE en situaciones cotidianas.

Parámetros del SP:

  • @Nm_Fuente: Parámetro de tipo VARCHAR(MAX), contiene el nombre de la tabla fuente de datos (fuente). El nombre de la tabla debe ser específico en formato completo (base de datos.esquema.tabla)
  • @Nm_Target: Parámetro de tipo VARCHAR(MAX), contiene el nombre de la tabla de destino de los datos (target), es decir, donde se insertarán/actualizarán los datos. El nombre de la tabla debe ser específico en formato completo (base de datos.esquema.tabla)
  • @Cd_Join: Parámetro de tipo VARCHAR(MAX), se debe informar la columna o columnas del JOIN para vincular los datos de las tablas origen y destino. Para un uso simplificado, puede especificar solo los nombres de las columnas, si son iguales entre el origen y el destino, separando con una coma “,” si hay más de una columna para realizar el JOIN. Si el nombre de las columnas entre origen y destino son diferentes, se debe utilizar el formato nombre_columna_origen:nombre_columna_destino, utilizando también el separador de coma “,” en caso de varias columnas.
  • @Cd_Chave: Tipo de parámetro VARCHAR(MAX), se debe informar el nombre de la columna secuencial (IDENTITY) de la tabla de destino, para evitar que el SP intente realizar un UPDATE o INSERT usando esa columna. Si no hay una columna IDENTIDAD en el destino, puede ignorar este parámetro, ya que su valor predeterminado ya está vacío.
  • @Fl_Actualización: Parámetro de tipo BIT, con valor predeterminado = 1, esta columna informa al SP si los registros en la tabla de destino (destino) deben actualizarse si existen en el origen y el destino (@Fl_Update = 1) o no.
  • @Fl_Debug: Parámetro de tipo BIT que hace que el SP muestre el comando MERGE generado al final de su ejecución en la ventana de salida

Ejemplo 1: uso sencillo

Utilizando el mismo escenario que los ejemplos de este artículo, usaremos stpETL_Upset para realizar nuestra FUSIÓN:

SELECT * FROM dbo.Dim_Venda

EXEC dbo.stpETL_Upsert
    @Nm_Source = 'dirceuresende.dbo.Venda', -- varchar(max)
    @Nm_Target = 'dirceuresende.dbo.Dim_Venda', -- varchar(max)
    @Cd_Join = 'Id_Venda', -- varchar(max)
    @Cd_Chave = '', -- varchar(max)
    @Fl_Update = 1, -- bit
    @Fl_Debug = 1 -- bit

SELECT * FROM dbo.Dim_Venda

Resultado:

Código de fusión generado:

Ejemplo 2: uso de varias columnas para JOIN

Si necesita utilizar varias columnas para UNIR los datos, simplemente sepárelas con una coma “,” en la llamada al SP:

EXEC dbo.stpETL_Upsert
    @Nm_Source = 'dirceuresende.dbo.Venda', -- varchar(max)
    @Nm_Target = 'dirceuresende.dbo.Dim_Venda', -- varchar(max)
    @Cd_Join = 'Id_Venda, Id_Produto, Dt_Venda', -- varchar(max)
    @Cd_Chave = '', -- varchar(max)
    @Fl_Update = 1, -- bit
    @Fl_Debug = 1 -- bit

Código generado:

Ejemplo 3: uso de JOINS con diferentes columnas

Si las columnas JOIN no tienen exactamente el mismo nombre o desea usar más de una cláusula JOIN, puede usar SP como se muestra a continuación, usando la sintaxis nombre_coluna_table_origin:nombre_coluna_table_destination, y también puede usar el carácter de coma “,” para usar múltiples asignaciones.

En el siguiente ejemplo, la tabla de origen “Venda” tiene un campo llamado Id_Venda, que realizará el JOIN con la columna Cod_Venda en la tabla de destino “Dim_Venda”.

EXEC dbo.stpETL_Upsert
    @Nm_Source = 'dirceuresende.dbo.Venda', -- varchar(max)
    @Nm_Target = 'dirceuresende.dbo.Dim_Venda', -- varchar(max)
    @Cd_Join = 'Id_Venda:Cod_Venda', -- varchar(max)
    @Cd_Chave = '', -- varchar(max)
    @Fl_Update = 1, -- bit
    @Fl_Debug = 1 -- bit

Código generado:

código fuente

¿Te gustó stpETL_Upset? Bastante práctico, ¿verdad? Haré que el código fuente de este Procedimiento almacenado esté disponible a continuación para que pueda comenzar a usarlo en su vida diaria. No olvides crear los objetos fncSplit y fncSplitTexto que están en los requisitos, ¿eh?, de lo contrario será error y me quejaras en los comentarios.. jajaja
Ver el código fuente de stpETL_Upsert

CREATE PROCEDURE [dbo].[stpETL_Upsert] (
    @Nm_Source VARCHAR(MAX), 
    @Nm_Target VARCHAR(MAX), 
    @Cd_Join VARCHAR(MAX),
    @Cd_Chave VARCHAR(MAX) = '',
    @Fl_Update BIT = 1,
    @Fl_Debug BIT = 0
)
AS 
BEGIN

    -- DECLARE @Nm_Source varchar(max) = 'dirceuresende.dbo.Venda', @Nm_Target varchar(max) = 'dirceuresende.dbo.Dim_Venda', @Cd_Join varchar(max) = 'Id_Venda2:Cod_Venda, Dt_Venda:Data', @Cd_Chave VARCHAR(MAX) = '', @Fl_Update bit = 1, @Fl_Debug bit = 0

    SET NOCOUNT ON

    IF (OBJECT_ID(@Nm_Source) IS NULL)
        RAISERROR('A tabela de ORIGEM não existe!', 16, 1) WITH NOWAIT

    IF (OBJECT_ID(@Nm_Target) IS NULL)
        RAISERROR('A tabela de DESTINO não existe!', 16, 1) WITH NOWAIT


    DECLARE 
        @Source_Banco varchar(max) = dbo.fncSplit(@Nm_Source,'.',1), -- https://dirceuresende.com/blog/quebrando-strings-em-sub-strings-utilizando-separador-no-sql-server/
        @Source_Schema varchar(max) = dbo.fncSplit(@Nm_Source,'.',2),
        @Source_Tabela varchar(max) = dbo.fncSplit(@Nm_Source,'.',3),

        @Target_Banco varchar(max) = dbo.fncSplit(@Nm_Target,'.',1),
        @Target_Schema varchar(max) = dbo.fncSplit(@Nm_Target,'.',2),
        @Target_Tabela varchar(max) = dbo.fncSplit(@Nm_Target,'.',3),

        @cmdJoin varchar(max) = '',
        @cmdInsert varchar(max) = '', 
        @cmdValues varchar(max) = '',
        @cmdUpdate VARCHAR(MAX)	= '',

        @matched varchar(MAX) = '',
        @usingFrom varchar(MAX) = @Nm_Source,
        @cmdMerge varchar(MAX) = ''
    


    DECLARE @ColunasSource AS TABLE (
        Nm_Coluna VARCHAR(MAX)
    )

    DECLARE @ColunasTarget AS TABLE (
        Nm_Coluna VARCHAR(MAX)
    )

    DECLARE @Joins as TABLE (
        Id_Join int identity(1,1), 
        Pt_Source varchar(max), 
        Fl_IsNull_Source int,
        Pt_Target varchar(max),
        Fl_IsNull_Target int
    )
    

    INSERT INTO @ColunasSource EXEC('SELECT name Nm_Coluna FROM ' + @Source_Banco + '..syscolumns WHERE id = OBJECT_ID(''' + @Nm_Source + ''')')
    INSERT INTO @ColunasTarget EXEC('SELECT name Nm_Coluna FROM ' + @Target_Banco + '..syscolumns WHERE id = OBJECT_ID(''' + @Nm_Target + ''')')

    
    INSERT INTO @Joins (Pt_Source) 
    SELECT Palavra 
    FROM dbo.fncSplitTexto(@Cd_Join,',') -- https://dirceuresende.com/blog/como-quebrar-uma-string-tabela-substrings-utilizando-delimitador-sql-server
    
    
    UPDATE @Joins
    SET 
        Pt_Source = dbo.fncSplit(Pt_Source,':',1),
        Pt_Target = dbo.fncSplit(Pt_Source,':',2)

    
    UPDATE @Joins
    SET 
        Pt_Target = Pt_Source
    WHERE 
        Pt_Target IS NULL


    UPDATE @Joins
    SET 
        Fl_IsNull_Source = (CASE WHEN RIGHT(Pt_Source,1) = '*' THEN 1 ELSE 0 END),
        Fl_IsNull_Target = (CASE WHEN RIGHT(Pt_Target,1) = '*' THEN 1 ELSE 0 END)
    

    UPDATE @Joins
    SET 
        Pt_Source = REPLACE(Pt_Source,'*',''),
        Pt_Target = REPLACE(Pt_Target,'*','')
    
    
    SELECT 
        @cmdJoin += '
            (SOURCE.[' + Pt_Source +'] = TARGET.[' + Pt_Target + ']' + (CASE WHEN Fl_IsNull_Source = 1 THEN ' OR (SOURCE.[' + Pt_Source + '] IS NULL AND TARGET.[' + Pt_Target + '] IS NULL)' ELSE '' END) + ')'
        + ' AND '
    FROM 
        @Joins

    SET @cmdJoin = SUBSTRING(@cmdJoin, 0, LEN(@cmdJoin) - 3)

    
    SELECT 
        @cmdInsert += '[' + A.Nm_Coluna + '], ', 
        @cmdValues += 'SOURCE.[' + A.Nm_Coluna + '], '
    FROM 
        @ColunasSource A
        JOIN @ColunasTarget B ON A.Nm_Coluna = B.Nm_Coluna
    WHERE 
        A.Nm_Coluna <> @Cd_Chave


    SELECT 
        @cmdInsert += + '[' + Pt_Target + '], ', 
        @cmdValues += 'SOURCE.[' + Pt_Source + '], '
    FROM 
        @Joins
    WHERE 
        Pt_Source <> Pt_Target
        AND Pt_Source <> @Cd_Chave


    SELECT
        @cmdInsert = SUBSTRING(@cmdInsert, 0, LEN(@cmdInsert)),
        @cmdValues = SUBSTRING(@cmdValues, 0, LEN(@cmdValues))
        
    
    IF (@Fl_Update = 1) 
    BEGIN
    
        SELECT 
            @cmdUpdate += '[' + A.Nm_Coluna + '] = SOURCE.[' + B.Nm_Coluna + '], ' + CHAR(10) + CHAR(9) + CHAR(9)
        FROM
            @ColunasSource A
            JOIN @ColunasTarget B ON A.Nm_Coluna = B.Nm_Coluna
        WHERE 
            A.Nm_Coluna NOT IN (SELECT Pt_Source FROM @Joins) 
            AND B.Nm_Coluna NOT IN (SELECT Pt_Target FROM @Joins)
            AND A.Nm_Coluna <> @Cd_Chave
            
        SET @cmdUpdate = SUBSTRING(@cmdUpdate, 0, LEN(@cmdUpdate) - 4)

    END


    IF (@cmdUpdate <> '') SET @matched = 'WHEN MATCHED THEN UPDATE SET ' + @cmdUpdate
    IF (@Source_Banco = 'TempDB') SET @usingFrom = @Source_Tabela

    
    SET @cmdMerge = '
        MERGE ' + @Nm_Target + ' as TARGET
        USING (SELECT * FROM ' + @usingFrom + ') AS SOURCE ON (
        ' + @cmdJoin + '
        
        )
    ' + @matched + '
    WHEN NOT MATCHED THEN
        INSERT (' + @cmdInsert + ')
        VALUES (' + @cmdValues + ');
    '	


    IF (@Fl_Debug = 1)
        PRINT @cmdMerge


    BEGIN TRY
        EXEC(@cmdMerge)
    END TRY
    BEGIN CATCH
        DECLARE @Erro varchar(max) = ERROR_MESSAGE()
        PRINT @cmdMerge
        RAISERROR(@Erro,16,1)
    END CATCH
    

END
GO

Requisitos:

Referencias

Bueno chicos!
Espero que hayas disfrutado de esta publicación y nos vemos la próxima.
¡Gran abrazo!