miércoles, abril 24, 2024

Usando MERGE para INSERT y UPDATE

Que es lo que se hace cuando en una aplicación requerimos agregar un registro nuevo, pero si este ya existe modificarlo con los nuevos datos. Si estas utilizando SQL Server 2005 o inferior debes escribir 2 o 3 querys, uno para verificar si el registro existe, si existe entonces actualizas y si no existe lo insertas.  En SQL Server 2008 se han hecho cambios para mejorar esto y ahora en una sola sentencia puedes hacer esto, insert o update usando la instrucción MERGE.

La instrucción MERGE básicamente une datos de un resultado de origen establecido en una tabla destino. Se envían los datos al MERGE, el los compara (por la llave primaria), si existe los actualiza y si no existe los ingresa, también podría ser que si no cumple con los requisitos los pueda borrar,  insert, update y delete en una sola instrucción.

MERGE también permite comparar dos tablas, una fuente y otra destino en ingresar o modificar los datos de la tabla en base a los datos de otra tabla, eso lo veremos en otro ejemplo que publicaremos en este sitio.

En este primer ejemplo sobre MERGE vamos a hacer el manejo típico de datos en una tabla, insert y update.

Tabla que vamos a utilizar de ejemplo.

CREATE TABLE [dbo].[tablaPrueba](
    [id] [int] NOT NULL,
    [nombre] [varchar](50) NULL,
    [fecha_ingreso] [datetime] NULL,
    [fecha_actualizacion] [datetime] NULL,
 CONSTRAINT [PK_tablaPrueba] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)

Primero vamos a mostrar como se hace o hacia normalmente en SQL, principalmente si se utiliza SQL 2005 o inferior.

-- Ejemplo 1 obsoleto pero funcional en SQL 2008 o superior
CREATE PROCEDURE InsertaTablaPrueba(
@id int,
@nombre varchar(50) 
)
AS
BEGIN
    SET NOCOUNT ON
 
    --Se actualizan los registros en la tabla
    UPDATE tablaPrueba
       SET nombre = @nombre,
           fecha_actualizacion = GETDATE()
     WHERE id = @id
     --Si no se actualizó ningún registro se inserta en la tabla
     IF (@@ROWCOUNT = 0 )
     BEGIN
        INSERT INTO tablaPrueba (id, nombre, fecha_ingreso, fecha_actualizacion)
        VALUES(@id, @nombre, GETDATE(), GETDATE())
     END
END

En ese ejemplo podemos ver que se solicitar modificar un registro y si este no afectó ningún registro (@@ROWCOUNT=0) lo actualiza. En el peor de los casos se ejecutan dos querys, se toca dos veces la tabla.

-- Ejemplo 2, obsoleto pero funcional en SQL 2008 o superior
CREATE PROCEDURE InsertaTablaPrueba2(
@id int,
@nombre varchar(50) 
)
AS
BEGIN
    SET NOCOUNT ON
 
    IF EXISTS(SELECT 1 FROM tablaPrueba
               WHERE id = @id)
    BEGIN
        --Se actualizan los registros en la tabla
        UPDATE tablaPrueba
           SET nombre = @nombre,
               fecha_actualizacion = GETDATE()
         WHERE id = @id
     END
     ELSE
     BEGIN
        --Si no existe el registro se inserta en la tabla
        INSERT INTO tablaPrueba (id, nombre, fecha_ingreso, fecha_actualizacion)
        VALUES(@id, @nombre, GETDATE(), GETDATE())
     END
 
END

En este ejemplo se consulta si el registro existe, y luego se actualiza o se inserta, siempre se toca 2 veces la tabla.

Ahora en el siguiente ejemplo vamos a utilizar MERGE, vamos a ver como en una sola sentencia se inserta o actualiza un registro, mucho más eficiente.

-- Ejemplo 3, usando MERGE, mucho más eficiente
CREATE PROCEDURE InsertaTablaPruebaMerge(
@id int,
@nombre varchar(50) 
)
AS
BEGIN
    SET NOCOUNT ON
 
    MERGE tablaPrueba as TARGET
    USING(SELECT @id, @nombre) AS SOURCE(id, nombre)
    ON (TARGET.id = SOURCE.id)
    WHEN MATCHED THEN
        UPDATE SET nombre = SOURCE.nombre, 
                   fecha_actualizacion = GETDATE()
    WHEN NOT MATCHED THEN
        INSERT (id, nombre, fecha_ingreso, fecha_actualizacion)
        VALUES (SOURCE.id, SOURCE.nombre, GETDATE(), GETDATE());
END

Podemos ver que MERGE recibe una tabla destino, que es a la que se le van a ingresar o modificar los datos (TARGET), luego recibe la tabla fuente o los datos fuente, en este caso son datos fuente (SOURCE). Luego de estos, cuando los datos concuerdan (WHEN MATCHED) realiza el UPDATE en la tabla, y cuando no existen los registros (WHEN NOT MATCHED) realiza el INSERT

Datos para probar los 3 procedimientos

EXEC InsertaTablaPrueba 1, 'Valor núm. 1'
EXEC InsertaTablaPrueba 2, 'Valor núm. 2'
EXEC InsertaTablaPrueba 3, 'Valor núm. 3'
EXEC InsertaTablaPrueba 4, 'Valor núm. 4'
 
EXEC InsertaTablaPrueba2 4, 'Valor núm. 4.1'
 
EXEC InsertaTablaPruebaMerge 5, 'Valor núm. 5'
EXEC InsertaTablaPruebaMerge 3, 'Valor núm. 3.1'
 
EXEC InsertaTablaPruebaMerge 6, 'Valor núm. 6'
EXEC InsertaTablaPrueba 2, 'Valor núm. 2.1'
 
SELECT * FROM tablaPrueba

Roy Rojas
Roy Rojashttp://www.dotnetcr.com
Con más de 20 años de experiencia en programación, experto en lenguajes .NET, VB, C#, ASP.NET, Xamarin, XCode, DBA en SQL Server. Creador de dotnetcr.com, sitio web para programadores en español. royrojas.com | dotnetcr.com | GitHub
Roy Rojas
Roy Rojashttp://www.dotnetcr.com
Con más de 20 años de experiencia en programación, experto en lenguajes .NET, VB, C#, ASP.NET, Xamarin, XCode, DBA en SQL Server. Creador de dotnetcr.com, sitio web para programadores en español. royrojas.com | dotnetcr.com | GitHub