Niedawno odziedziczyłem bazę danych SQL Server, która używa BINARY(16)
zamiast UNIQUEIDENTIFIER
przechowywać Guids. Robi to wszystko, w tym klucze podstawowe.
Czy powinienem się martwić?
Niedawno odziedziczyłem bazę danych SQL Server, która używa BINARY(16)
zamiast UNIQUEIDENTIFIER
przechowywać Guids. Robi to wszystko, w tym klucze podstawowe.
Czy powinienem się martwić?
Odpowiedzi:
Czy powinienem się martwić?
Jest tu kilka rzeczy, które są trochę niepokojące.
Po pierwsze: chociaż prawdą jest, że a UNIQUEIDENTIFIER
(tj. Guid
) Jest 16-bajtową wartością binarną, prawdą jest również, że:
INT
Mogą być przechowywane BINARY(4)
, DATETIME
mogą być przechowywane w BINARY(8)
itp.), Stąd # 2 ↴sysname
Jako alias NVARCHAR(128)
).Trzy różnice behawioralne, które mogę znaleźć to:
Porównywanie UNIQUEIDENTIFIER
wartości w SQL Server, na lepsze lub na gorsze, w rzeczywistości nie odbywa się w ten sam sposób, co porównywanie BINARY(16)
wartości. Zgodnie ze stroną MSDN do porównywania wartości identyfikatora GUID i unikalnego identyfikatora podczas porównywania UNIQUEIDENTIFIER
wartości w programie SQL Server:
ostatnie sześć bajtów wartości jest najbardziej znaczących
Chociaż te wartości nie są często sortowane, istnieje niewielka różnica między tymi dwoma typami. Według strony MSDN dla unikalnego identyfikatora :
porządkowanie nie jest realizowane przez porównywanie wzorów bitowych dwóch wartości.
Biorąc pod uwagę, że istnieją różnice w sposobie, w jaki wartości GUID są obsługiwane między SQL Server i .NET (zauważono na stronie „Porównywanie wartości GUID i unikatowych identyfikatorów” powyżej), wyciągnięcie tych danych z SQL Server do kodu aplikacji może nie być właściwie obsługiwane w kod aplikacji, jeśli trzeba emulować zachowanie porównawcze programu SQL Server. To zachowanie można emulować, konwertując na a SqlGuid
, ale czy programista wiedziałby o tym?
Po drugie: w oparciu o następujące oświadczenie
Robi to wszystko, w tym klucze podstawowe.
Byłbym ogólnie zaniepokojony wydajnością systemu za pomocą GUID-ów jako PK zamiast kluczy alternatywnych wraz z używaniem INT
lub nawet BIGINT
jako PK. I jeszcze bardziej zaniepokojony, jeśli te GUID PK są indeksami klastrowymi.
Poniższy komentarz, zamieszczony przez OP w odpowiedzi na @ Roba, budzi dodatkowe obawy:
został migrowany z MySQL
Identyfikatory GUID mogą być przechowywane w 2 różnych formatach binarnych . Tak więc, nie mógłby być powodem do niepokoju w zależności od:
Problem z wygenerowaniem reprezentacji binarnej dotyczy uporządkowania bajtów pierwszych 3 z 4 „pól”. Jeśli klikniesz powyższy link do artykułu w Wikipedii, zobaczysz, że RFC 4122 określa użycie kodowania „Big Endian” dla wszystkich 4 pól, ale identyfikatory GUID firmy Microsoft określają użycie „Native” Endianness. Architektura Intela to Little Endian, stąd kolejność bajtów dla pierwszych 3 pól jest odwrócona od systemów zgodnych z RFC (podobnie jak identyfikatory GUID typu Microsoft generowane w systemach Big Endian). Pierwsze pole, „Dane 1”, ma 4 bajty. W jednym Endianness byłby reprezentowany jako (hipotetycznie) 0x01020304
. Ale w drugiej Endianness byłoby to 0x04030201
. Więc jeśli bieżąca baza danych „BINARY(16)
ta reprezentacja binarna została wygenerowana w systemie zgodnym z RFC, a następnie konwersja danych znajdujących się obecnie w BINARY(16)
polu na inny UNIQUEIDENTIFIER
spowoduje, że GUID będzie inny niż pierwotnie utworzony. Nie stanowi to tak naprawdę problemu, JEŻELI wartości nigdy nie opuściły bazy danych, a wartości są zawsze porównywane tylko dla równości, a nie porządku.
Problem z zamawianiem polega po prostu na tym, że po konwersji nie będą one w tej samej kolejności UNIQUEIDENTIFIER
. Na szczęście, jeśli oryginalnym systemem naprawdę był MySQL, to w ogóle nigdy nie przeprowadzono porządkowania reprezentacji binarnej, ponieważ MySQL ma tylko ciąg znaków UUID .
Niepokój związany z używaniem wartości ciągu poza bazą danych jest poważniejszy, jeśli reprezentacja binarna została wygenerowana poza systemem Windows / SQL Server. Ponieważ kolejność bajtów jest potencjalnie różna, ten sam identyfikator GUID w postaci ciągu spowodowałby 2 różne reprezentacje binarne, w zależności od tego, gdzie miała miejsce ta konwersja. Jeśli kod aplikacji lub klienci otrzymają identyfikator GUID w postaci ciągu jako ABC
pochodzący z postaci binarnej, 123
a reprezentacja binarna zostanie wygenerowana w systemie zgodnym z RFC, wówczas ta sama reprezentacja binarna (tj. 123
) Przełoży się na postać ciągową DEF
po przekonwertowaniu na UNIQUEIDENTIFIER
. Podobnie, oryginalna forma ciągu ABC
przekształciłaby się w postać binarną 456
po konwersji na a UNIQUEIDENTIFIER
.
Jeśli więc identyfikatory GUID nigdy nie opuściły bazy danych, nie ma powodów do obaw poza zamówieniem. Lub, jeśli import z MySQL został dokonany przez konwersję postaci ciągu (tj. FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40
), Może być w porządku. W przeciwnym razie, jeśli te identyfikatory GUID zostały podane klientom lub w kodzie aplikacji, możesz przetestować, aby przekonać się, w jaki sposób dokonują konwersji, uzyskując jeden i konwertując je, SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');
i sprawdzając, czy znajdziesz oczekiwany rekord. Jeśli nie możesz dopasować rekordów, być może będziesz musiał zachować pola jako BINARY(16)
.
Najprawdopodobniej nie będzie problemu, ale wspominam o tym, ponieważ w odpowiednich warunkach może wystąpić problem.
A w jaki sposób wstawiane są nowe identyfikatory GUID? Wygenerowano w kodzie aplikacji?
Jeśli poprzednie wyjaśnienie potencjalnego problemu związanego z importowaniem reprezentacji binarnych identyfikatorów GUID wygenerowanych w innym systemie było nieco (lub bardzo) mylące, mam nadzieję, że następujące wyjaśnienia będą nieco jaśniejsze:
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
W wynikach pokazanych powyżej wartości „String” i „Binary” pochodzą z tego samego identyfikatora GUID. Wartość pod wierszem „Binarna” jest taka sama jak linia „Binarna”, ale sformatowana w tym samym stylu co linia „Ciąg” (tzn. Usunięta „0x” i dodała cztery myślniki). Porównując pierwszą i trzecią wartość, nie są dokładnie takie same, ale są bardzo blisko: dwie skrajnie prawe dwie sekcje są identyczne, ale trzy skrajnie lewe nie są. Ale jeśli przyjrzysz się uważnie, zobaczysz, że są to te same bajty w każdej z trzech sekcji, tylko w innej kolejności. Łatwiej byłoby zobaczyć, czy pokazuję tylko te trzy pierwsze sekcje i numeruję bajty, więc łatwiej jest zobaczyć, jak różni się ich kolejność między dwiema reprezentacjami:
Łańcuch = 1 5F 2 ED 3 23 4 BE - 5 E5 6 2C - 7 40 8 EE
Binarny = 4 BE 3 23 2 ED 1 5F - 6 2C 5 E5 - 8 EE 7 40 (w systemie Windows / SQL Server)
Tak więc w każdej grupie kolejność bajtów jest odwrócona, ale tylko w systemie Windows, a także SQL Server. Jednak w systemie zgodnym z RFC reprezentacja binarna odzwierciedlałaby reprezentację żądła, ponieważ nie nastąpiłoby odwrócenie kolejności bajtów.
W jaki sposób dane zostały wprowadzone do SQL Server z MySQL? Oto kilka opcji:
SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));
Zwroty:
0x35464544323342452D453532432D3430
0x5FED23BEE52C40EE8F4549664C9472FD
0xBE23ED5F2CE5EE408F4549664C9472FD
Zakładając, że był to prosty układ dwójkowy na dwójkowy (tj. Konwertuj nr 2 powyżej), wynikowy identyfikator GUID, jeśli zostanie przekonwertowany na rzeczywisty UNIQUEIDENTIFIER
, będzie:
SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);
Zwroty:
BE23ED5F-2CE5-EE40-8F45-49664C9472FD
Co jest złe. I to pozostawia nam trzy pytania:
Zawsze możesz się martwić. ;)
System mógł zostać migrowany z innego systemu, który nie obsługuje unikalnego identyfikatora. Czy są jeszcze inne kompromisy, o których nie wiesz?
Projektant mógł nie wiedzieć o typie unikalnego identyfikatora. O jakich innych sprawach nie wiedzieli?
Technicznie jednak - nie powinno to stanowić większego problemu.