¿Por qué `SELECT @@ IDENTITY` devuelve un decimal?

24

Estoy usando Dapper para ejecutar la siguiente consulta en una instancia de SQL Server 2008 R2 Express desde una aplicación ASP.NET MVC 3 (.NET 4.0).

INSERT INTO Customers (
         Type, Name, Address, ContactName, 
         ContactNumber, ContactEmail, Supplier)
VALUES (
         @Type, @Name, @Address, @ContactName, 
         @ContactNumber, @ContactEmail, @Supplier)

SELECT @@IDENTITY

La llamada a connection.Query<int>(sql, ...)está lanzando una excepción de lanzamiento no válida. Lo he depurado y es en el punto donde Dapper llama GetValueal devuelto SqlDataReader.

El tipo de retorno de GetValuees Object, inspeccionándolo en el programa del depurador, es un decimal en caja.

Si cambio la selección a SELECT CAST(@@IDENTITY as int), el retorno de GetValue es un int en caja y la excepción no se produce.

La columna Id es definitivamente del tipo int; ¿Por qué SELECT @@IDENTITYdevolvería un decimal?

Alguna información adicional:

  • La base de datos es completamente nueva.
  • La tabla Clientes es el único objeto que le he agregado. No hay otras tablas (vistas), vistas, disparadores o procedimientos almacenados en la base de datos.
  • Hay 10 filas en la base de datos, las Id son 1,2,3,4,5,6,7,8,9,10 (es decir, la columna no está más allá de los límites de un int).

La definición de mi tabla es

CREATE TABLE [dbo].[Customers](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Type] [int] NOT NULL,
    [Name] [nvarchar](255) NOT NULL,
    [Address] [nvarchar](1000) NOT NULL,
    [ContactName] [nvarchar](255) NOT NULL,
    [ContactNumber] [nvarchar](50) NOT NULL,
    [ContactEmail] [nvarchar](255) NOT NULL,
    [Supplier] [nvarchar](255) NOT NULL,
 CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (
    PAD_INDEX  = OFF, 
    STATISTICS_NORECOMPUTE  = OFF, 
    IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS  = ON, 
    ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
Greg B
fuente
¿Tiene algún desencadenante en la tabla Clientes?
Richard
3
Me gustaría utilizar SCOPE_IDENTITY () en lugar de @@ IDENTITY. @@ IDENTITY le dará el último valor de identidad creado por cualquier cosa en la conexión actual, a diferencia de su alcance actual. Entonces, como sugirió Richard, un disparador que modifica otra tabla y genera una identidad afectaría el retorno de @@ IDENTITY.
Nick Chammas
No hay desencadenantes en la base de datos. Es una base de datos nueva y la tabla Clientes es la única tabla que he creado.
Greg B
@ Greg B: este es el tipo de retorno de la función. ¿Esperaba int / bigint como el tipo de retorno (como sugiere la pregunta) o está cuestionando la elección de MS para esta función?
Marian

Respuestas:

28
  1. @@ identity devuelve un valor numérico (38,0) . Tendrás que lanzarlo para llevarlo a un int.

    SELECT CAST (@@ identidad como INT)

  2. Además, intente usar scope_identity en su lugar. Si tiene algún desencadenante en la tabla Clientes, puede terminar obteniendo la última identidad de otra tabla.

  3. Finalmente, dado que está usando Dapper , querrá incluir todo eso dentro de un procedimiento almacenado para garantizar la ejecución de la inserción y luego la selección de la identidad en el mismo lote.

    Teóricamente, debería funcionar la mayor parte del tiempo ejecutar ambos por su cuenta. Pero podrían surgir problemas si tiene que ir a la base de datos dos veces. (Por ejemplo, ¿cómo funciona esto con la agrupación de conexiones? ¿Qué pasa con las conexiones caídas? Etc.) Si simplemente lo arroja todo en un procedimiento almacenado, no tendrá que preocuparse por ese esfuerzo adicional en el futuro.

Ricardo
fuente
Gracias por el # 3. ¿No hay forma de definir un lote en una declaración SQL adhoc ?
Greg B
Lo estaba mirando de nuevo. Si incluye todas las declaraciones en una llamada, es todo un lote. Si divide las declaraciones en llamadas separadas, las cosas podrían ponerse defectuosas.
Richard
3
+1 por recomendar SCOPE_IDENTITY ()
Andrew Bickerton
10

Crear tabla dice:

" IDENTIDAD

Indica que la nueva columna es una columna de identidad. Cuando se agrega una nueva fila a la tabla, Microsoft® SQL Server ™ proporciona un valor incremental único para la columna. Las columnas de identidad se usan comúnmente junto con restricciones de PRIMARY KEY para servir como el identificador único de fila para la tabla. La propiedad IDENTITY se puede asignar a las columnas tinyint, smallint, int, bigint, decimal (p, 0) o numérica (p, 0). Solo se puede crear una columna de identidad por tabla. Los valores predeterminados enlazados y las restricciones POR DEFECTO no se pueden usar con una columna de identidad. Debe especificar tanto la semilla como el incremento o ninguno. Si no se especifica ninguno, el valor predeterminado es (1,1).

semilla

Es el valor utilizado para la primera fila cargada en la tabla.

incremento

Es el valor incremental agregado al valor de identidad de la fila anterior cargada ".

Por lo tanto, la función del sistema @@ identity tendrá que hacer frente al tipo más completo.

Mariana
fuente
¿Y es por eso que regresa numericya que tiene el rango más amplio ...? Gracias
Greg B
3
Una función no puede tener más de un tipo de retorno. Tendrá que usar el tipo más amplio para incluir todas las posibilidades.
Marian
6

"¿Por qué SELECT @@ IDENTITY devolvería un decimal"

Debido a que puede ser demasiado grande para caber en un int- no coincide con el tipo de la columna de identidad, pero como Richard dice devuelve un número (38,0) ( numericy decimal son sinónimos )

Jack Douglas
fuente