C'è una penalità per l'uso di BINARY (16) invece di UNIQUEIDENTIFIER?


19

Di recente ho ereditato un database SQL Server che utilizza BINARY(16)invece di UNIQUEIDENTIFIERarchiviare le guide. Lo fa per tutto, comprese le chiavi primarie.

Dovrei essere preoccupato?


Usa binario (16) in modo coerente in tutto? Incluso per variabili e parametri? In caso contrario, è necessario considerare gli effetti dei cast impliciti.
Martin Smith,

Sì, per fortuna non devo occuparmi anche di cast impliciti.
Jonathan Allen,

Risposte:


21

Dovrei essere preoccupato?

Bene, ci sono un paio di cose qui che sono un po 'preoccupanti.

Primo: mentre è vero che un UNIQUEIDENTIFIER(cioè Guid) è un valore binario a 16 byte, è anche vero che:

  1. Tutti i dati possono essere memorizzati in forma binaria (ad esempio, INTpotrebbe essere immagazzinate nella BINARY(4), DATETIMEpossono essere memorizzati in BINARY(8), ecc), quindi 2 # ↴
  2. Vi è probabilmente una ragione per avere un tipo di dati separato per i GUID al di fuori della semplice praticità (ad es. sysnameCome alias per NVARCHAR(128)).

Le tre differenze comportamentali che posso trovare sono:

  • Il confronto dei UNIQUEIDENTIFIERvalori in SQL Server, nel bene e nel male, non è effettivamente fatto allo stesso modo del confronto dei BINARY(16)valori. Secondo la pagina MSDN per confrontare i valori GUID e uniqueidentifier , quando si confrontano i UNIQUEIDENTIFIERvalori in SQL Server:

    gli ultimi sei byte di un valore sono più significativi

  • Sebbene questi valori non vengano ordinati di frequente, esiste una leggera differenza tra questi due tipi. Secondo la pagina MSDN per uniqueidentifier :

    l'ordinamento non viene implementato confrontando i modelli di bit dei due valori.

  • Dato che esistono differenze nel modo in cui i valori GUID vengono gestiti tra SQL Server e .NET (indicato nella pagina "Confronto tra valori GUID e uniqueidentifier" collegati sopra), l'estrazione di questi dati da SQL Server nel codice dell'app potrebbe non essere gestita correttamente in il codice dell'app se è necessario emulare il comportamento di confronto di SQL Server. Tale comportamento può essere emulato convertendosi in a SqlGuid, ma uno sviluppatore saprebbe farlo?

Secondo: basato sulla seguente dichiarazione

Lo fa per tutto, comprese le chiavi primarie.

Sarei preoccupato in generale per le prestazioni del sistema usando GUID come PK anziché come chiavi alternative insieme all'uso INTo anche BIGINTcome PK. E ancora più preoccupato se questi GUK PK sono gli indici cluster.

AGGIORNARE

Il seguente commento, fatto dall'OP sulla risposta di @ Rob, solleva un'ulteriore preoccupazione:

è stato migrato da penso a MySQL

I GUID possono essere memorizzati in 2 diversi formati binari . Quindi, potrebbero esserci motivi di preoccupazione a seconda di:

  1. su quale sistema è stata generata la rappresentazione binaria e
  2. se i valori di stringa sono stati utilizzati al di fuori del sistema originale, ad esempio nel codice dell'app o dati ai client da utilizzare nei file di importazione, ecc.

Il problema con cui è stata generata la rappresentazione binaria ha a che fare con l'ordinamento dei byte dei primi 3 dei 4 "campi". Se segui il link sopra all'articolo di Wikipedia, vedrai che RFC 4122 specifica di utilizzare la codifica "Big Endian" per tutti e 4 i campi, ma i GUID Microsoft specificano l'utilizzo di Endianness "nativo". Bene, l'architettura Intel è Little Endian, quindi l'ordine dei byte per i primi 3 campi è invertito dai sistemi che seguono l'RFC (così come i GUID in stile Microsoft generati sui sistemi Big Endian). Il primo campo, "Dati 1", è di 4 byte. In un Endianness sarebbe rappresentato come (ipoteticamente) 0x01020304. Ma nell'altro Endianness sarebbe 0x04030201. Quindi se il database corrente "BINARY(16)tale rappresentazione binaria è stata generata su un sistema che segue RFC, quindi la conversione dei dati attualmente presenti nel BINARY(16)campo in un UNIQUEIDENTIFIERrisultato risulterà in un GUID diverso da quello originariamente creato. Ciò non rappresenta un problema SE i valori non hanno mai lasciato il database e i valori vengono sempre e solo confrontati per uguaglianza e non ordinamento.

La preoccupazione per l'ordinamento è semplicemente che non saranno nello stesso ordine dopo la conversione in UNIQUEIDENTIFIER. Fortunatamente, se il sistema originale era davvero MySQL, l'ordinamento non è mai stato fatto sulla rappresentazione binaria in primo luogo poiché MySQL ha solo una rappresentazione in stringa di UUID .

La preoccupazione per i valori di stringa utilizzati all'esterno del database è ancora più grave se la rappresentazione binaria è stata generata al di fuori di Windows / SQL Server. Poiché l'ordinamento dei byte è potenzialmente diverso, lo stesso GUID in forma di stringa comporterebbe 2 diverse rappresentazioni binarie, a seconda del luogo in cui è avvenuta la conversione. Se al codice dell'app o ai clienti fosse assegnato un GUID in forma di stringa come ABCproveniente da una forma binaria di 123 e la rappresentazione binaria fosse generata su un sistema che segue RFC, quella stessa rappresentazione binaria (cioè 123) si tradurrebbe in una forma stringa di DEFquando convertita in a UNIQUEIDENTIFIER. Allo stesso modo, la forma stringa originale di ABCverrebbe convertita in una forma binaria di 456quando convertita in a UNIQUEIDENTIFIER.

Quindi, se i GUID non hanno mai lasciato il database, non c'è molto di cui preoccuparsi al di fuori dell'ordine. Oppure, se l'importazione da MySQL è stata eseguita convertendo il modulo stringa (cioè FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40), potrebbe essere ok. Altrimenti, se quei GUID sono stati dati ai clienti o nel codice dell'app, puoi testare per vedere come si convertono ottenendone uno e convertendolo tramite SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');e vedi se trovi il record previsto. Se non riesci ad abbinare i record, potresti dover mantenere i campi come BINARY(16).

Con ogni probabilità non ci sarà un problema, ma lo sto menzionando perché nelle giuste condizioni potrebbe esserci un problema.

E come vengono comunque inseriti i nuovi GUID? Generato nel codice dell'app?

AGGIORNAMENTO 2

Se la precedente spiegazione del potenziale problema relativo all'importazione di rappresentazioni binarie di GUID generati su un altro sistema era un po '(o molto) confusa, si spera che quanto segue sarà un po' più chiaro:

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

Nell'output mostrato sopra, i valori "String" e "Binary" provengono dallo stesso GUID. Il valore sotto la riga "Binary" ha lo stesso valore della riga "Binary", ma formattato nello stesso stile della riga "String" (cioè rimosso "0x" e aggiunto i quattro trattini). Confrontando il primo e il terzo valore, non sono esattamente gli stessi, ma sono molto vicini: le due sezioni più a destra sono identiche, ma le tre sezioni più a sinistra non lo sono. Ma se guardi da vicino, puoi vedere che sono gli stessi byte in ciascuna delle tre sezioni, solo in un ordine diverso. Potrebbe essere più facile vedere se mostro solo quelle prime tre sezioni e numerare i byte, quindi è più facile vedere come il loro ordine differisce tra le due rappresentazioni:

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

Quindi all'interno di ogni raggruppamento, l'ordinamento dei byte è invertito, ma solo all'interno di Windows e anche di SQL Server. Tuttavia, su un sistema che aderisce alla RFC, la rappresentazione binaria rispecchierebbe la rappresentazione pungente perché non ci sarebbe alcuna inversione dell'ordine dei byte.

Come sono stati trasferiti i dati in SQL Server da MySQL? Ecco alcune scelte:

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

Ritorna:

0x35464544323342452D453532432D3430  
0x5FED23BEE52C40EE8F4549664C9472FD  
0xBE23ED5F2CE5EE408F4549664C9472FD

Supponendo che fosse direttamente binario in binario (cioè Converti n. 2 sopra), il GUID risultante, se convertito in un effettivo UNIQUEIDENTIFIER, sarebbe:

SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);

Ritorna:

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

Che è sbagliato E questo ci lascia con tre domande:

  1. Come sono stati importati i dati in SQL Server?
  2. In quale lingua è scritto il codice dell'app?
  3. Su quale piattaforma è in esecuzione il codice dell'app?

Suppongo che i GUID vengano generati nell'applicazione, poiché non li vedo nel database.
Jonathan Allen,

Non posso dire di seguire completamente la spiegazione sull'ordinamento dei byte, ma mi sta facendo pensare all'indicizzazione. Uniqueidentifier avrebbe più o meno probabilità di provocare una frammentazione dell'indice rispetto al binario?
Jonathan Allen,

2
@JonathanAllen Ho aggiunto un'altra sezione di AGGIORNAMENTO per spiegare meglio. E no, l'indicizzazione non dovrebbe essere diversa tra loro.
Solomon Rutzky,

"Per fortuna", SQL Server non cambia l'ordinamento tra la variante 1 e la variante 2 - anche se 'potrebbe' essere memorizzato in modo diverso sul disco, è lo stesso ordine confuso in modo coerente.
user2864740

5

Puoi sempre preoccuparti. ;)

Il sistema potrebbe essere stato migrato da qualche altro sistema che non supporta IDidentificatore univoco. Ci sono altri compromessi che non conosci?

Il progettista potrebbe non essere a conoscenza del tipo di identificativo univoco. Di quali altre cose non sapevano?

Tecnicamente però - non dovrebbe essere una delle maggiori preoccupazioni.


Sì, è stato migrato da penso a MySQL. E sì, ci sono molte ... cose interessanti da guardare.
Jonathan Allen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.