Recentemente, herdei um banco de dados do SQL Server que usa em BINARY(16)
vez de UNIQUEIDENTIFIER
armazenar Guids. Faz isso para tudo, incluindo chaves primárias.
Devo me preocupar?
Recentemente, herdei um banco de dados do SQL Server que usa em BINARY(16)
vez de UNIQUEIDENTIFIER
armazenar Guids. Faz isso para tudo, incluindo chaves primárias.
Devo me preocupar?
Respostas:
Devo me preocupar?
Bem, há algumas coisas aqui que são um pouco preocupantes.
Primeiro: embora seja verdade que a UNIQUEIDENTIFIER
(ie Guid
) seja um valor binário de 16 bytes, também é verdade que:
INT
podem ser armazenados BINARY(4)
, DATETIME
podem ser armazenados BINARY(8)
etc), portanto, o nº 2 ↴sysname
como um alias para NVARCHAR(128)
).As três diferenças comportamentais que posso encontrar são:
Comparar UNIQUEIDENTIFIER
valores no SQL Server, para o bem ou para o mal, na verdade não é feito da mesma maneira que comparar BINARY(16)
valores. De acordo com a página do MSDN para comparar valores GUID e uniqueidentifier , ao comparar UNIQUEIDENTIFIER
valores no SQL Server:
os últimos seis bytes de um valor são mais significativos
Embora esses valores não sejam classificados com frequência, há uma pequena diferença entre esses dois tipos. De acordo com a página do MSDN para uniqueidentifier :
a ordenação não é implementada comparando os padrões de bits dos dois valores.
Como existem diferenças na maneira como os valores GUID são manipulados entre o SQL Server e o .NET (observados na página "Comparando GUID e valores exclusivos do identificador", acima), extrair esses dados do SQL Server para o código do aplicativo pode não ser tratado adequadamente em o código do aplicativo, se for necessário emular o comportamento de comparação do SQL Server. Esse comportamento pode ser emulado através da conversão para a SqlGuid
, mas um desenvolvedor saberia fazer isso?
Segundo: baseado na seguinte declaração
Faz isso para tudo, incluindo chaves primárias.
Em geral, eu me preocuparia com o desempenho do sistema usando GUIDs como PKs em vez de como Chaves Alternativas, além de usar um INT
ou mesmo BIGINT
como PK. E ainda mais preocupado se essas PKs de GUID são os índices agrupados.
O comentário a seguir, feito pelo OP na resposta de @ Rob, traz uma preocupação adicional:
foi migrado do MySQL
Os GUIDs podem ser armazenados em 2 formatos binários diferentes . Portanto, pode haver motivos de preocupação, dependendo de:
O problema com o qual a representação binária foi gerada tem a ver com a ordem de bytes dos 3 primeiros dos 4 "campos". Se você seguir o link acima para o artigo da Wikipedia, verá que o RFC 4122 especifica o uso da codificação "Big Endian" para todos os 4 campos, mas os GUIDs da Microsoft especificam o uso do Endianness "Nativo". Bem, a arquitetura da Intel é Little Endian, portanto, a ordem de bytes para os três primeiros campos é revertida dos sistemas após o RFC (assim como os GUIDs no estilo da Microsoft gerados nos sistemas Big Endian). O primeiro campo, "Dados 1", é de 4 bytes. Em um Endianness seria representado como (hipoteticamente) 0x01020304
. Mas no outro Endianness seria 0x04030201
. Então, se o banco de dados atual 'BINARY(16)
essa representação binária foi gerada em um sistema após a RFC e, em seguida, converter os dados atualmente no BINARY(16)
campo em UNIQUEIDENTIFIER
resultará em um GUID diferente do que foi criado originalmente. Isso realmente não representa um problema se os valores nunca saírem do banco de dados e os valores são sempre comparados para igualdade e não para ordem.
A preocupação com o pedido é simplesmente que eles não estarão na mesma ordem após a conversão para UNIQUEIDENTIFIER
. Felizmente, se o sistema original realmente era o MySQL, o pedido nunca foi feito na representação binária em primeiro lugar, já que o MySQL possui apenas uma representação de string do UUID .
A preocupação com os valores das strings sendo usadas fora do banco de dados é mais séria, novamente, se a representação binária foi gerada fora do Windows / SQL Server. Como a ordem dos bytes é potencialmente diferente, o mesmo GUID na forma de sequência resultaria em 2 representações binárias diferentes, dependendo de onde a conversão ocorreu. Se o código do aplicativo ou os clientes receberem um GUID em forma de sequência como ABC
proveniente de uma forma binária de 123
e a representação binária for gerada em um sistema após a RFC, a mesma representação binária (ou seja 123
) será convertida em uma forma de sequência DEF
quando convertida em a UNIQUEIDENTIFIER
. Da mesma forma, a forma original da string ABC
seria convertida em uma forma binária de 456
quando convertida em a UNIQUEIDENTIFIER
.
Portanto, se os GUIDs nunca saírem do banco de dados, não haverá muito o que se preocupar fora do pedido. Ou, se a importação do MySQL foi feita convertendo a forma de string (ou seja FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40
), isso pode estar ok. Senão, se esses GUIDs foram fornecidos aos clientes ou no código do aplicativo, você pode testar para ver como eles convertem obtendo um e convertendo via SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');
e veja se você encontra o registro esperado. Se você não conseguir corresponder os registros, talvez seja necessário manter os campos como BINARY(16)
.
Com toda a probabilidade, não haverá um problema, mas estou mencionando isso porque, nas condições certas, pode haver um problema.
E como novos GUIDs são inseridos? Gerado no código do aplicativo?
Se a explicação anterior do possível problema relacionado à importação de representações binárias de GUID geradas em outro sistema foi um pouco (ou muito) confusa, espero que o seguinte seja um pouco mais 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
Na saída mostrada acima, os valores "String" e "Binary" são do mesmo GUID. O valor abaixo da linha "Binário" é o mesmo valor da linha "Binário", mas formatado no mesmo estilo da linha "String" (ou seja, removido "0x" e adicionado os quatro traços). Comparando o primeiro e o terceiro valores, eles não são exatamente iguais, mas são muito próximos: as duas seções mais à direita são idênticas, mas as três seções mais à esquerda não são. Mas se você olhar atentamente, poderá ver que são os mesmos bytes em cada uma das três seções, apenas em uma ordem diferente. Pode ser mais fácil ver se mostro apenas as três primeiras seções e numero os bytes, para que seja mais fácil ver como a ordem deles difere entre as duas representações:
String = 1 5F 2 ED 3 23 4 BE - 5 E5 6 2C - 7 40 8 EE
Binário = 4 BE 3 23 2 ED 1 5F - 6 2C 5 E5 - 8 EE 7 40 (no Windows / SQL Server)
Portanto, em cada agrupamento, a ordem dos bytes é revertida, mas apenas no Windows e também no SQL Server. No entanto, em um sistema que adere à RFC, a representação binária espelhará a representação sting porque não haveria reversão da ordem dos bytes.
Como os dados foram trazidos para o SQL Server do MySQL? Aqui estão algumas opções:
SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));
Devoluções:
0x35464544323342452D453532432D3430
0x5FED23BEE52C40EE8F4549664C9472FD
0xBE23ED5F2CE5EE408F4549664C9472FD
Supondo que fosse binário a binário direto (ou seja, o Convert # 2 acima), o GUID resultante, se convertido em um real UNIQUEIDENTIFIER
, seria:
SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);
Devoluções:
BE23ED5F-2CE5-EE40-8F45-49664C9472FD
O que está errado. E isso nos deixa com três perguntas:
Você sempre pode estar preocupado. ;)
O sistema pode ter sido migrado de outro sistema que não suporta identificador exclusivo. Existem outros compromissos que você não conhece?
O designer pode não ter conhecimento sobre o tipo de identificador exclusivo. Que outras coisas eles não sabiam?
Tecnicamente, porém - não deve ser uma grande preocupação.