Pourquoi `SELECT @@ IDENTITY` renvoie une décimale?

24

J'utilise Dapper pour exécuter la requête suivante sur une instance SQL Server 2008 R2 Express à partir d'une application 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

L'appel à connection.Query<int>(sql, ...)lève une exception de distribution non valide. Je l'ai débogué et c'est au point où Dapper appelle GetValueles retournés SqlDataReader.

Le type de retour de GetValueest Object, l'inspectant dans le programme de débogage, c'est une décimale encadrée.

Si je change la sélection en SELECT CAST(@@IDENTITY as int), le retour de GetValue est un entier encadré et l'exception n'est pas levée.

La colonne Id est définitivement de type int; Pourquoi SELECT @@IDENTITYretournerait une décimale?

Quelques informations supplémentaires:

  • La base de données est toute neuve.
  • La table Customers est le seul objet que je lui ai ajouté. Il n'y a aucune autre table (utilisateur), vue, déclencheur ou procédure stockée dans la base de données.
  • Il y a 10 lignes dans la base de données, les ID sont 1,2,3,4,5,6,7,8,9,10 (c'est-à-dire que la colonne n'est pas au-delà des limites d'un int).

Ma définition de table est

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
la source
Avez-vous des déclencheurs sur la table Clients?
Richard
3
J'utiliserais SCOPE_IDENTITY () au lieu de @@ IDENTITY. @@ IDENTITY vous donnera la dernière valeur d'identité créée par n'importe quoi sur la connexion actuelle, par opposition à dans votre étendue actuelle. Ainsi, comme Richard l'a suggéré, un déclencheur modifiant une autre table et générant une identité affecterait le retour de @@ IDENTITY.
Nick Chammas
Il n'y a aucun déclencheur dans la base de données. C'est une nouvelle base de données et la table Customers est la seule table que j'ai créée.
Greg B
@Greg B: c'est le type de retour de la fonction. Vous attendiez-vous à int / bigint comme type de retour (comme le suggère la question) ou remettez-vous en question le choix MS pour cette fonction?
Marian

Réponses:

28
  1. @@ identity renvoie un numérique (38,0) . Vous aurez besoin de le lancer pour le mettre à un int.

    SELECT CAST (@@ identity AS INT)

  2. Essayez également d'utiliser scope_identity à la place. Si vous avez des déclencheurs sur la table Customers, vous pouvez finir par obtenir la dernière identité d'une autre table.

  3. Enfin, étant donné que vous utilisez dapper , vous souhaiterez envelopper tout cela dans une procédure stockée afin de garantir l'exécution de l'insertion, puis la sélection de l'identité dans le même lot.

    Théoriquement, cela devrait fonctionner la plupart du temps pour exécuter les deux seuls. Mais des problèmes peuvent survenir si vous devez accéder à la base de données deux fois. (Par exemple, comment cela fonctionne-t-il avec le regroupement de connexions? Qu'en est-il des connexions interrompues? Etc.) Si vous jetez tout cela dans une procédure stockée, vous n'aurez pas à vous soucier de cet effort supplémentaire en cours de route.

Richard
la source
Merci pour # 3. N'y a-t-il aucun moyen de définir un lot dans une instruction SQL adhoc ?
Greg B
J'y jetais un autre regard. Si vous incluez toutes les instructions dans un seul appel, il s'agit d'un seul lot. Si vous divisez les instructions en appels séparés, les choses peuvent devenir boguées.
Richard
3
+1 pour avoir recommandé SCOPE_IDENTITY ()
Andrew Bickerton
10

Créer une table dit:

" IDENTITÉ

Indique que la nouvelle colonne est une colonne d'identité. Lorsqu'une nouvelle ligne est ajoutée au tableau, Microsoft® SQL Server ™ fournit une valeur incrémentielle unique pour la colonne. Les colonnes d'identité sont couramment utilisées conjointement avec les contraintes PRIMARY KEY pour servir d'identifiant de ligne unique pour la table. La propriété IDENTITY peut être affectée aux colonnes tinyint, smallint, int, bigint, décimal (p, 0) ou numérique (p, 0). Une seule colonne d'identité peut être créée par table. Les valeurs par défaut liées et les contraintes DEFAULT ne peuvent pas être utilisées avec une colonne d'identité. Vous devez spécifier à la fois la valeur de départ et l'incrément ou aucun. Si aucun n'est spécifié, la valeur par défaut est (1,1).

la graine

Est la valeur utilisée pour la toute première ligne chargée dans la table.

incrément

La valeur incrémentielle ajoutée à la valeur d'identité de la ligne précédente est-elle chargée. "

La fonction système @@ identity devra donc faire face au type le plus couvrant.

Marian
la source
Et c'est pourquoi il revient numericcar il a la gamme la plus large ..? Merci
Greg B
3
Une fonction ne peut pas avoir plus d'un type de retour. Il devra utiliser le type le plus large pour inclure toutes les possibilités.
Marian
6

"Pourquoi SELECT @@ IDENTITY renverrait-il une décimale"

Parce qu'il peut être trop grand pour tenir dans un int- il ne correspond pas au type de la colonne d'identité mais comme Richard le dit renvoie un numérique (38,0) ( numericet decimal sont des synonymes )

Jack Douglas
la source