Я недавно унаследовал базу данных SQL Server, которая использует BINARY(16)
вместо UNIQUEIDENTIFIER
хранения Guids. Это делает это для всего, включая первичные ключи.
Должен ли я быть обеспокоен?
Я недавно унаследовал базу данных SQL Server, которая использует BINARY(16)
вместо UNIQUEIDENTIFIER
хранения Guids. Это делает это для всего, включая первичные ключи.
Должен ли я быть обеспокоен?
Ответы:
Должен ли я быть обеспокоен?
Ну, есть пара вещей, которые немного касаются.
Первое: хотя верно, что 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 Server и .NET (отмечено на странице «Сравнение значений GUID и уникальных идентификаторов», ссылка на которую приведена выше), извлечение этих данных из SQL Server в код приложения может быть неправильно обработано в код приложения, если необходимо эмулировать поведение сравнения SQL Server. Такое поведение можно эмулировать, конвертируя в a SqlGuid
, но знает ли разработчик, что делать это?
Второе: на основании следующего утверждения
Это делает это для всего, включая первичные ключи.
В целом, я бы беспокоился о производительности системы, используя GUID в качестве PK вместо альтернативных ключей вместе с использованием INT
или даже BIGINT
в качестве PK. И даже больше беспокоит, являются ли эти GUID PK кластерными индексами.
Следующий комментарий, сделанный ФП к ответу @ Роба, вызывает дополнительную озабоченность:
это было перенесено с, я думаю, MySQL
GUID могут храниться в 2 разных двоичных форматах . Таким образом, может быть причина для беспокойства в зависимости от:
Проблема с тем, где было сгенерировано двоичное представление, связана с порядком байтов первых 3 из 4 «полей». Если вы перейдете по ссылке выше на статью в Википедии, вы увидите, что RFC 4122 указывает на использование кодировки «Big Endian» для всех 4 полей, а идентификаторы Microsoft GUID указывают на использование «Собственного» Endianness. Итак, архитектура Intel имеет формат Little Endian, следовательно, порядок следования байтов для первых 3 полей меняется в зависимости от RFC (а также GUID в стиле Microsoft, генерируемых в системах Big Endian). Первое поле, «Данные 1», составляет 4 байта. В одном Endianness это будет представлено как (гипотетически) 0x01020304
. Но в другом Endianness это было бы 0x04030201
. Так что, если текущая база данныхBINARY(16)
это двоичное представление было сгенерировано в системе, следующей за RFC, и преобразование данных, находящихся в настоящее время в BINARY(16)
поле, в UNIQUEIDENTIFIER
результат приведет к тому, что GUID будет отличаться от того, который был изначально создан. Это на самом деле не создает проблемы, если значения никогда не покидают базу данных, а значения сравниваются только на равенство, а не на порядок.
Проблема с порядком заключается в том, что они не будут в том же порядке после преобразования в UNIQUEIDENTIFIER
. К счастью, если исходная система действительно была MySQL, то в двоичном представлении упорядочение никогда не выполнялось, так как MySQL имеет только строковое представление UUID .
Опасность использования строковых значений вне базы данных более серьезна, опять же, если двоичное представление было создано вне Windows / SQL Server. Поскольку порядок байтов потенциально различен, один и тот же GUID в строковой форме приведет к 2 различным двоичным представлениям, в зависимости от того, где произошло это преобразование. Если код приложения или клиенты получили GUID в виде строки как ABC
исходящий из двоичной формы 123
и двоичное представление было сгенерировано в системе, следующей за RFC, то это же двоичное представление (то есть 123
) будет преобразовано в строковую форму DEF
при преобразовании в а UNIQUEIDENTIFIER
. Аналогично, исходная строковая форма ABC
преобразуется в двоичную форму 456
при преобразовании в 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. Значение под строкой «Binary» совпадает со значением строки «Binary», но отформатировано в том же стиле, что и строка «String» (т. Е. Удалено «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, двоичное представление будет зеркально отображать строковое представление, потому что не будет никакого изменения порядка байтов.
Как данные были перенесены в 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
Что не так. И это оставляет нас с тремя вопросами:
Вы всегда можете быть обеспокоены. ;)
Возможно, система была перенесена из какой-то другой системы, которая не поддерживает uniqueidentifier. Есть ли другие компромиссы, о которых вы не знаете?
Разработчик может не знать о типе uniqueidentifier. О чем еще они не знали?
Технически, хотя - это не должно быть серьезной проблемой.