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

19

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

Dois-je m'inquiéter?

Jonathan Allen
la source
Utilise-t-il le binaire (16) de manière cohérente tout au long? Y compris pour les variables et les paramètres? Sinon, vous devez considérer les effets des transtypages implicites.
Martin Smith
Oui, heureusement, je n'ai pas non plus à gérer les conversions implicites.
Jonathan Allen

Réponses:

21

Dois-je m'inquiéter?

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

Premièrement: s'il est vrai que a UNIQUEIDENTIFIER(ie 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 INTpourraient être stockées dans BINARY(4), DATETIMEpeuvent ê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 sysnamecomme alias pour NVARCHAR(128)).

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

  • La comparaison des UNIQUEIDENTIFIERvaleurs dans SQL Server, pour le meilleur ou pour le pire, ne se fait pas en fait de la même manière que la comparaison des BINARY(16)valeurs. Selon la page MSDN pour la comparaison des valeurs GUID et uniqueidentifier , lors de la comparaison des UNIQUEIDENTIFIERvaleurs 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 traitées entre SQL Server et .NET (noté dans la page "Comparaison des valeurs GUID et uniqueidentifier" liée ci-dessus), l'extraction de ces données de SQL Server dans le code d'application peut ne pas être traitée 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 un SqlGuid, mais un développeur le saurait-il?

Deuxièmement: sur la base de la déclaration suivante

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

Je serais préoccupé en général pour les performances du système en utilisant des GUID en tant que PK plutôt qu'en tant que clés alternatives avec l'utilisation d'un INTou même en BIGINTtant que PK. Et encore plus préoccupé si ces PK GUID sont les index clusterisés.

MISE À JOUR

Le commentaire suivant, fait par le PO 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 . Ainsi, il pourrait y avoir lieu de s'inquiéter 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 la base de données actuelle "BINARY(16)cette représentation binaire a été générée sur un système suivant le RFC, puis la conversion des données actuellement sur le BINARY(16)terrain en un UNIQUEIDENTIFIERaura pour résultat 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 que pour l'égalité et non pour l'ordre.

Le problème avec la commande est simplement qu'ils ne seront pas dans le même ordre après la conversion 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 puisque MySQL n'a qu'une représentation sous forme de chaîne d' 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 recevaient un GUID sous forme de chaîne comme ABCprovenant d'une forme binaire de 123 et que la représentation binaire était générée sur un système suivant la RFC, alors cette même représentation binaire (c.-à-d. 123) Se traduirait sous forme de chaîne DEFlorsqu'elle sera convertie en a UNIQUEIDENTIFIER. De même, la forme de chaîne d'origine de ABCserait convertie en une forme binaire 456lorsqu'elle est convertie en a 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 sous 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 les mêmes, 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:

Chaîne = 1 5F 2 ED 3 23 4 BE - 5 E5 6 2C - 7 40 8 EE
Binaire = 4 BE 3 23 2 ED 1 5F - 6 2C 5 E5 - 8 EE 7 40 (sous Windows / SQL Server)

Ainsi, au sein de chaque regroupement, 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 réel UNIQUEIDENTIFIER, 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?
Solomon Rutzky
la source
Je suppose que les GUID sont générés dans l'application, car je ne les vois pas dans la base de données.
Jonathan Allen
Je ne peux pas dire que je suis complètement l'explication de l'ordre des octets, mais cela me fait penser à l'indexation. L'identificateur unique serait-il plus ou moins susceptible d'entraîner une fragmentation de l'index que le binaire?
Jonathan Allen
2
@JonathanAllen J'ai ajouté une autre section UPDATE pour, je l'espère, mieux expliquer. Et non, l'indexation ne devrait pas être différente entre eux.
Solomon Rutzky
«Heureusement», SQL Server ne change pas l'ordre entre la variante 1 et la variante 2 - même si «pourrait» être stocké différemment sur le disque, c'est toujours le même ordre confus.
user2864740
5

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.

Rob Farley
la source
Oui, il a été migré depuis MySQL, je pense. Et oui, il y a beaucoup de ... choses intéressantes à regarder.
Jonathan Allen