Recientemente heredé una base de datos de SQL Server que usa en BINARY(16)
lugar de UNIQUEIDENTIFIER
almacenar Guías. Hace esto para todo, incluidas las claves principales.
¿Debería Preocuparme?
Recientemente heredé una base de datos de SQL Server que usa en BINARY(16)
lugar de UNIQUEIDENTIFIER
almacenar Guías. Hace esto para todo, incluidas las claves principales.
¿Debería Preocuparme?
Respuestas:
¿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:
INT
podrían almacenarse en BINARY(4)
, DATETIME
pueden almacenarse en BINARY(8)
, etc.), por lo tanto, # 2 ↴sysname
como un alias para NVARCHAR(128)
).Las tres diferencias de comportamiento que puedo encontrar son:
La comparación de UNIQUEIDENTIFIER
valores 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 UNIQUEIDENTIFIER
valores 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 INT
o incluso BIGINT
como PK. Y aún más preocupado si estos PK GUID son los índices agrupados.
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:
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 UNIQUEIDENTIFIER
resultado 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 ABC
proveniente 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 DEF
cuando se convierte a a UNIQUEIDENTIFIER
. Del mismo modo, la forma de cadena original de ABC
se convertiría en una forma binaria de 456
cuando 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?
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:
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.