Un champ identity est un champ numérique de type compteur, qui n'est pas nécessairement un « int » (c'est le type le plus couramment utilisé car permettant un panel assez large de valeur). C'est une propriété du champ qui s'applique à des nombres entiers
Les types possibles pour l'identity sont :
La définition de ce type se fait grâce à la commande suivante :
IDENTITY [ (seed,increment) * [NOT FOR REPLICATION]
Où :
Le champ portant l'attribut IDENTITY n'est pas nécessairement clef primaire, il n'est pas obligatoire de l'indexer, il ne porte pas de contrainte unique. Ce champ ne doit pas permettre les valeurs nulles et ne doit pas avoir de valeur par défaut.
Il est par contre unique dans la table, en aucun cas vous ne pourrez en ajouter un deuxième dans la même table. Vous ne pouvez pas par défaut insérer de valeur directement dans un champ de ce type, sauf exceptions (voir plus loin).
Les valeurs de ce champ ne sont pas continues, et elles ne dépendent pas la transaction courante (et ne pas bas être affectées par un ROLLBACK).
L'identity est lié à un champ dans une table, voici comment il est défini dans la déclaration d'une table
CREATE TABLE MaTable ( Clef int IDENTITY(1,1) NOT NULL, MonChamp varchar(100) )
==> Je reprends la définition de cette table tout au long de mes exemples de codes T-SQL
Pour connaître les propriétés de ce champ vous avez à votre disposition plusieurs fonctionnalités.
Tout d'abord ne jamais utiliser deSELECT MAX(Clef) !!!!! Cette commande ne vous
provoquera que des problèmes. Il existe 1 seul et unique cas où elle fonctionnera convenablement, c'est si vous utiliser le niveau d'isolation SERIALIZABLE qui interdira toute insertion pendant le SELECT, cependant cela engendrera un verrouillage important de la
Il est possible de connaître les valeurs insérées dans ce type de champ grâce à :
Dans tous les cas seule la dernière valeur est renvoyée. Si vous voulez obtenir la liste des valeurs insérées il vous faudra (et sous SQL Server 2005 uniquement) utiliser la clause OUTPUT :
DECLARE @liste TABLE (ChampID int) INSERT INTO MaTable (MonChamp) OUTPUT inserted.Clef INTO @liste SELECT ChampA FROM UneAutreTable SELECT * FROM @liste
Output s'appuie obligatoirement sur une variable de type table dans la clause INTO. Comme il s'agit de SQL Server 2005 vous pouvez remplacer inserted.Clef, par inserted.$IDENTITY pour obtenir un code plus générique.
Il y a 3 possibilités pour manipuler la valeur courante d'un champ identity
DBCC CHECKIDENT('MaTable', RESEED)
DBCC CHECKIDENT('MaTable', RESEED, 234)
La valeur suivante attribuée sera celle affectée (234) plus la valeur de l'incrément.
SET IDENTITY_INSERT MaTable ON
INSERT MaTable (Clef, MonChamp) VALUES(-10,'AAAA')
SETIDENTITY_INSERT MaTable OFF
La valeur courante n'est pas modifiée dans ce
cas, attention tout de même à ne pas provoquer de télescopage en insérant des
valeurs de compteur que l'identity risque d'atteindre. C'est pour cette raison
que dans l'exemple j'ai inseré la valeur -10, mon compteur étant positif, je
n'ai aucune chance de tomber sur cette valeur.
TRUNCATE TABLE MaTable
Attention au TRUNCATE TABLE qui est une opération accessible uniquement à des utilisateurs ayant des privilèges important sur le serveur. De plus cette commande ne peut
être exécutée s'il existe des clefs étrangères sur la table. A noter qu'un
DELETE simple n'affecte pas la valeur du champ identity.
Je pense que vous serez d'accord avec le fait que la méthode la plus rapide pour la copie d'une table dans SQL Server est la syntaxe SELECT INTO, qui en une seule commande duplique la structure de la table et ses données.
==> Dans les exemples qui suivent j'utilise volontairement une table temporaire locale (marqué par un #). celà fonctionne de la même manière avec une table classique.
Mais que se passe t'il si un champ de type IDENTITY se trouve dans la table source ?
SELECT * INTO #TableTemp FROM MaTable
Pour réinitialiser le champ identity avec un
nouveau compteur par exemple, on pourra utiliser :
SELECT IDENTITY(int, 1, 1) AS Clef, MonChamp INTO #MaTableTemp FROM MaTable
Ici c'est la fonction IDENTITY qui a été utilisée, elle permet la création dans la table cible d'un champ identity avec les caractéristiques renseignés, par exemple ici : Valeur initiale 1, incrément 1 et type du champ int. Vous pouvez de cette façon ajouter un champ identity à
une table n'en ayant pas lors de la copie.
A l'opposé comment supprimer l'attribut identity du champ lors d'une copie de la table ?
SELECT CAST(Clef AS int) AS Clef, MonChamp INTO #MaTableTemp FROM MaTable
On a tout simplement effectué un CAST (convertion).
Certains d'entre vous je suis sûr trouve
l'identity limité pour un certains nombres de raisons. Et bien nous allons voir
d'autres solutions possibles pour palier aux quelques besoins que vous pourriez
Pour avoir un identifiant unique au sein de la base de données il existe le type timestamp (ou rowversion dans la norme ISO dont la valeur est unique au sein de la base de données. Cependant sa valeur change dès qu'il se produit une modification sur l'un des champs de l'enregistrement. De ce fait il sert essentiellement de « traceur » de modification dans la base de données plutôt que d'identifiant unique, celà le rend très utilile dans des batch incrémentaux.
Autre alternative intéressante qui fera l'objet d'un prochain article, c'est l'utilisation du GUID qui a le double avantage d'être unique quel que soit la machine (utilisation indispensable d'une carte réseau dans ce cas) et d'être aléatoire (ne peut être deviné ou déduit de ses
valeurs précédentes).
Dernières solutions qui ressemblent un peu au principe des séquences de certains moteurs concurrents. L'idée étant de générer ses propres compteurs au sein d'une base de données ou même d'un ensemble de base de données ou de serveurs.
On va distinguer 3 catégories de compteurs
==> Les compteurs sont présentés dans l'ordre du moins contraignant (donc plus performant) au plus contraingant (donc moins performant).
La séquence lâche, que vous ne pourrez pas gérer dans le cadre d'une transaction (valeurs perdues possibles). Equivalente à l'identity pour plusieurs tables. Nécessite autant de tables que de compteurs. Le cas ici est très simple, on s'appuie sur une table contenant un identity, mais qui va être utilisé par plusieurs tables. Cette table de compteur sera en
permanence étant donné que seul nous intéresse la valeur courante de notre
IF OBJECT_ID('Sequence_Lache','U') IS NOT NULL DROP TABLE Sequence_Lache GO CREATE TABLE Sequence_Lache ( Num int Identity(1,1) PRIMARY KEY, Seq int NOT NULL DEFAULT(1) ) GO
IF OBJECT_ID('GetSeq_Lache','P') IS NOT NULL DROP PROCEDURE GetSeq_Lache GO CREATE PROCEDURE GetSeq_Lache @value int OUTPUT AS DECLARE @acttran int SET @acttran = @@TRANCOUNT IF @@TRANCOUNT= 0 BEGIN TRAN SAVE TRAN GetSeq INSERT INTO Sequence_Lache (Seq) VALUES(0) SET @value = @@IDENTITY ROLLBACK TRAN GetSeq IF @acttran != @@TRANCOUNT ROLLBACK TRAN GO
DECLARE @val int EXEC GetSeq_Lache @val OUTPUT SELECT @val
La séquence non lâche, gérable dans la cadre d'une transaction. Nécessite une seule table pour un ensemble de compteurs.
IF OBJECT_ID('Sequence_NonLache','U') IS NOT NULL DROP TABLE Sequence_NonLache GO CREATE TABLE Sequence_NonLache ( Num int Identity(1,1) PRIMARY KEY, Seq int NOT NULL DEFAULT(1), Inc int NOT NULL DEFAULT(1) ) GO
INSERT INTO Sequence_NonLache (Seq, Inc) VALUES(1,1) GO
IF OBJECT_ID('GetSeq_NonLache','P') IS NOT NULL DROP PROCEDURE GetSeq_NonLache GO
CREATE PROCEDURE GetSeq_NonLache @num int, @value int OUTPUT AS DECLARE @acttran int SET @acttran = @@TRANCOUNT IF @@TRANCOUNT= 0 BEGIN TRAN UPDATE Sequence_NonLache SET Seq = Seq + Inc WHERE Num = @num SELECT @value = Seq FROM Sequence_NonLache WHERE Num = @num IF @acttran != @@TRANCOUNT COMMIT TRAN GO
DECLARE @val int EXEC GetSeq_NonLache 1, @val OUTPUT SELECT @val
Dernière solution le compteur dense, qui va récupérer les « trous » dans la liste des identifiants. On pourra s'appuyer dans le cas de cette solution sur table contenant la liste de valeur de compteur qui servira de référence et pour déterminer la première valeur de libre faire une jointure entre les table pour déterminer les valeurs manquantes, récupérer
la valeur minimale, verrouiller l'enregistrement correspondant et le tour est
joué.
Où se trouvent les informations concernant l'identity dans les tables systèmes ?
Comme c'est un attribut de champ, vous les trouverez sans surprise dans la table système syscolumns (ou la vue système syscolumns sur SQL Server 2005).
SELECT autoval, colstat, status FROM sys.syscolumns
Ces 3 champs contiennent toutes les informations sur les identity. autoval contient la valeur courante, initiale et celle de l'incrément de l'identity ainsi que d'autres options, c'est un champ binaire (Je vous déconseille de toucher à autoval directement). Le champ colstat
peut se révéler intéressant à modifier, car c'est lui qui porte le status de l'identity (avec le champ status) avec entre autre l'attribut NOT FOR REPLICATION. Le champ status permet de savoir si ce champ est identity.
SELECT id, colid, autoval, status, colstat
FROM sys.syscolumns
WHERE status & 0x80 = 0x80
Plus simplement sous SQL Server 2005 on utilisera les vues systèmes pour identifier ces champs.
SELECT column_id, name, is_identity FROM sys.columns WHERE is_identity = 1
Sous SQL Server 2000 il est possible de modifier sous certaines conditions les tables systèmes, même si cela n'est pas conseillé. S'il vous arrivait de toucher aux champs définissant l'identity, utilisez impérativement DBCC CHECKIDENT après la modification pour vous assurez que les valeurs des champs définissant l'identity sont dans un état cohérant.
Sous SQL Server 2005 vous n'avez plus cette possibilité.
EXEC sp_configure 'allow updates', 1 RECONFIGURE WITH OVERRIDE GO BEGIN TRAN UPDATE syscolumns SET -- A vous de compléter ;o) DBCC CHECKIDENT('matable') COMMIT TRAN GO EXEC sp_configure 'allow updates', 0 RECONFIGURE WITH OVERRIDE GO
Dernier point, sachez que le changement de la valeur du champ de type identity est une opération journalisée (qui par contre ne peut être annulée via un ROLLBACK), c'est-à-dire qu'elle nécessite l'inscription d'informations dans le journal de transaction. Ceci s'explique par le fait que la valeur courante du compteur est préservée dans la table système
évoquée ci-dessus.
Voilà c'est tout sur l'identity... En commençant avec çà je ne pensais pas en faire 8 pages sous Word... Comme quoi le sujet est vaste et souvent malheureusement mal connu.
Bon développements avec SQL Server !!!