sql-server - example - sql merge two tables
T-SQL Insertar o actualizar (5)
Ambos funcionan bien, pero normalmente uso la opción 2 (pre-mssql 2008) ya que se lee un poco más claramente. Tampoco me preocuparía por el rendimiento aquí ... Si se convierte en un problema, puede usar NOLOCK
en la cláusula exists
. Aunque antes de comenzar a usar NOLOCK en todas partes, asegúrese de haber cubierto todas sus bases (índices y cosas de arquitectura de imagen grande). Si sabe que actualizará cada artículo más de una vez, entonces puede ser conveniente considerar la opción 1.
La opción 3 es no usar actualizaciones destructivas. Requiere más trabajo, pero básicamente inserta una nueva fila cada vez que cambian los datos (nunca actualice o elimine de la tabla) y tiene una vista que selecciona todas las filas más recientes. Es útil si desea que la tabla contenga un historial de todos sus estados anteriores, pero también puede ser una exageración.
Tengo una pregunta sobre el rendimiento de SQL Server.
Supongamos que tengo una tabla de persons
con las siguientes columnas: id
, name
, surname
.
Ahora, quiero insertar una nueva fila en esta tabla. La regla es la siguiente:
Si
id
no está presente en la tabla, inserte la fila.Si la
id
está presente, entonces actualiza.
Tengo dos soluciones aquí:
Primero:
update persons
set id=@p_id, name=@p_name, surname=@p_surname
where id=@p_id
if @@ROWCOUNT = 0
insert into persons(id, name, surname)
values (@p_id, @p_name, @p_surname)
Segundo:
if exists (select id from persons where id = @p_id)
update persons
set id=@p_id, name=@p_name, surname=@p_surname
where id=@p_id
else
insert into persons(id, name, surname)
values (@p_id, @p_name, @p_surname)
¿Cuál es un mejor enfoque? Parece que en la segunda opción, para actualizar una fila, se debe buscar dos veces, mientras que en la primera opción, solo una vez. ¿Hay alguna otra solución al problema? Estoy utilizando MS SQL 2000.
Con el objetivo de ser un poco más seco, evito escribir la lista de valores dos veces.
begin tran
insert into persons (id)
select @p_id from persons
where not exists (select * from persons where id = @p_id)
update persons
set name=@p_name, surname=@p_surname
where id = @p_id
commit
El name
y el surname
de la columna deben ser nulos.
La transacción significa que ningún otro usuario verá el registro "en blanco".
Edición: limpieza
La opción 1 parece buena. Sin embargo, si está en SQL Server 2008, también podría usar MERGE , que puede funcionar bien para tales tareas de UPSERT.
Tenga en cuenta que es posible que desee utilizar una transacción explícita y la opción XACT_ABORT para tales tareas, de modo que la consistencia de la transacción permanezca en el caso de un problema o cambio concurrente.
Puedes usar @@ RowCount para ver si la actualización hizo algo. Algo como:
UPDATE MyTable
SET SomeData = ''Some Data'' WHERE ID = 1
IF @@ROWCOUNT = 0
BEGIN
INSERT MyTable
SELECT 1, ''Some Data''
END
Tiendo a usar la opción 1. Si hay un registro en una tabla, guarda una búsqueda. Si no hay, no pierdes nada. Además, en la segunda opción puede encontrarse con problemas divertidos de bloqueo y interbloqueo relacionados con la incompatibilidad de los bloqueos. Hay más información en mi blog:
http://sqlblogcasts.com/blogs/piotr_rodak/archive/2010/01/04/updlock-holdlock-and-deadlocks.aspx