Нещодавно я успадкував базу даних SQL Server, яка використовує BINARY(16)
замість того, UNIQUEIDENTIFIER
щоб зберігати путівники. Це робиться для всього, включаючи первинні ключі.
Чи варто мене турбувати?
Нещодавно я успадкував базу даних SQL Server, яка використовує BINARY(16)
замість того, UNIQUEIDENTIFIER
щоб зберігати путівники. Це робиться для всього, включаючи первинні ключі.
Чи варто мене турбувати?
Відповіді:
Чи варто мене турбувати?
Ну, тут є декілька речей, які трохи стосуються.
По-перше: хоча це правда, що UNIQUEIDENTIFIER
(тобто Guid
) є 16-байтовим бінарним значенням, також вірно, що:
INT
можуть зберігатися в BINARY(4)
, DATETIME
можуть зберігатися в BINARY(8)
тощо), отже, №2 ↴sysname
як псевдонім для NVARCHAR(128)
).Три поведінкові відмінності, які я можу знайти:
Порівняння UNIQUEIDENTIFIER
значень у SQL Server для кращого або гіршого насправді не робиться так само, як порівняння BINARY(16)
значень. Відповідно до сторінки MSDN для порівняння значень GUID та унікальних ідентифікаторів при порівнянні UNIQUEIDENTIFIER
значень у SQL Server:
останні шість байтів значення є найбільш значущими
Хоча ці значення часто не сортуються, між цими двома типами є незначна різниця. Відповідно до сторінки MSDN для унікального ідентифікатора :
упорядкування не реалізується шляхом порівняння бітових моделей двох значень.
Зважаючи на те, що існують відмінності в обробці значень GUID між SQL сервером та .NET (зазначено на сторінці "Порівняння значень GUID та унікальних ідентифікаторів", витягнення цих даних із SQL Server у код додатка не може бути належним чином вирішено в код програми, якщо потрібно емулювати поведінку порівняння SQL Server. Таку поведінку можна наслідувати переходом до SqlGuid
, але чи знає розробник це робити?
Другий: на основі наступного твердження
Це робиться для всього, включаючи первинні ключі.
Я б взагалі занепокоєний ефективністю системи, використовуючи GUID в якості ПК, а не як альтернативні ключі разом з використанням INT
або навіть BIGINT
як ПК. І ще більше стурбовано, якщо ці GUID ПК - це індекси кластеру.
Наступний коментар, зроблений ОП на відповідь @ Роба, викликає додаткову стурбованість:
він був перенесений з, я думаю, MySQL
GUID можуть зберігатися у двох різних бінарних форматах . Отже, може виникнути причина для занепокоєння залежно від:
Проблема, де генерується бінарне представлення, пов'язана з упорядкуванням байтів перших 3 з 4 "полів". Якщо ви перейдете за посиланням вище до статті у Вікіпедії, ви побачите, що RFC 4122 вказує використовувати кодування "Big Endian" для всіх 4 полів, проте GUID-адреси Microsoft визначають, використовуючи "Native" Endianness. Ну, архітектура Intel - Little Endian, тому порядок байтів для перших трьох полів змінюється на системах, що слідують за RFC (а також GUID-стилі Microsoft, створені в системах Big Endian). Перше поле "Дані 1" - це 4 байти. В одній Ендіанс це було б представлено як (гіпотетично) 0x01020304
. Але в іншому Endianness це було б 0x04030201
. Отже, якщо поле поточної бази даних BINARY(16)
було заповнене з файлу імпорту, використовуючи двійкові позначення 0x01020304що бінарне представлення було сформовано в системі, що слідує за RFC, а потім перетворення даних, що перебувають у BINARY(16)
полі, у UNIQUEIDENTIFIER
формат, призведе до іншого GUID, ніж те, що було створено спочатку. Це насправді не створює проблеми, якщо значення ніколи не виходили з бази даних, і значення лише коли-небудь порівнюються за рівність і не впорядкованість.
Проблема із замовленням полягає лише в тому, що після переходу вони не будуть в тому ж порядку UNIQUEIDENTIFIER
. На щастя, якщо оригінальною системою справді був MySQL, то впорядкування ніколи не здійснювалося на бінарному представленні, в першу чергу, оскільки MySQL має лише рядкове представлення UUID .
Побоювання з приводу того, що рядкові значення використовуються за межами бази даних, знову серйозніші, якщо двійкове представлення генерується за межами Windows / SQL Server. Оскільки впорядкування байтів потенційно відрізняється, то той самий GUID у рядковій формі призведе до двох різних двійкових уявлень, залежно від того, де відбулося перетворення. Якщо коду додатку або клієнтам було надано GUID у рядковій формі як такий, що ABC
виходить з двійкової форми, 123
а двійкове представлення генерується в системі, що слідує за RFC, то те саме бінарне представлення (тобто 123
) перетворюється на рядову форму DEF
при перетворенні в а UNIQUEIDENTIFIER
. Аналогічно, початковий рядок у формі ABC
перетворюється у двійкову форму 456
при перетворенні в a UNIQUEIDENTIFIER
.
Отже, якщо GUID ніколи не залишали базу даних, то не варто турбуватися поза замовленням. Або, якщо імпорт з MySQL був здійснений шляхом перетворення рядкової форми (тобто FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40
), то це може бути нормально. В іншому випадку, якщо ці GUID були надані клієнтам або в коді програми, ви можете перевірити, як вони конвертують, отримавши один і перетворивши через нього, SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');
і побачити, чи знайдете ви очікуваний запис. Якщо ви не можете зіставити записи, то, можливо, доведеться зберегти поля як BINARY(16)
.
Ймовірно, проблеми не буде, але я згадую про це, оскільки при правильних умовах може виникнути проблема.
І як все-таки нові GUID вставляти? Створено в коді програми?
Якщо попереднє пояснення потенційної проблеми, пов’язаної з імпортом бінарних представлень GUID, сформованих в іншій системі, було трохи (або багато) заплутаним, сподіваємось, наступне буде трохи зрозумілішим:
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
У висновку, показаному вище, значення "String" і "Binary" є з одного GUID. Значення під рядком "Бінарний" - це те саме значення, що і у рядку "Бінарний", але відформатоване у тому ж стилі, що і рядок "Рядок" (тобто видалено "0x" та додано чотири тире). Порівнюючи перше і третє значення, вони не зовсім однакові, але вони дуже близькі: правий - більшість двох розділів однаковий, а лівий - найбільш три розділи - ні. Але якщо придивитися уважно, то можна побачити, що це однакові байти у кожному з трьох розділів, просто в іншому порядку. Можливо, буде легше побачити, якщо я показую лише ці перші три розділи та нумерую байти, щоб було легше побачити, як їх порядок відрізняється між двома представленнями:
Рядок = 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 (у Windows / SQL Server)
Таким чином, у межах кожного групування впорядкування байтів змінюється зворотно, але лише в межах Windows, а також SQL Server. Однак у системі, яка приєднується до RFC, бінарне представлення відображатиме відображення sting, оскільки не було б жодного зміни стороннього порядку.
Як дані були внесені в SQL Server з MySQL? Ось кілька варіантів:
SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));
Повернення:
0x35464544323342452D453532432D3430
0x5FED23BEE52C40EE8F4549664C9472FD
0xBE23ED5F2CE5EE408F4549664C9472FD
Якщо припустити, що це був прямий бінарний у бінарний (тобто конвертувати №2 вище), то отриманий GUID, якщо він перетворюється на фактичний UNIQUEIDENTIFIER
, буде таким:
SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);
Повернення:
BE23ED5F-2CE5-EE40-8F45-49664C9472FD
Що неправильно. І це залишає перед нами три питання:
Ви завжди можете бути стурбованими. ;)
Можливо, система була перенесена з іншої системи, яка не підтримує унікальний ідентифікатор. Чи є інші компроміси, про які ви не знаєте?
Дизайнер, можливо, не знав про тип унікального ідентифікатора. Про які ще речі вони не знали?
Технічно - це не повинно викликати особливих проблем.