使用 BINARY(16) 而不是 UNIQUEIDENTIFIER 会受到惩罚吗?

Jon*_*len 19 sql-server migration uuid

我最近继承了一个使用BINARY(16)而不是UNIQUEIDENTIFIER存储 Guid的 SQL Server 数据库。它对包括主键在内的所有内容都执行此操作。

我应该担心吗?

Sol*_*zky 23

我应该担心吗?

嗯,这里有几件事有点令人担忧。

第一:虽然 a UNIQUEIDENTIFIER(ie Guid) 是一个 16 字节的二进制值是正确的,但它也是正确的:

  1. 所有数据都可以以二进制形式存储(例如INT可以存储在 中BINARY(4)DATETIME可以存储在BINARY(8)中等),因此 #2 ?
  2. 出于方便(例如sysname作为 的别名NVARCHAR(128)),可能有一个单独的 GUID 数据类型的原因。

我能找到的三个行为差异是:

  • 比较UNIQUEIDENTIFIERSQL Server 中的值,无论好坏,实际上与比较BINARY(16)值的方式不同。根据比较 GUID 和 uniqueidentifier 值的 MSDN 页面,在比较UNIQUEIDENTIFIERSQL Server 中的时:

    值的最后六个字节是最重要的

  • 虽然这些值不经常排序,但这两种类型之间存在细微差别。根据uniqueidentifier的 MSDN 页面:

    排序不是通过比较两个值的位模式来实现的。

  • 鉴于在 SQL Server 和 .NET 之间处理 GUID 值的方式存在差异(在上面链接的“比较 GUID 和 uniqueidentifier 值”页面中注明),将这些数据从 SQL Server 中提取到应用程序代码中可能无法正确处理如果需要模拟 SQL Server 比较行为,请使用应用程序代码。这种行为可以通过转换为 a 来模拟SqlGuid,但开发人员知道这样做吗?

第二:基于以下陈述

它对包括主键在内的所有内容都执行此操作。

我通常会通过使用 GUID 作为 PK 而不是替代键以及使用PKINT甚至BIGINT作为 PK来关注系统性能。如果这些 GUID PK 是聚集索引,则更令人担忧。

更新

OP 对@Rob 的回答发表的以下评论引起了额外的关注:

它是从我认为 MySQL 迁移的

GUID 可以以2 种不同的二进制格式存储。因此,可能会引起关注,具体取决于:

  1. 二进制表示是在什么系统上生成的,以及
  2. 如果字符串值在原始系统之外使用,例如在应用程序代码中或提供给客户端以在导入文件中使用等。

生成二进制表示的问题与 4 个“字段”中的前 3 个的字节顺序有关。如果您按照上面的链接访问 Wikipedia 文章,您将看到 RFC 4122 指定对所有 4 个字段使用“Big Endian”编码,但 Microsoft GUID 指定使用“Native”Endianness。嗯,Intel 架构是 Little Endian,因此前 3 个字段的字节顺序与遵循 RFC 的系统相反(以及在 Big Endian 系统上生成的 Microsoft 风格的 GUID)。第一个字段“数据 1”是 4 个字节。在一种字节顺序中,它将表示为 (假设) 0x01020304。但在另一个字节序中它会是0x04030201。所以如果当前数据库'BINARY(16)该二进制表示是在遵循 RFC 的系统上生成的,然后将当前在该BINARY(16)字段中的数据转换为 aUNIQUEIDENTIFIER将导致与最初创建的 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?在应用程序代码中生成?

更新 2

如果之前对与导入在另一个系统上生成的 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
Run Code Online (Sandbox Code Playgroud)

在上面显示的输出中,“String”和“Binary”值来自相同的 GUID。“二进制”行下方的值与“二进制”行的值相同,但格式与“字符串”行的样式相同(即删除了“0x”并添加了四个破折号)。比较第一个和第三个值,它们并不完全相同,但它们非常接近:最右侧的两个部分相同,但最左侧的三个部分不同。但是如果仔细观察,您会发现三个部分中的每个部分都是相同的字节,只是顺序不同。如果我只显示前三个部分,并给字节编号可能更容易看出它们的顺序在两种表示之间有何不同:

字符串 = 1 5F 2 ED 3 23 4 BE 5 E5 6 2C 7 40 8 EE
二进制 = 4 BE 3 23 2 ED 1 5F 6 2C 5 E5 8 EE 7 40(在 Windows / SQL Server 中)

因此,在每个分组中,字节的顺序是相反的,但仅限于 Windows 和 SQL Server。但是,在遵循 RFC 的系统上,二进制表示将反映 sting 表示,因为字节顺序不会发生任何逆转。

数据是如何从 MySQL 带入 SQL Server 的?这里有几个选择:

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

返回:

0x35464544323342452D453532432D3430  
0x5FED23BEE52C40EE8F4549664C9472FD  
0xBE23ED5F2CE5EE408F4549664C9472FD
Run Code Online (Sandbox Code Playgroud)

假设它是直接的二进制到二进制(即上面的 Convert #2),那么生成的 GUID,如果转换为实际的UNIQUEIDENTIFIER,将是:

SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);
Run Code Online (Sandbox Code Playgroud)

返回:

BE23ED5F-2CE5-EE40-8F45-49664C9472FD
Run Code Online (Sandbox Code Playgroud)

这是错误的。这给我们留下了三个问题:

  1. 数据是如何导入 SQL Server 的?
  2. 应用程序代码是用什么语言编写的?
  3. 应用程序代码在什么平台上运行?

  • @JonathanAllen 我添加了另一个更新部分,希望能更好地解释。不,它们之间的索引不应该有任何不同。 (2认同)

Rob*_*ley 5

你总是可以担心。;)

该系统可能是从不支持唯一标识符的其他系统迁移而来的。还有其他你不知道的妥协吗?

设计者可能不知道 uniqueidentifier 类型。还有哪些事情是他们不知道的?

虽然从技术上讲 - 这不应该是一个主要问题。