GUID - UniqueIdentifier ?
GUID signifie Global Unique Identifier, c’est un type qui a fait son apparition dans le monde Microsoft entre autre pour identifier de manière unique un objet COM.
Le GUID est une valeur binaire de 16 octets, elle est générée par rapport à l’adresse MAC (Adresse physique de la carte) de la carte réseau présente sur la machine (Ethernet ou Token Ring). Cette adresse MAC est nécessairement unique (unicité garantie pour les cartes Ethernet, pas forcément pour d’autres adaptateurs) ce qui fait que le GUID lui aussi doit être unique quel que soit la machine où il se trouve généré. L’autres facteur de la création d’un GUID est tout simplement le temps, le reste c’est de l’aléatoire (à base de générateur pseudo-aléatoire).
Il y a plusieurs manière de générer le GUID, il y a même une composante de version incluse dans le GUID lui-même pour bien indiqué la manière dont il a été généré. Depuis Windows 2000 on utilise les fonctions de cryptographie de Windows pour générer cette valeur, l’adresse MAC n’étant plus qu’une des bases de la génération.
La forme de représentation du GUID est la suivante :
{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
Les X représentent le nombre hexadécimal représentant le GUID.
Pour plus d’informations sur le sujet :
http://fr.wikipedia.org/wiki/GUID
Sous SQL Server, le GUID est supporté depuis SQL Server 2000, le type de champ associé à un champ de type GUID est uniqueidentifier. Il est nécessaire de générer une valeur par défaut pour ce champ si vous souhaitez qu’il se remplisse seul.
Voici le code de création d’une table avec un tel champ :
CREATE TABLE MaTable<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" /?>
(
Clef uniqueidentifierDEFAULTNEWID(),
MonChamp varchar(100)
)
Ou depuis SQL Server 2005 une variante est possible
CREATE TABLE MaTable
(
Clef uniqueidentifierDEFAULTNEWSEQUENTIALID(),
MonChamp varchar(100)
)
La 2ème méthode génère des GUID triés ou séquentiel.
SQL Server se sert des GUID lors des réplications bidirectionnelles (fusion ou transactionnelle), pour justement avoir un identifiant unique quel que soit le serveur où il est généré.
A cet effet la propriété ROWGUIDCOL permet d’indiquer quel est le champ qui est utilisé pour identifier de manière unique un enregistrement, c'est-à-dire celui qui sera utilisé dans le cadre de la réplication bidirectionnelle.
$ROWGUID fait référence à ce même champ mais sans le nommer, cela permet de rendre le script plus générique en cas de changement du nom du champ par exemple.
Cette fonction peut être utilisée dans n’importe quel endroit de votre code Transact-SQL.
Les particularités des GUID générés par cette fonction sont :
· L’unicité quel que soit la machine, si celle-ci possède une carte TokenRing ou Ethernet
· L’impossibilité de définir la valeur du prochain GUID à partir d’un autre, les valeurs sont donc aléatoires
Il est aussi possible de créer ce type de valeur directement depuis un environnement de développement, via .Net avec ici un exemple en C# :
Guid myId = System.Guid.NewGuid();
Pour plus d’informations sur l’API utilisée pour la génération de ce type de valeur :
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rpc/rpc/uuidcreate.asp
Plusieurs possibilités, on peut générer le GUID au sein de l’application et dans ce cas passer sa valeur comme paramètre. De la même façon dans du code T-SQL on peut effectuer la même chose :
DECLARE @mavar uniqueidentifier
SET @mavar =NEWID()
-- On utilisera @mavar ici
Avec SQL Server 2005 il est aussi possible de faire cela :
-- Attention cet exemple est simple on ne récupère que 1 enregistrement
-- Mais il est possible de récuperer la listes des GUID inserés
DECLARE @mesguid table(guid uniqueidentifier)
INSERT INTO matable (champ1, champ2)
OUTPUT inserted.champguid INTO @mesguid
VALUES ( 'A' , 'B' )
SELECT @mesguid
Sinon sous SQL Server 2000 il n’y a pas de méthode type @@IDENTITY pour récupérer la dernière valeur de GUID générée dans la session.
Tirage aléatoire de données :
-- Renvoie 1 enregistrement de la table
SELECT TOP 1 *
FROM MaTable
ORDER BY NEWID ()
Génération de données aléatoires :
-- Renvoie un entier 32 bit signé
SELECT CHECKSUM ( NEWID ())
Cette fonction ne peut être utilisée que dans la définition d’une valeur par défaut, elle est d’ailleurs non reconnue par l’interface graphique (SQL Server Management Studio), mais cela devrait être corrigé.
Les particularités des GUID générés par cette fonction sont :
· L’unicité quel que soit la machine, si celle-ci possède une carte TokenRing ou Ethernet
· La valeur n’est pas aléatoire, elle est obtenue grâce à un numéro de séquence et l’adresse de carte réseau de la machine (adresse MAC).
· Il est possible très facilement de déterminer la machine qui a généré le GUID
Pour plus d’informations sur l’API utilisée pour la génération de ce type de valeur :
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rpc/rpc/uuidcreatesequential.asp
Comme on le voit au niveau API, la génération d’un GUID séquentiel s’appuie toujours sur une valeur de séquence, il est donc très simple de trouver la séquence de GUID générée quand on a une de ces valeurs. On n’utilisera donc pas ce type de valeur dans un environnement où il est impératif pour des questions de confidentialité de ne pas avoir de relations entre les valeurs.
Du fait que la fonction ne peut être placé que dans une valeur par défaut il n’y a moins de possibilités de connaître celle-ci.
On pourra utiliser la clause OUTPUT comme pour la valeur générée par NEWID :
-- Attention cet exemple est simple on ne récupère que 1 enregistrement
-- Mais il est possible de récuperer la listes des GUID inserés
DECLARE @mesguid table(guid uniqueidentifier)
INSERT INTO matable (champ1, champ2)
OUTPUT inserted.champguid INTO @mesguid
VALUES ( 'A' , 'B' )
SELECT @mesguid
Il est aussi possible de se baser sur un MAX ou TOP, mais attention il faut verrouiller la table pour que cela fonctionne, et cela peut nuire aux accès aux données dans la base de données (je ne recommande pas cette solution).
-- On interdit toute insertion par une session tierce
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
-- On démarre une nouvelle transaction
BEGIN TRANSACTION
-- On insère une enregistrement
INSERT INTO matable (champ1, champ2)
VALUES ( 'A' , 'B' )
-- Les GUID étant séquentiels il est plus facile de récupérer leur valeur
SELECT TOP 1 champguid
FROM matable
ORDER BY champguid
-- On valide la transaction
COMMIT TRANSACTION
-- On repasse le mode d'isolation par défaut
SET TRANSACTION READ COMMITTED
Si maintenant vous souhaitez toujours générez la valeur depuis l’application ou depuis du code T-SQL avant de l’utiliser voici le code (en C#) qu’il va vous falloir.
using System.Runtime.InteropServices;
[DllImport("rpcrt4.dll", SetLastError = true) *
static extern int UuidCreateSequential(outGuid guid);
static Guid UuidCreateSequential()
{
const int RET_OK = 0;
Guid uuid;
int ret = UuidCreateSequential(out uuid);
return uuid;
}
Vous pourrez bien entendu incorporer cette fonction dans une fonction ou procédure stockée SQLCLR ce qui pourra vous permettre facilement d’utiliser les GUID séquentiel avec plus de facilité.
Faisons maintenant un petit test avec IPCONFIG pour connaître l’adresse MAC de notre carte réseau :
Media State . . . . . . . . . . . : Media disconnected
Description . . . . . . . . . . . : Bluetooth Device (Personal Area Network)
Physical Address. . . . . . . . . : 00-16-41-5C-67-8E
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
Et jetons un coup d’œil aux données générées par cette fonction :
0feca252-7a4a-11db-94bc-0016415c678e
On voit bien ici notre numéro de séquence et … l’adresse MAC de la carte BlueTooth de mon ordinateur.
Attention à la manière dont sont comparé et trié ces types dans SQL Server, vous trouverz ici toutes les informations ç ce sujet :
http://blogs.msdn.com/sqlprogrammability/archive/2006/11/06/how-are-guids-compared-in-sql-server-2005.aspx
Premier points sur les performances, la suite viendra dans le prochain article entre les GUID et les IDENTITY. Ici il s’agit plutôt de comparer rapidement NEWSEQUENTIALID et NEWID au niveau du type de GUID.
Le GUID généré de manière séquentielle est généré de manière plus rapide étant donné qu’il n’y a pas de logique de cryptographie à utiliser. Autre avantage il est séquentiel et dans le cas de données indexé c’est un très gros avantage dans SQL Server.
En effet l’utilisation du NEWSEQUENTIALID va se comporter comme la génération d’un champ identity (et même mieux dans certains cas) et ne va pas fragmenter les index. Il garde cependant le désavantage de tous les GUID, sa taille, en effet 16 octets c’est beaucoup en terme de taille de stockage.
L’autre GUID, généré quant à lui par NEWID s’il n’est pas indexé peut être utilisé sans problème, il présente le même désavantage que son collègue, c'est-à-dire sa taille. Si par contre le champ est indexé ce type de valeur est à proscrire totalement, une valeur de type aléatoire comme celle-ci provoquant nécessairement de la fragmentation et donc un ralentissement des insertions, des mises à jour et de certaines lectures (lecture de plages d’enregistrements).
Dans la dernière partie de cette suite d’article je vous parlerais des 3 types d’identifiants GUID (séquentiel ou non) et identity en vous présentant les problèmes de performance ou gain apportés au cas par cas.