SQL Server - Valor de retorno después de INSERTAR


318

Estoy tratando de recuperar el valor-clave después de una instrucción INSERT. Ejemplo: Tengo una tabla con el nombre y la identificación de los atributos. id es un valor generado.

    INSERT INTO table (name) VALUES('bob');

Ahora quiero recuperar la identificación en el mismo paso. ¿Cómo se hace esto?

Estamos usando Microsoft SQL Server 2008.


Encontré una respuesta útil aquí: [preparado-declaración-con-declaración-retorno-generado-claves] [1] [1]: stackoverflow.com/questions/4224228/…
Lars Ladegaard

Respuestas:


477

No es necesario un SELECT por separado ...

INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');

Esto también funciona para columnas sin IDENTIDAD (como GUID)


29
¿Podrías elaborar un poco? ¿A dónde va la salida en este ejemplo? La documentación solo muestra ejemplos de tablas (usando output ... into). Idealmente, me gustaría poder pasarlo a una variable
JonnyRaa

2
@ JonnyLeeds: no puede hacerlo con una variable (a menos que sea una variable de tabla). La SALIDA va al cliente o a una mesa
gbn

77
Desafortunadamente, no puede confiar en esto ya que agregar un disparador a la tabla romperá sus declaraciones. re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…
hajikelist

1
@hajikelist: este es un caso bastante extremo, SET NCOOUNT ON en el gatillo generalmente ayuda. Ver stackoverflow.com/questions/1483732/set-nocount-on-usage
gbn

55
Nunca use @@ IDENTITY. SCOPE_IDENTITY, sí, pero nunca @@ IDENTITY. No es confiable
gbn

188

Use SCOPE_IDENTITY()para obtener el nuevo valor de ID

INSERT INTO table (name) VALUES('bob');

SELECT SCOPE_IDENTITY()

http://msdn.microsoft.com/en-us/library/ms190315.aspx


77
asumiendo que ides identidad
Ilia G

12
@ liho1eye: el OP se refirió al nombre de la columna de identidad como id, entonces sí.
Curt

3
En un sistema más grande, ¿qué pasa si se ejecutan muchos sql al mismo tiempo? ¿Devolverá la última identificación insertada a cada solicitud?
Shiv

2
@Shiv "SCOPE_IDENTITY devuelve valores insertados solo dentro del alcance actual"
goodies4uall

45
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

Es la apuesta más segura ya que existe un problema conocido con el conflicto de la cláusula OUTPUT en tablas con activadores. Hace que esto sea poco confiable, ya que incluso si su tabla actualmente no tiene ningún desencadenante: alguien que agregue uno en la línea interrumpirá su aplicación. Tipo de comportamiento de bomba de tiempo.

Consulte el artículo de msdn para obtener una explicación más profunda:

http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx


Solo si no agrega SET NOCOUNT ON en los activadores. Ver también docs.microsoft.com/en-us/sql/database-engine/configure-windows/…
gbn

esta no es una opción para nuestros entornos heredados @gbn
hajikelist

@hajikelist Todos tenemos un legado, pero el riesgo de que los desencadenantes estropeen OUTPUT es bajo, todo lo que se necesita es activar la cuenta. Si alguien agrega un desencadenador, entonces debería saber cómo codificarlo (lo que implica que tiene el control principalmente), o si necesita capacitar a sus desarrolladores. En algún momento, se verá obligado a migrar cuando esa versión de SQL ya no esté disponible. compatible, etc., por lo que los disparadores no causarán un conjunto de resultados. Sea lo que sea, no es la mejor respuesta porque si tiene desencadenadores INSTEAD OF OF SCOPE_IDENTITY podría no funcionar ( stackoverflow.com/questions/908257/… )
gbn

@gbn: me gusta evitar cosas tontas como esta. No voy a decirle a todos mis desarrolladores: "No olviden agregar el 'no rompa la declaración de mi aplicación' en cada disparador". - puedes quedártelo. El escenario "en cambio" es mucho más un caso límite de la OMI.
hajikelist

Una respuesta más segura puede ser simplemente hacer que la aplicación ejecute otra consulta una vez que regrese de esta. Siempre y cuando se haga en el back-end, la penalización de rendimiento debería valer la simplicidad de administrar el desarrollo en grupos de personas, y está más cerca de acuerdo con los estándares que alguna característica loca con casos extremos. Prefiero que los casos extremos estén en mi código y los evite en las plataformas. solo mi opinión no se asuste :)
Dan Chase

33

Entity Framework realiza algo similar a la respuesta de gbn:

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO @generated_keys
VALUES('bob');

SELECT t.[CustomerID]
FROM @generated_keys AS g 
   JOIN dbo.Customers AS t 
   ON g.Id = t.CustomerID
WHERE @@ROWCOUNT > 0

Los resultados de salida se almacenan en una variable de tabla temporal y luego se vuelven a seleccionar al cliente. Tienes que estar al tanto del problema:

las inserciones pueden generar más de una fila, por lo que la variable puede contener más de una fila, por lo que puede devolverse más de una ID

No tengo idea de por qué EF uniría internamente la tabla efímera a la tabla real (en qué circunstancias los dos no coincidirían).

Pero eso es lo que hace EF.

SQL Server 2008 o más reciente solamente. Si es 2005, entonces no tienes suerte.


2
La razón por la que EF lo hace es para asegurarse de que también puede "ver" todos los demás cambios en el Customerregistro insertado , ya que puede haber otra lógica del lado DB que lo afecte, por ejemplo, DEFAULTen algunas columnas, disparadores en la tabla, etc. EF actualiza el entidad (objeto) que utilizó para la inserción, por lo que el lado del cliente obtiene el objeto del cliente con la ID y todo lo demás que representa el estado actual de la fila.
Hilarion

Otra razón para no usar EF.
cskwg

11

@@IDENTITY Es una función del sistema que devuelve el último valor de identidad insertado.


44
Debo advertir que nunca use @@ IDENTITY: no es preciso (demasiado amplio) y mucho menos seguro para subprocesos; consulte la respuesta de @ Curt sobre SCOPE_IDENTITY ().
zanlok

10

Hay muchas formas de salir después de insertar

Cuando inserta datos en una tabla, puede usar la cláusula OUTPUT para devolver una copia de los datos que se han insertado en la tabla. La cláusula OUTPUT toma dos formas básicas: OUTPUT y OUTPUT INTO. Use el formulario de SALIDA si desea devolver los datos a la aplicación que realiza la llamada. Use el formulario OUTPUT INTO si desea devolver los datos a una tabla o una variable de tabla.

DECLARE @MyTableVar TABLE (id INT,NAME NVARCHAR(50));

INSERT INTO tableName
(
  NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO @MyTableVar
VALUES
(
   'test',...
)

IDENT_CURRENT : devuelve la última identidad creada para una tabla o vista en particular en cualquier sesión.

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY : devuelve la última identidad de una misma sesión y el mismo alcance. Un ámbito es un procedimiento almacenado / disparador, etc.

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@ IDENTITY : Devuelve la última identidad de la misma sesión.

SELECT @@IDENTITY AS [@@IDENTITY];

1
@RezaJenabi Jun, out puesto está muy bien trabajado, es mejor que encontrar muchas identificaciones en la tabla. Solía out putpara bulk inserty el inserto por select statement. gracias su sugerencia
Amirhossein

5

La mejor y más segura solución es usar SCOPE_IDENTITY() .

Solo tiene que obtener la identidad del alcance después de cada inserción y guardarla en una variable porque puede llamar a dos inserciones en el mismo alcance.

ident_currenty @@identitypuede ser que funcionen pero no tienen un alcance seguro. Puedes tener problemas en una aplicación grande

  declare @duplicataId int
  select @duplicataId =   (SELECT SCOPE_IDENTITY())

Más detalles aquí. Documentos de Microsoft


1
Puede simplificar esto aselect @duplicataId = SCOPE_IDENTITY()
pcnate

1
OUTPUTcláusula es una solución mejor y más pura :)
Dale K

OUTPUT INTO es muy lento.
cskwg


1

Hay varias formas de obtener la última ID insertada después del comando de inserción.

  1. @@IDENTITY : Devuelve el último valor de identidad generado en una conexión en la sesión actual, independientemente de la tabla y el alcance de la declaración que produjo el valor
  2. SCOPE_IDENTITY(): Devuelve el último valor de identidad generado por la instrucción de inserción en el ámbito actual en la conexión actual, independientemente de la tabla.
  3. IDENT_CURRENT(‘TABLENAME’): Devuelve el último valor de identidad generado en la tabla especificada, independientemente de cualquier conexión, sesión o alcance. IDENT_CURRENT no está limitado por el alcance y la sesión; se limita a una tabla especificada.

Ahora parece más difícil decidir cuál será la coincidencia exacta para mi requerimiento.

Principalmente prefiero SCOPE_IDENTITY ().

Si usa select SCOPE_IDENTITY () junto con TableName en la instrucción de inserción, obtendrá el resultado exacto según sus expectativas.

Fuente: CodoBee


0

Así es como uso OUTPUT INSERTED, cuando inserto en una tabla que usa ID como columna de identidad en SQL Server:

'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)

0

Puede agregar una declaración de selección a su declaración de inserción. Entero myInt = Insertar en los valores de tabla1 (FName) ('Fred'); Seleccione Scope_Identity (); Esto devolverá un valor de la identidad cuando se ejecute el escalador.



-4

* El orden de los parámetros en la cadena de conexión a veces es importante. * La ubicación del parámetro Proveedor puede romper el cursor del conjunto de registros después de agregar una fila. Vimos este comportamiento con el proveedor SQLOLEDB.

Después de agregar una fila, los campos de la fila no están disponibles, A MENOS que el proveedor se especifique como el primer parámetro en la cadena de conexión. Cuando el proveedor está en cualquier parte de la cadena de conexión, excepto como primer parámetro, los campos de fila recién insertados no están disponibles. Cuando movimos el Proveedor al primer parámetro, los campos de fila aparecieron mágicamente.


1
¿Podría decirnos cómo este comentario responde / es relevante para la pregunta que se hizo? No creo que merezca mayúsculas / negrita. Si su respuesta se considera útil, los usuarios la votarán.
n__o

Probablemente muchos usuarios vinieron a esta página porque no tenían campos válidos para identificar la fila que se acaba de agregar. Este comportamiento que encontramos (que simplemente cambiar el orden de los parámetros en la cadena de conexión permite acceder de inmediato a la fila recién agregada) es tan extraño que pensé que merecía mencionarlo en mayúsculas, especialmente porque es muy probable que solucione la razón por la que la gente quiere el nuevo ID de fila y otros campos de esa fila. Simplemente poniendo al proveedor como el primer parámetro, el problema desaparece.
David Guidos

Necesita editar y mejorar su respuesta. Actualmente es ruidoso y no parece una respuesta decente o incluso un intento
James

¿Qué quieres decir exactamente con "ruidoso"? Necesita explicar su queja. Es tan simple como puede ser. Si cambia el orden de los parámetros en su cadena de conexión, puede afectar si los datos de fila están disponibles después de una inserción.
David Guidos
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.