UUID vs GUID: qué es realmente distinto

GUID es el nombre que Microsoft da al UUID. Las diferencias que encontrará en la práctica son el formato de texto, el orden de los bytes cuando el valor se guarda como 16 bytes y la forma en que SQL Server los ordena.

Si solo necesita la respuesta corta: un GUID y un UUID son el mismo tipo de valor, un identificador de 128 bits que normalmente se escribe como 32 dígitos hexadecimales repartidos en cinco grupos. Guid.NewGuid() en .NET y crypto.randomUUID() en JavaScript producen UUID aleatorios de la versión 4. Aun así, surgen problemas de interoperabilidad, y vienen de tres sitios: cómo se escribe el valor como texto, en qué orden van sus 16 bytes y cómo lo compara SQL Server.

Dos nombres para un mismo estándar

Los UUID proceden del Network Computing System de Apollo y pasaron después al Distributed Computing Environment (DCE) de OSF. Microsoft adoptó el diseño de DCE para COM y llamó a estos valores identificadores globalmente únicos (globally unique identifiers). Más tarde, el IETF publicó el formato como RFC 4122 en julio de 2005, sustituido en mayo de 2024 por el RFC 9562, la versión que añadió UUID v6, v7 y v8. Ambos RFC dicen expresamente que los UUID también se conocen como GUID.

Hay un campo que conserva la historia del nombre. Los bits de variante al principio del cuarto grupo distinguen los UUID del RFC, en los que ese grupo empieza por 8, 9, a o b, de un rango reservado para la compatibilidad con versiones anteriores de Microsoft, que empieza por c o d. Los GUID que generan las versiones actuales de Windows y .NET usan la variante del RFC, así que en la práctica son indistinguibles.

Formatos de texto

El método Guid.ToString() de .NET acepta un especificador de formato. Estas son las salidas para un mismo valor en .NET 8:

EspecificadorSalida
Nf47ac10b58cc4372a5670e02b2c3d479
D (por defecto)f47ac10b-58cc-4372-a567-0e02b2c3d479
B{f47ac10b-58cc-4372-a567-0e02b2c3d479}
P(f47ac10b-58cc-4372-a567-0e02b2c3d479)
X{0xf47ac10b,0x58cc,0x4372,{0xa5,0x67,0x0e,0x02,0xb2,0xc3,0xd4,0x79}}

Todas están en minúsculas. El estilo en mayúsculas y con llaves, {F47AC10B-58CC-4372-A567-0E02B2C3D479}, procede del registro de Windows, de los identificadores de clase COM y de los archivos de proyecto de Visual Studio, y SQL Server también muestra los valores uniqueidentifier en mayúsculas. El RFC 4122 pide generar la salida en minúsculas y aceptar la entrada sin distinguir mayúsculas de minúsculas, y Guid.Parse acepta todas las formas de la tabla.

La regla práctica: nunca compare GUID como cadenas. "{F47AC10B-...}" y "f47ac10b-..." son cadenas distintas y, a la vez, el mismo valor. Analice ambos lados, o normalícelos a minúsculas y sin llaves antes de guardarlos o compararlos.

Orden de bytes: la diferencia que da problemas

Guardado como 16 bytes, un UUID del RFC es big-endian: los bytes van en el mismo orden que los dígitos hexadecimales. La estructura GUID de Microsoft es distinta. Se declara como un entero de 32 bits, dos enteros de 16 bits y 8 bytes sueltos, y en las máquinas little-endian los tres enteros se guardan con los bytes invertidos. Guid.ToByteArray() devuelve exactamente esa disposición, y new Guid(byte[]) es la que espera:

Text:           f47ac10b-58cc-4372-a567-0e02b2c3d479
RFC bytes:      f4 7a c1 0b  58 cc  43 72  a5 67  0e 02 b2 c3 d4 79
ToByteArray():  0b c1 7a f4  cc 58  72 43  a5 67  0e 02 b2 c3 d4 79

Los últimos 8 bytes son idénticos; los tres primeros grupos están invertidos. SQL Server usa la misma disposición de orden mixto: convertir un uniqueidentifier a binary(16) da los mismos bytes que ToByteArray().

El fallo aparece cuando un lado escribe los bytes con una disposición y el otro los lee con la otra. Un servicio .NET escribe ToByteArray() en una columna BINARY(16) o en un mensaje binario; un servicio en Java, Go o Python lee esos bytes como un UUID del RFC y obtiene 0bc17af4-cc58-7243-a567-0e02b2c3d479. El valor sigue siendo único, así que nada falla, pero las búsquedas entre los dos sistemas nunca coinciden. También se ha movido el dígito de versión: en este ejemplo, el valor desordenado afirma ser de la versión 7.

Elija una disposición y convierta de forma explícita en la frontera entre sistemas:

  • .NET 8 y posteriores: guid.ToByteArray(bigEndian: true) y new Guid(bytes, bigEndian: true) leen y escriben en el orden del RFC.
  • Python: uuid.UUID(bytes_le=data) lee la disposición de Microsoft, y u.bytes_le la escribe.
  • En cualquier entorno: si guarda texto o un tipo UUID nativo en lugar de bytes en bruto, el problema ni siquiera se plantea.

Cómo ordena SQL Server los uniqueidentifier

SQL Server no ordena los GUID tal como se leen en texto. Compara primero el último grupo de seis bytes, luego el cuarto grupo y después el tercero, el segundo y el primero, y dentro de esos tres primeros grupos pesa más el byte situado más a la derecha. Esta lista está en orden ascendente de uniqueidentifier, tal como la produce el tipo SqlGuid de .NET, que implementa la misma comparación:

01000000-0000-0000-0000-000000000000
ff000000-0000-0000-0000-000000000000
00000001-0000-0000-0000-000000000000
00000000-0100-0000-0000-000000000000
00000000-0001-0000-0000-000000000000
00000000-0000-0100-0000-000000000000
00000000-0000-0001-0000-000000000000
00000000-0000-0000-0001-000000000000
00000000-0000-0000-0100-000000000000
00000000-0000-0000-0000-000000000002
00000000-0000-0000-0000-010000000000

Si se ordenan los mismos valores como System.Guid en .NET, o como cadenas en cualquier lenguaje, el resultado es otro: 00000000-0000-0000-0000-000000000002 queda el primero y ff000000-0000-0000-0000-000000000000, el último. De ahí se derivan dos consecuencias:

  • La paginación por keyset puede saltarse o repetir filas si la aplicación ordena los GUID por su cuenta y luego pide a SQL Server las filas WHERE id > @last. Deje que la base de datos se encargue de ambas cosas.
  • UUID v7 no ayuda en SQL Server. Un valor v7 empieza con una marca de tiempo, pero SQL Server compara primero la cola aleatoria, así que las claves v7, incluidas las de Guid.CreateVersion7() de .NET 9, caen en posiciones aleatorias de un índice uniqueidentifier, igual que las claves v4.

NEWID vs NEWSEQUENTIALID

NEWID()NEWSEQUENTIALID()
Cómo se generaAleatorio, versión 4UuidCreateSequential de Windows, un UUID basado en el tiempo, con los bytes reordenados
Dónde se puede usarEn cualquier parte de T-SQLSolo en una restricción DEFAULT de una columna uniqueidentifier
OrdenAleatorioCreciente según el orden de SQL Server en esa máquina; puede volver a empezar desde valores menores tras reiniciar Windows
PredecibleNoSí: el siguiente valor se puede adivinar
Comportamiento en el índiceLas inserciones caen en cualquier parte y provocan divisiones de páginaLas inserciones van al final del índice

NEWSEQUENTIALID() existe porque las claves aleatorias fragmentan los índices agrupados (clustered). La reordenación de bytes es lo que hace que sus valores crezcan bajo la peculiar comparación de SQL Server, y también la razón por la que ordenarlos como texto no reproduce el orden en que se crearon. Como los valores se pueden adivinar, la documentación de Microsoft desaconseja usarla cuando la privacidad importa; mantenga esos IDs fuera de las URL. Si las claves se generan en la aplicación, ordenarlas como lo hace SQL Server exige colocar los bytes basados en el tiempo en el último grupo, justo lo contrario de lo que hace UUID v7.

Generar uno u otro

Como los valores son los mismos, cualquier generador de UUID v4 es también un generador de GUID. El generador de GUID produce valores aleatorios de la versión 4 al estilo del registro de Windows, en mayúsculas y con llaves, y el generador de UUID v4 produce el mismo tipo de valor en la forma del RFC, en minúsculas. Los dos permiten activar o quitar las llaves, las mayúsculas y los guiones, lo que cubre los formatos N, D y B de la tabla anterior.

Preguntas Frecuentes

¿Un GUID es lo mismo que un UUID?

Sí. GUID es el nombre que usa Microsoft para el formato UUID. Ambos son valores de 128 bits, y Guid.NewGuid() produce un UUID estándar aleatorio de la versión 4.

¿Por qué mi GUID se ve distinto después de leerlo de la base de datos?

Los tres primeros grupos tienen los bytes intercambiados. ToByteArray() de .NET y SQL Server guardan esos grupos en orden little-endian, mientras que la mayoría de los demás lenguajes leen los 16 bytes en el orden big-endian del RFC. Convierta de forma explícita, o guarde el valor como texto o en un tipo UUID nativo.

¿Los GUID deben ir en mayúsculas o en minúsculas?

Las dos formas son válidas. El RFC 4122 recomienda generar en minúsculas y aceptar la entrada sin distinguir mayúsculas; las herramientas de Windows y SQL Server los muestran en mayúsculas. Compare valores analizados, no cadenas.

¿UUID v7 mantiene ordenados los índices de SQL Server?

No. SQL Server compara primero los seis últimos bytes de un uniqueidentifier, y en un valor v7 esos bytes son aleatorios. NEWSEQUENTIALID() está ordenado para SQL Server; v7 lo está para las bases de datos que comparan los bytes de izquierda a derecha.