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.

melbic
fuente
Encontré una respuesta útil aquí: [preparado-declaración-con-declaración-retorno-generado-claves] [1] [1]: stackoverflow.com/questions/4224228/…
Lars Ladegaard
1
¿Posible duplicado de la mejor manera de obtener la identidad de la fila insertada?
Vladimir Vagaytsev

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)

gbn
fuente
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

Brusco
fuente
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

hajikelist
fuente
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.

Ian Boyd
fuente
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.

AngelaG
fuente
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];
Reza Jenabi
fuente
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

MNF
fuente
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
4

Puedes usar scope_identity() para seleccionar la ID de la fila que acaba de insertar en una variable y luego seleccionar las columnas que desee de esa tabla donde id = la identidad que obtuvoscope_identity()

Consulte aquí la información de MSDN http://msdn.microsoft.com/en-us/library/ms190315.aspx

Pez dorado
fuente
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

Ishrar
fuente
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)
Richard Zembron
fuente
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.

Robert Quinn
fuente
-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.

David Guidos
fuente
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