T-SQL Insertar o actualizar


Tengo una pregunta sobre el rendimiento de SQL Server.

Supongamos que tengo una tabla persons con las siguientes columnas: id, name, surname.

Ahora, quiero insertar una nueva fila en esta tabla. La regla es la siguiente:

  1. Si id no está presente en la tabla, inserte la fila.

  2. Si id está presente, entonces actualice.

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)

¿Qué es un mejor enfoque? Parece que en la segunda opción, para actualizar una fila, debe buscarse dos veces, mientras que en la primera opción, solo una vez. ¿Hay alguna otra solución al problema? Estoy usando MS SQL 2000.

Author: mustaccio, 2010-02-16

5 answers

Ambos funcionan bien, pero normalmente uso la opción 2 (pre-mssql 2008) ya que se lee un poco más claramente. Yo tampoco me estresaría por la actuación aquí...Si se convierte en un problema, puede usar NOLOCK en la cláusula exists. Sin embargo, 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 podría pagar considerar la opción 1.

La opción 3 es no utilizar actualizaciones destructivas. Se necesita más trabajo, pero básicamente se inserta una nueva fila cada vez que los datos cambian (nunca actualizar o eliminar 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 exagerado.

 8
Author: dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-17 00:52:50

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 usar una transacción explícita y la opción XACT_ABORT para dichas tareas, de modo que la consistencia de la transacción permanezca en caso de un problema o cambio simultáneo.

 11
Author: Lucero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-16 20:25:00

Tiendo a utilizar la opción 1. Si hay un registro en una tabla, guarda una búsqueda. Si no lo hay, no pierdes nada. Además, en la segunda opción puede encontrarse con problemas de bloqueo y bloqueo divertidos relacionados con la incompatibilidad de bloqueos. Hay más información en mi blog:

Http://sqlblogcasts.com/blogs/piotr_rodak/archive/2010/01/04/updlock-holdlock-and-deadlocks.aspx

 4
Author: Piotr Rodak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-16 23:58:14

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

Las columnas name y surname tienen que ser nullables.

La transacción significa que ningún otro usuario verá nunca el registro "en blanco".

Editar: limpieza

 3
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-27 01:54:58

Solo 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
 0
Author: KISS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-09 10:24:15