web-dev-qa-db-fra.com

Y a-t-il une pénalité pour l'utilisation de BINARY (16) au lieu de UNIQUEIDENTIFIER?

J'ai récemment hérité d'une base de données SQL Server qui utilise BINARY(16) au lieu de UNIQUEIDENTIFIER pour stocker les Guids. Il le fait pour tout, y compris les clés primaires.

Dois-je m'inquiéter?

19
Jonathan Allen

Dois-je m'inquiéter?

Eh bien, il y a deux ou trois choses qui sont un peu préoccupantes.

Premièrement: alors qu'il est vrai qu'un UNIQUEIDENTIFIER (c'est-à-dire Guid) est une valeur binaire de 16 octets, il est également vrai que:

  1. Toutes les données peuvent être stockées sous forme binaire (par exemple INT peuvent être stockées dans BINARY(4), DATETIME peuvent être stockées dans BINARY(8), etc.), d'où # 2 ↴
  2. Il y a probablement une raison pour avoir un type de données séparé pour les GUID en dehors de la simple commodité (par exemple sysname comme alias pour NVARCHAR(128)).

Les trois différences de comportement que je peux trouver sont:

  • La comparaison des valeurs UNIQUEIDENTIFIER dans SQL Server, pour le meilleur ou pour le pire, ne se fait pas de la même manière que la comparaison des valeurs BINARY(16). Selon la page MSDN pour Comparaison des valeurs GUID et uniqueidentifier , lors de la comparaison des valeurs UNIQUEIDENTIFIER dans SQL Server:

    les six derniers octets d'une valeur sont les plus significatifs

  • Bien que ces valeurs ne soient pas fréquemment triées, il existe une légère différence entre ces deux types. Selon la page MSDN pour uniqueidentifier :

    l'ordre n'est pas implémenté en comparant les modèles binaires des deux valeurs.

  • Étant donné qu'il existe des différences dans la façon dont les valeurs GUID sont gérées entre SQL Server et .NET (notées dans la page "Comparaison GUID et valeurs d'identifiant unique" ci-dessus), extraire ces données de SQL Server dans le code d'application peut ne pas être traité correctement dans le code d'application si vous devez émuler le comportement de comparaison SQL Server. Ce comportement peut être émulé en le convertissant en SqlGuid, mais un développeur le saurait-il?

Deuxième: basé sur l'énoncé suivant

Il le fait pour tout, y compris les clés primaires.

Je m'inquiéterais en général pour les performances du système en utilisant des GUID comme PK au lieu de comme clés alternatives et en utilisant un INT ou même BIGINT comme PK. Et encore plus préoccupé si ces GUID PK sont les index clusterisés.

MISE À JOUR

Le commentaire suivant, fait par l'O.P.sur la réponse de @ Rob, soulève une préoccupation supplémentaire:

il a été migré de je pense que MySQL

Les GUID peuvent être stockés dans 2 formats binaires différents . Donc, il y a pourrait être préoccupant selon:

  1. sur quel système la représentation binaire a été générée, et
  2. si les valeurs de chaîne ont été utilisées en dehors du système d'origine, comme dans le code d'application ou données aux clients à utiliser dans les fichiers d'importation, etc.

Le problème avec l'endroit où la représentation binaire a été générée a à voir avec l'ordre des octets des 3 premiers des 4 "champs". Si vous suivez le lien ci-dessus pour l'article Wikipedia, vous verrez que la RFC 4122 spécifie d'utiliser le codage "Big Endian" pour les 4 champs, mais les GUID Microsoft spécifient l'utilisation de l'endianness "Native". Eh bien, l'architecture Intel est Little Endian, donc l'ordre des octets pour les 3 premiers champs est inversé à partir des systèmes suivant la RFC (ainsi que les GUID de style Microsoft générés sur les systèmes Big Endian). Le premier champ, "Data 1", est de 4 octets. Dans un endianisme, il serait représenté (hypothétiquement) 0x01020304. Mais dans l'autre Endianness, ce serait 0x04030201. Donc, si le champ BINARY(16) de la base de données actuelle a été rempli à partir d'un fichier d'importation en utilisant la notation binaire 0x01020304 et cette représentation binaire a été générée sur un système suivant le RFC, puis en convertissant les données actuellement dans le champ BINARY(16) dans un UNIQUEIDENTIFIER entraînera un GUID différent de ce qui a été créé à l'origine. Cela ne pose pas vraiment de problème [~ # ~] si [~ # ~] les valeurs n'ont jamais quitté la base de données, et les valeurs ne sont jamais comparées pour l'égalité et ne pas ordonner.

Le problème avec la commande est simplement qu'ils ne seront pas dans le même ordre après la conversion en UNIQUEIDENTIFIER. Heureusement, si le système d'origine était vraiment MySQL, la commande n'a jamais été effectuée sur la représentation binaire en premier lieu car MySQL n'a qu'une représentation sous forme de chaîne de [~ # ~] uuid [~ # ~] .

Le problème avec les valeurs de chaîne utilisées en dehors de la base de données est encore plus grave si la représentation binaire a été générée en dehors de Windows/SQL Server. Étant donné que l'ordre des octets est potentiellement différent, le même GUID sous forme de chaîne entraînerait 2 représentations binaires différentes, selon l'endroit où cette conversion a eu lieu. Si le code d'application ou les clients ont reçu un GUID sous forme de chaîne comme ABC provenant d'une forme binaire de 123 et la représentation binaire a été généré sur un système suivant le RFC, alors cette même représentation binaire (c'est-à-dire 123) se traduirait sous la forme d'une chaîne de DEF une fois convertie en UNIQUEIDENTIFIER. De même, la forme de chaîne d'origine de ABC serait convertie en une forme binaire de 456 Lors de la conversion en UNIQUEIDENTIFIER.

Donc, si les GUID n'ont jamais quitté la base de données, il n'y a pas grand-chose à craindre en dehors de la commande. Ou, si l'importation à partir de MySQL a été effectuée en convertissant la forme de chaîne (c'est-à-dire FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40), Cela peut être correct. Sinon, si ces GUID ont été donnés aux clients ou dans le code de l'application, vous pouvez tester pour voir comment ils se convertissent en en obtenant un et en convertissant via SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database'); et voir si vous trouvez l'enregistrement attendu. Si vous ne pouvez pas faire correspondre les enregistrements, vous devrez peut-être conserver les champs en tant que BINARY(16).

Selon toute vraisemblance, il n'y aura pas de problème, mais je le mentionne parce que dans les bonnes conditions, il pourrait y avoir un problème.

Et comment les nouveaux GUID sont-ils insérés de toute façon? Généré dans le code de l'application?

MISE À JOUR 2

Si l'explication précédente du problème potentiel lié à l'importation de représentations binaires de GUID générées sur un autre système était un peu (ou beaucoup) confuse, j'espère que ce qui suit sera un peu plus clair:

DECLARE @GUID UNIQUEIDENTIFIER = NEWID();
SELECT @GUID AS [String], CONVERT(BINARY(16), @GUID) AS [Binary];
-- String = 5FED23BE-E52C-40EE-8F45-49664C9472FD
-- Binary = 0xBE23ED5F2CE5EE408F4549664C9472FD
--          BE23ED5F-2CE5-EE40-8F45-49664C9472FD

Dans la sortie illustrée ci-dessus, les valeurs "String" et "Binary" proviennent du même GUID. La valeur sous la ligne "Binaire" est la même valeur que la ligne "Binaire", mais formatée dans le même style que la ligne "Chaîne" (c'est-à-dire supprimé "0x" et ajouté les quatre tirets). En comparant les première et troisième valeurs, elles ne sont pas exactement identiques, mais elles sont très proches: les deux sections les plus à droite sont identiques, mais les trois sections les plus à gauche ne le sont pas. Mais si vous regardez attentivement, vous pouvez voir que ce sont les mêmes octets dans chacune des trois sections, juste dans un ordre différent. Il pourrait être plus facile de voir si je ne montre que ces trois premières sections et de numéroter les octets, il est donc plus facile de voir comment leur ordre diffère entre les deux représentations:

String = 15F2ED3234BE -  5E562C -  7408EE
Binaire = 4ÊTRE3232ED15F -  62C5E5 -  8EE740 (sous Windows/SQL Server)

Ainsi, au sein de chaque groupe, l'ordre des octets est inversé, mais uniquement dans Windows et également SQL Server. Cependant, sur un système qui adhère à la RFC, la représentation binaire refléterait la représentation sting car il n'y aurait pas d'inversion de l'ordre des octets.

Comment les données ont-elles été importées dans SQL Server à partir de MySQL? Voici quelques choix:

SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
       CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
    CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));

Retour:

0x35464544323342452D453532432D3430  
0x5FED23BEE52C40EE8F4549664C9472FD  
0xBE23ED5F2CE5EE408F4549664C9472FD

En supposant qu'il s'agissait d'un simple binaire en binaire (c'est-à-dire Convert # 2 ci-dessus), le GUID résultant, s'il était converti en un UNIQUEIDENTIFIER réel, serait:

SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);

Retour:

BE23ED5F-2CE5-EE40-8F45-49664C9472FD

Ce qui est faux. Et cela nous laisse avec trois questions:

  1. Comment les données ont-elles été importées dans SQL Server?
  2. Dans quelle langue le code d'application est-il écrit?
  3. Sur quelle plateforme fonctionne le code de l'application?
21
Solomon Rutzky

Vous pouvez toujours être inquiet. ;)

Le système peut avoir été migré à partir d'un autre système qui ne prend pas en charge uniqueidentifier. Y a-t-il d'autres compromis que vous ne connaissez pas?

Le concepteur peut ne pas connaître le type d'identificateur unique. Quelles autres choses ignoraient-ils?

Techniquement, cela ne devrait pas être une préoccupation majeure.

5
Rob Farley