¿Hay una penalización por usar BINARY (16) en lugar de UNIQUEIDENTIFIER?

19

Recientemente heredé una base de datos de SQL Server que usa en BINARY(16)lugar de UNIQUEIDENTIFIERalmacenar Guías. Hace esto para todo, incluidas las claves principales.

¿Debería Preocuparme?

Jonathan Allen
fuente
¿Utiliza binario (16) de manera consistente? Incluyendo para variables y parámetros? Si no, debe considerar los efectos de los moldes implícitos.
Martin Smith
Sí, afortunadamente, no tengo que lidiar con los moldes implícitos también.
Jonathan Allen el

Respuestas:

21

¿Debería Preocuparme?

Bueno, hay un par de cosas aquí que son un poco preocupantes.

Primero: si bien es cierto que un UNIQUEIDENTIFIER(es decir Guid) es un valor binario de 16 bytes, también es cierto que:

  1. Todos los datos pueden almacenarse en forma binaria (por ejemplo, INTpodrían almacenarse en BINARY(4), DATETIMEpueden almacenarse en BINARY(8), etc.), por lo tanto, # 2 ↴
  2. Probablemente haya una razón para tener un tipo de datos separado para GUID fuera de la mera conveniencia (por ejemplo, sysnamecomo un alias para NVARCHAR(128)).

Las tres diferencias de comportamiento que puedo encontrar son:

  • La comparación de UNIQUEIDENTIFIERvalores en SQL Server, para bien o para mal, en realidad no se hace de la misma manera que la comparación de BINARY(16)valores. Según la página de MSDN para comparar GUID y valores de identificador único , al comparar UNIQUEIDENTIFIERvalores en SQL Server:

    los últimos seis bytes de un valor son los más significativos

  • Si bien estos valores no se ordenan con frecuencia, existe una ligera diferencia entre estos dos tipos. Según la página de MSDN para uniqueidentifier :

    el orden no se implementa comparando los patrones de bits de los dos valores.

  • Dado que existen diferencias en la forma en que se manejan los valores de GUID entre SQL Server y .NET (anotado en la página "Comparación de valores de GUID y de identificador único" vinculada anteriormente), es posible que extraer estos datos de SQL Server en el código de la aplicación no se aborde correctamente en el código de la aplicación si necesita emular el comportamiento de comparación de SQL Server. Ese comportamiento se puede emular convirtiendo a a SqlGuid, pero ¿un desarrollador sabría hacer eso?

Segundo: basado en la siguiente declaración

Hace esto para todo, incluidas las claves principales.

En general, estaría preocupado por el rendimiento del sistema mediante el uso de GUID como PK en lugar de como claves alternativas junto con el uso de INTo incluso BIGINTcomo PK. Y aún más preocupado si estos PK GUID son los índices agrupados.

ACTUALIZAR

El siguiente comentario, realizado por el OP sobre la respuesta de @ Rob, plantea una preocupación adicional:

fue migrado de creo que MySQL

Los GUID se pueden almacenar en 2 formatos binarios diferentes . Por lo tanto, podría ser motivo de preocupación dependiendo de:

  1. en qué sistema se generó la representación binaria, y
  2. si los valores de cadena se usaron fuera del sistema original, como en el código de la aplicación o se les dio a los clientes para usar en archivos de importación, etc.

El problema con el lugar donde se generó la representación binaria tiene que ver con el orden de bytes de los primeros 3 de los 4 "campos". Si sigue el enlace anterior al artículo de Wikipedia, verá que RFC 4122 especifica el uso de la codificación "Big Endian" para los 4 campos, pero los GUID de Microsoft especifican el uso de Endianness "nativo". Bueno, la arquitectura Intel es Little Endian, por lo tanto, el orden de bytes para los primeros 3 campos se invierte de los sistemas que siguen el RFC (así como los GUID de estilo Microsoft generados en los sistemas Big Endian). El primer campo, "Datos 1", es de 4 bytes. En una Endianness se representaría como (hipotéticamente) 0x01020304. Pero en el otro Endianness lo sería 0x04030201. Entonces, si la base de datos actual 'BINARY(16)esa representación binaria se generó en un sistema que sigue el RFC, y luego convertir los datos actualmente en el BINARY(16)campo en un UNIQUEIDENTIFIERresultado dará un GUID diferente al que se creó originalmente. Esto realmente no plantea un problema SI los valores nunca salieron de la base de datos, y los valores solo se comparan por igualdad y no por orden.

La preocupación con el pedido es simplemente que no estarán en el mismo orden después de la conversión UNIQUEIDENTIFIER. Afortunadamente, si el sistema original realmente era MySQL, entonces el pedido nunca se realizó en la representación binaria en primer lugar, ya que MySQL solo tiene una representación de cadena de UUID .

La preocupación con los valores de cadena que se usan fuera de la base de datos es más grave, de nuevo, si la representación binaria se generó fuera de Windows / SQL Server. Dado que el orden de los bytes es potencialmente diferente, el mismo GUID en forma de cadena daría como resultado 2 representaciones binarias diferentes, dependiendo de dónde tuvo lugar esa conversión. Si el código de la aplicación o los clientes recibieron un GUID en forma de cadena como ABCproveniente de una forma binaria 123 y la representación binaria se generó en un sistema que sigue al RFC, entonces esa misma representación binaria (es decir 123) se traduciría en una forma de cadena de DEFcuando se convierte a a UNIQUEIDENTIFIER. Del mismo modo, la forma de cadena original de ABCse convertiría en una forma binaria de 456cuando se convierte en a UNIQUEIDENTIFIER.

Entonces, si los GUID nunca salieron de la base de datos, entonces no hay mucho de qué preocuparse fuera del pedido. O, si la importación desde MySQL se realizó mediante la conversión de la forma de cadena (es decir FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40), entonces podría estar bien. De lo contrario, si se proporcionaron esos GUID a los clientes o en el código de la aplicación, puede probar para ver cómo se convierten obteniendo uno y convirtiendo a través SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');y ver si encuentra el registro esperado. Si no puede hacer coincidir los registros, es posible que deba mantener los campos como BINARY(16).

Con toda probabilidad, no habrá un problema, pero lo menciono porque en las condiciones adecuadas podría haber un problema.

¿Y cómo se insertan los nuevos GUID de todos modos? Generado en el código de la aplicación?

ACTUALIZACIÓN 2

Si la explicación anterior del problema potencial relacionado con la importación de representaciones binarias de GUID generadas en otro sistema fue un poco (o mucho) confusa, es de esperar que lo siguiente sea un poco más claro:

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

En el resultado que se muestra arriba, los valores "String" y "Binary" son del mismo GUID. El valor debajo de la línea "Binary" es el mismo valor que la línea "Binary", pero formateado en el mismo estilo que la línea "String" (es decir, eliminó "0x" y agregó los cuatro guiones). Comparando el primer y el tercer valor, no son exactamente iguales, pero están muy cerca: las dos secciones de la derecha son idénticas, pero las tres secciones de la izquierda no lo son. Pero si observa de cerca, puede ver que son los mismos bytes en cada una de las tres secciones, solo en un orden diferente. Puede ser más fácil ver si muestro solo esas primeras tres secciones y numerar los bytes para que sea más fácil ver cómo su orden difiere entre las dos representaciones:

Cadena = 1 5F 2 ED 3 23 4 BE - 5 E5 6 2C - 7 40 8 EE
Binario = 4 BE 3 23 2 ED 1 5F - 6 2C 5 E5 - 8 EE 7 40 (en Windows / SQL Server)

Entonces, dentro de cada agrupación, el orden de los bytes se invierte, pero solo dentro de Windows y también en SQL Server. Sin embargo, en un sistema que se adhiere a la RFC, la representación binaria reflejaría la representación de picadura porque no habría ninguna inversión del orden de bytes.

¿Cómo se introdujeron los datos en SQL Server desde MySQL? Aquí hay algunas opciones:

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

Devoluciones:

0x35464544323342452D453532432D3430  
0x5FED23BEE52C40EE8F4549664C9472FD  
0xBE23ED5F2CE5EE408F4549664C9472FD

Suponiendo que fuera directamente binario a binario (es decir, Convertir # 2 arriba), el GUID resultante, si se convierte en un real UNIQUEIDENTIFIER, sería:

SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);

Devoluciones:

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

Cuál está mal. Y eso nos deja con tres preguntas:

  1. ¿Cómo se importaron los datos a SQL Server?
  2. ¿En qué idioma está escrito el código de la aplicación?
  3. ¿En qué plataforma se está ejecutando el código de la aplicación?
Solomon Rutzky
fuente
Supongo que los GUID se generan en la aplicación, ya que no los veo en la base de datos.
Jonathan Allen el
No puedo decir que sigo completamente la explicación sobre el orden de los bytes, pero me está haciendo pensar en la indexación. ¿Sería más o menos probable que uniqueidentifier produzca una fragmentación del índice que el binario?
Jonathan Allen el
2
@ JonathanAllen Agregué otra sección de ACTUALIZACIÓN para poder explicar mejor. Y no, la indexación no debería ser diferente entre ellos.
Solomon Rutzky
"Afortunadamente", SQL Server no cambia el orden entre la Variante 1 y la Variante 2, incluso si 'podría' almacenarse de manera diferente en el disco, es el mismo orden confuso de manera consistente.
usuario2864740
5

Siempre puedes estar preocupado. ;)

El sistema puede haberse migrado desde otro sistema que no admite identificador único. ¿Hay otros compromisos que no conoces?

Es posible que el diseñador no haya sabido sobre el tipo de identificador único. ¿Qué otras cosas no sabían?

Técnicamente, sin embargo, no debería ser una gran preocupación.

Rob Farley
fuente
Sí, se migró desde creo que MySQL. Y sí, hay muchas ... cosas interesantes para mirar.
Jonathan Allen el