¿La mejor manera de obtener la identidad de la fila insertada?

¿Cuál es la mejor manera de obtener IDENTITY de la fila insertada?

Se acerca de @@IDENTITY y IDENT_CURRENT y SCOPE_IDENTITY pero no entiendo los pros y los contras de cada uno.

¿Alguien puede explicar las diferencias y cuándo debería usar cada una?

preguntado el 03 de septiembre de 08 a las 19:09

INSERT INTO Table1(fields...) OUTPUT INSERTED.id VALUES (...), o un método más antiguo: INSERT INTO Table1(fields...) VALUES (...); SELECT SCOPE_IDENTITY(); puede obtenerlo en c # usando ExecuteScalar (). -

¿Cómo es eso mejor que las otras respuestas? (también, ¿por qué no publicas esto como una respuesta en lugar de un comentario?). Escriba una respuesta completa (y explique por qué esta es una mejor opción que las publicadas; si es específica de la versión, dígalo). -

es como un breve resumen. ; D La respuesta aceptada no menciona la sintaxis de la cláusula OUTPUT y carece de una muestra. Además, las muestras en otras publicaciones no están tan limpias ... -

@saeedserpooshan - luego edítalo en. Puedes hacerlo, ¿sabes? ¿Ves cuando se publicó esa respuesta? Eso es anterior al OUTPUT cláusula en SQL Server. -

14 Respuestas

  • @@IDENTITY devuelve el último valor de identidad generado para cualquier tabla en la sesión actual, en todos los ámbitos. Debes tener cuidado aquí, ya que está en todos los ámbitos. Puede obtener un valor de un disparador, en lugar de su declaración actual.

  • SCOPE_IDENTITY() devuelve el último valor de identidad generado para cualquier tabla en la sesión actual y el ámbito actual. Generalmente lo que quieres usar.

  • IDENT_CURRENT('tableName') devuelve el último valor de identidad generado para una tabla específica en cualquier sesión y en cualquier ámbito. Esto le permite especificar de qué tabla desea el valor, en caso de que los dos anteriores no sean exactamente lo que necesita (muy raro). Tambien como @tipo starbuck mencionó, "Puede usar esto si desea obtener el valor de IDENTIDAD actual para una tabla en la que no ha insertado un registro".

  • La OUTPUT cláusula de los INSERT declaración le permitirá acceder a todas las filas que se insertaron a través de esa declaración. Dado que está en el ámbito de la declaración específica, es más sencillo que las otras funciones anteriores. Sin embargo, es un poco más detallado (deberá insertar en una tabla variable / tabla temporal y luego consultar eso) y da resultados incluso en un escenario de error donde la declaración se revierte. Dicho esto, si su consulta utiliza un plan de ejecución paralelo, este es el único método garantizado para obtener la identidad (salvo desactivar el paralelismo). Sin embargo, se ejecuta antes disparadores y no se puede utilizar para devolver valores generados por disparadores.

contestado el 23 de mayo de 17 a las 13:05

error conocido con SCOPE_IDENTITY () devolviendo los valores incorrectos: blog.sqlauthority.com/2009/03/24/… la solución es no ejecutar INSERT en un plan paralelo multiprocesador o usar la cláusula OUTPUT - KM.

Casi todas las veces que he querido 'identidad', he querido saber la (s) clave (s) de los registros que acabo de insertar. Si esa es su situación, desea utilizar la cláusula OUTPUT. Si desea algo más, aplique el esfuerzo de leer y comprender la respuesta de bdukes. - jerry

Contamos con output no es necesario crear una tabla temporal para almacenar y consultar los resultados. Solo deja el into parte de la cláusula de salida y los enviará a un conjunto de resultados. - spb

Para evitar que otros se asusten, el error mencionado anteriormente se corrigió en la Actualización acumulativa 5 para SQL Server 2008 R2 Service Pack 1. - GaTechThomas

@niico, creo que la recomendación es la misma que ha sido, que es que OUTPUT es el "mejor" siempre que no utilice activadores y maneje errores, pero SCOPE_IDENTITY es el más simple y rara vez tiene problemas - duques

Creo que el método más seguro y preciso para recuperar la identificación insertada sería usar la cláusula de salida.

por ejemplo (tomado de lo siguiente MSDN artículo)

USE AdventureWorks2008R2;
GO
DECLARE @MyTableVar table( NewScrapReasonID smallint,
                           Name varchar(50),
                           ModifiedDate datetime);
INSERT Production.ScrapReason
    OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
        INTO @MyTableVar
VALUES (N'Operator error', GETDATE());

--Display the result set of the table variable.
SELECT NewScrapReasonID, Name, ModifiedDate FROM @MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate 
FROM Production.ScrapReason;
GO

Respondido el 10 de Septiembre de 20 a las 22:09

Sí, este es el método correcto en el futuro, solo use uno de los otros si no está en SQL Server 2008 (omitimos 2005, por lo que no estoy seguro de si OUTPUT estaba disponible en ese momento) - HLGEM

@HLGEM Hay un Página de MSDN para OUTPUT en SQL Server 2005, por lo que parece que solo SQL Server 2000 y versiones anteriores no lo tienen - duques

Para obtener un ejemplo realmente conciso para obtener la ID insertada, eche un vistazo a: stackoverflow.com/a/10999467/2003325 - Luke

Su uso de INTO con OUTPUT es una buena idea. Ver: blogs.msdn.microsoft.com/sqlprogrammability/2008/07/11/… (De un comentario aquí: stackoverflow.com/questions/7917695/…) - shlgug

Acabo de enterarme de esta función OUTPUT INSERT, que parece la respuesta real, funciona muy bien en sqlserver, pero no funciona con la clase SqlClient, arroja System.Data.SqlClient.SqlException: 'No se puede encontrar la columna "INSERTED" o la función definida por el usuario o agregado "INSERTED.Id", o el nombre es ambiguo. ', estoy planteando una pregunta en otro hilo, así que si alguien conoce la solución, será apreciado: stackoverflow.com/questions/49786640/… - yogur

Estoy diciendo lo mismo que los otros muchachos, así que todos tienen razón, solo trato de dejarlo más claro.

@@IDENTITY devuelve la identificación de la última cosa que fue insertada por la conexión de su cliente a la base de datos.
La mayoría de las veces esto funciona bien, pero a veces un disparador irá e insertará una nueva fila que no conoce, y obtendrá la ID de esta nueva fila, en lugar de la que desea.

SCOPE_IDENTITY() resuelve este problema. Devuelve el id de la última cosa que tu insertaste en el código SQL enviaste a la base de datos. Si los disparadores van y crean filas adicionales, no harán que se devuelva el valor incorrecto. Hurra

IDENT_CURRENT devuelve el último ID insertado por alguien. Si alguna otra aplicación inserta otra fila en un momento desafortunado, obtendrá la ID de esa fila en lugar de la suya.

Si quieres ir a lo seguro, usa siempre SCOPE_IDENTITY(). Si te quedas con @@IDENTITY y alguien decide agregar un disparador más adelante, todo su código se romperá.

Respondido el 03 de Septiembre de 08 a las 22:09

¿Qué pasa si digamos que 2 o 5 usuarios crearán un registro al mismo tiempo, SCOPE_IDENTITY () nos dio ese registro correcto para cada usuario, o? - Slava Ca

La mejor forma (léase: la más segura) de obtener la identidad de una fila recién insertada es mediante el output cláusula:

create table TableWithIdentity
           ( IdentityColumnName int identity(1, 1) not null primary key,
             ... )

-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare @IdentityOutput table ( ID int )

insert TableWithIdentity
     ( ... )
output inserted.IdentityColumnName into @IdentityOutput
values
     ( ... )

select @IdentityValue = (select ID from @IdentityOutput)

respondido 06 nov., 17:12

La agrupación en clústeres de servidores SQL es una característica de alta disponibilidad y no influye en el paralelismo. Es muy poco común para inserciones de una sola fila (el caso más común para scope_identity()) para obtener planes paralelos de todos modos. Y este error se corrigió más de un año antes de esta respuesta. - Martin Smith

¿Qué quieres decir con paralelismo? - user1451111

@MartinSmith El cliente no estaba dispuesto a permitir tiempo de inactividad en su clúster de servidores para instalar la CU solucionando este problema (no es broma), por lo que la única solución era que reescribiéramos todo el SQL para usar output en lugar de scope_identity(). He eliminado el FUD sobre agrupamiento en la respuesta. - Ian Kemp

Gracias, este es el único ejemplo que he podido encontrar que muestra cómo usar el valor de la salida en una variable en lugar de simplemente generarla. - sean ray

Añada

SELECT CAST(scope_identity() AS int);

al final de su declaración sql de inserción, luego

NewId = command.ExecuteScalar()

lo recuperará.

Respondido 26 Jul 16, 15:07

A donde estas consiguiendo NewId ¿de? ¿Y cuál es su tipo declarado? Para almacenar command.ExecuteScalar() en él asumo que es un Object? - tylerh

Cuando usa Entity Framework, usa internamente el OUTPUT técnica para devolver el valor de ID recién insertado

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');

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

Los resultados de salida se almacenan en una variable de tabla temporal, se vuelven a unir a la tabla y devuelven el valor de la fila fuera de la tabla.

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

Pero eso es lo que hace EF.

Esta tecnica (OUTPUT) solo está disponible en SQL Server 2008 o versiones posteriores.

Editar - El motivo de la unión

La razón por la que Entity Framework se une a la tabla original, en lugar de simplemente usar el OUTPUT valores se debe a que EF también usa esta técnica para obtener el rowversion de una fila recién insertada.

Puede utilizar la simultaneidad optimista en los modelos de su marco de entidad mediante usando el Timestamp atributo: 🕗

public class TurboEncabulator
{
   public String StatorSlots)

   [Timestamp]
   public byte[] RowVersion { get; set; }
}

Cuando haga esto, Entity Framework necesitará el rowversion de la fila recién insertada:

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');

SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM @generated_keys AS g 
   JOIN dbo.TurboEncabulators AS t 
   ON g.Id = t.TurboEncabulatorID 
WHERE @@ROWCOUNT > 0

Y para recuperar esto Timetsamp no pueden usar un OUTPUT cláusula.

Eso es porque si hay un disparador sobre la mesa, cualquier Timestamp tu SALIDA será incorrecta:

  • Inserto inicial. Marca de tiempo: 1
  • Marca de tiempo de salidas de la cláusula OUTPUT: 1
  • El disparador modifica la fila. Marca de tiempo: 2

La marca de tiempo devuelta nunca Sea correcto si tiene un disparador sobre la mesa. Vos tambien debe: usar un separado SELECT.

E incluso si estuviera dispuesto a sufrir la versión en fila incorrecta, la otra razón para realizar una SELECT es que no puedes SALIR un rowversion en una variable de tabla:

DECLARE @generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)

INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO @generated_keys
VALUES('Malleable logarithmic casing');

La tercera razón para hacerlo es por simetría. Al realizar una UPDATE en una mesa con un gatillo, no pueden usar un OUTPUT cláusula. Tratando de hacer UPDATE con una OUTPUT no es compatible y dará un error:

La única forma de hacerlo es con un seguimiento SELECT declaración:

UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))

SELECT RowVersion
FROM TurboEncabulators
WHERE @@ROWCOUNT > 0 AND TurboEncabulatorID = 1

Respondido el 21 de diciembre de 19 a las 14:12

Me imagino que los combinan para garantizar la integridad (por ejemplo, en el modo de concurrencia optimista, mientras selecciona de la variable de la tabla, alguien puede haber eliminado las filas del insertador). Además, ama a tu TurboEncabulators :) - Zaitsman

De MSDN

@@ IDENTITY, SCOPE_IDENTITY e IDENT_CURRENT son funciones similares, ya que devuelven el último valor insertado en la columna IDENTITY de una tabla.

@@ IDENTITY y SCOPE_IDENTITY devolverán el último valor de identidad generado en cualquier tabla de la sesión actual. Sin embargo, SCOPE_IDENTITY devuelve el valor solo dentro del alcance actual; @@ IDENTITY no está limitado a un alcance específico.

IDENT_CURRENT no está limitado por el alcance y la sesión; se limita a una tabla especificada. IDENT_CURRENT devuelve el valor de identidad generado para una tabla específica en cualquier sesión y cualquier ámbito. Para obtener más información, vea IDENT_CURRENT.

  • IDENT_CURRENT es una función que toma una tabla como argumento.
  • @@ IDENTITY puede devolver un resultado confuso cuando tiene un disparador sobre la mesa
  • SCOPE_IDENTITY es tu héroe la mayor parte del tiempo.

Respondido el 14 de enero de 21 a las 02:01

No puedo hablar con otras versiones de SQL Server, pero en 2012, la salida directa funciona bien. No necesitas molestarte con una mesa temporal.

INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)

Por cierto, esta técnica también funciona al insertar varias filas.

INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
    (...),
    (...),
    (...)

Salida

ID
2
3
4

Respondido el 06 de junio de 18 a las 17:06

Sin embargo, si quieres usarlo más tarde, imagino que necesitas la tabla temporal - DisfrutoComiendoVerduras

@JohnOsborne Puede usar una tabla temporal si lo desea, pero mi punto es que no es un requisito de OUTPUT. Si no necesita la tabla temporal, su consulta termina siendo mucho más simple. - MarredQueso

@@ IDENTIDAD es la última identidad insertada utilizando la conexión SQL actual. Este es un buen valor para devolver desde un procedimiento almacenado de inserción, donde solo necesita la identidad insertada para su nuevo registro, y no le importa si se agregaron más filas después.

ALCANCE_IDENTIDAD es la última identidad insertada usando la conexión SQL actual, y en el alcance actual, es decir, si hubiera una segunda IDENTITY insertada basada en un disparador después de su inserción, no se reflejaría en SCOPE_IDENTITY, solo la inserción que realizó. Francamente, nunca he tenido una razón para usar esto.

IDENT_CURRENT (nombre de tabla) es la última identidad insertada independientemente de la conexión o el alcance. Puede usar esto si desea obtener el valor de IDENTIDAD actual para una tabla en la que no ha insertado un registro.

Respondido 10 Oct 17, 22:10

Nunca debe usar @@ identity para este propósito. Si alguien agrega un disparador más tarde, perderá la integridad de los datos. @@ identiy es una práctica extremadamente peligrosa. - HLGEM

"valor para una tabla que tiene < > insertó un registro en. "¿En serio? - Abdul Sabur

SIEMPRE use scope_identity (), NUNCA hay necesidad de nada más.

Respondido 09 Oct 09, 21:10

No exactamente nunca pero 99 de cada 100 veces, usará Scope_Identity (). - CJM

¿Para qué has usado alguna otra cosa? - erikallen

si inserta varias filas con un INSERT-SELECT, necesitará capturar las múltiples ID usando la cláusula OUTPUT - KM.

@KM: Sí, pero me referí a scope_identity vs @@ identity vs ident_current. OUTPUT es una clase completamente diferente y, a menudo, útil. - erikallen

Echa un vistazo a Orry's ( stackoverflow.com/a/6073578/2440976) responda a esta pregunta - en paralelismo, y solo como una mejor práctica, sería prudente seguir su configuración ... ¡simplemente brillante! - Dan B

Otra forma de garantizar la identidad de las filas que inserta es especificar los valores de identidad y utilizar el SET IDENTITY_INSERT ON y entonces OFF. ¡Esto le garantiza saber exactamente cuáles son los valores de identidad! Siempre que los valores no estén en uso, puede insertar estos valores en la columna de identidad.

CREATE TABLE #foo 
  ( 
     fooid   INT IDENTITY NOT NULL, 
     fooname VARCHAR(20) 
  ) 

SELECT @@Identity            AS [@@Identity], 
       Scope_identity()      AS [SCOPE_IDENTITY()], 
       Ident_current('#Foo') AS [IDENT_CURRENT] 

SET IDENTITY_INSERT #foo ON 

INSERT INTO #foo 
            (fooid, 
             fooname) 
VALUES      (1, 
             'one'), 
            (2, 
             'Two') 

SET IDENTITY_INSERT #foo OFF 

SELECT @@Identity            AS [@@Identity], 
       Scope_identity()      AS [SCOPE_IDENTITY()], 
       Ident_current('#Foo') AS [IDENT_CURRENT] 

INSERT INTO #foo 
            (fooname) 
VALUES      ('Three') 

SELECT @@Identity            AS [@@Identity], 
       Scope_identity()      AS [SCOPE_IDENTITY()], 
       Ident_current('#Foo') AS [IDENT_CURRENT] 

-- YOU CAN INSERT  
SET IDENTITY_INSERT #foo ON 

INSERT INTO #foo 
            (fooid, 
             fooname) 
VALUES      (10, 
             'Ten'), 
            (11, 
             'Eleven') 

SET IDENTITY_INSERT #foo OFF 

SELECT @@Identity            AS [@@Identity], 
       Scope_identity()      AS [SCOPE_IDENTITY()], 
       Ident_current('#Foo') AS [IDENT_CURRENT] 

SELECT * 
FROM   #foo 

Esta puede ser una técnica muy útil si está cargando datos de otra fuente o fusionando datos de dos bases de datos, etc.

Respondido el 10 de diciembre de 19 a las 12:12

Crear un uuid y también insértelo en una columna. Entonces puede identificar fácilmente su fila con el uuid. Esa es la única solución 100% funcional que puede implementar. Todas las demás soluciones son demasiado complicadas o no funcionan en los mismos casos extremos. P.ej:

1) Crear fila

INSERT INTO table (uuid, name, street, zip) 
        VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');

2) Obtener fila creada

SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';

Respondido el 24 de diciembre de 19 a las 11:12

No olvide crear un índice para uuid en la base de datos. Entonces la fila se encontrará más rápido. - franco roth

Para node.js, puede usar este módulo para simplemente crear un uuid: https://www.npmjs.com/package/uuid. const uuidv4 = require('uuid/v4'); const uuid = uuidv4() - franco roth

Un GUID no es un valor de identidad, tiene algunos retrocesos en comparación con un entero simple. - Alejandro

Además, si el UUID se genera en el nivel de la tabla SQL como un UNIQUEIDENTIFIER tipo de datos con un valor predeterminado de newid() entonces no podrá obtenerlo con este método. Entonces necesitaría INSERT, dejando el UUID en blanco y luego hacer el OUTPUT INSERTED.uuid para obtenerlo - Keith E. Truesdell

Aunque este es un hilo más antiguo, hay una forma más nueva de hacer esto que evita algunos de los errores de la columna IDENTIDAD en versiones anteriores de SQL Server, como huecos en los valores de identidad después de que el servidor se reinicia. Las secuencias están disponibles en SQL Server 2016 en adelante, que es la forma más nueva de crear un objeto SEQUENCE utilizando TSQL. Esto le permite crear su propio objeto de secuencia numérica en SQL Server y controlar cómo se incrementa.

Aquí hay un ejemplo:

CREATE SEQUENCE CountBy1  
    START WITH 1  
    INCREMENT BY 1 ;  
GO  

Luego, en TSQL, haría lo siguiente para obtener el siguiente ID de secuencia:

SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO

Aquí están los enlaces a CREAR SECUENCIA y PRÓXIMO VALOR PARA

Respondido 19 Feb 20, 17:02

Las secuencias tienen los mismos problemas de identidad, como los huecos (que en realidad no son problemas). - Alejandro

Las brechas de identidad se produjeron aleatoriamente cuando se reinició SQL Server. Estas brechas no ocurren en los nuevos incrementos de SECUENCIA, a menos que el desarrollador no use la SECUENCIA que se genera, o deshaga una transacción que iba a usar el siguiente ID de SECUENCIA. De la documentación en línea: El objeto de secuencia genera números de acuerdo con su definición, pero el objeto de secuencia no controla cómo se usan los números. Los números de secuencia insertados en una tabla pueden tener espacios cuando se revierte una transacción, ... o cuando los números de secuencia se asignan sin usarlos en tablas. - stevenje

Después de su declaración de inserción, debe agregar esto. Y asegúrese del nombre de la tabla donde se insertan los datos. Obtendrá la fila actual donde la fila se ve afectada en este momento por su declaración de inserción.

IDENT_CURRENT('tableName')

Respondido el 31 de diciembre de 17 a las 06:12

¿Notó que esta misma sugerencia ha sido respondida varias veces antes? - TT.

si. pero estoy tratando de describir la solución a mi manera. - Khan Ataur Rahman

Y si alguien más ha insertado una fila entre su declaración de inserción y su llamada IDENT_CURRENT (), obtendrá la identificación del registro que alguien más ha insertado, probablemente no lo que desea. Como se señaló en la mayoría de las respuestas anteriores, en la mayoría de los casos debería usar SCOPE_IDENTITY (). - Trondster

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.