Answers:
我应该担心吗?
好吧,这里有些事情有点令人担忧。
首先:虽然UNIQUEIDENTIFIER
(Guid
是)是16字节的二进制值,但也确实是:
INT
,可以存储在中BINARY(4)
,DATETIME
可以存储在中BINARY(8)
,等等),因此#2↴sysname
作为的别名NVARCHAR(128)
)。我可以发现三种行为差异:
不管UNIQUEIDENTIFIER
是好是坏,比较SQL Server中的值实际上与比较BINARY(16)
值的方法不同。根据用于比较GUID和uniqueidentifier值的MSDN页面,UNIQUEIDENTIFIER
在SQL Server中比较值时:
值的最后六个字节为最高有效
虽然这些值不经常排序,但是这两种类型之间存在细微差别。根据MSDN页面上的uniqueidentifier:
通过比较两个值的位模式无法实现排序。
鉴于在SQL Server和.NET之间处理GUID值的方式有所不同(在上面的“比较GUID和uniqueidentifier值”页面中有注释),因此在SQL Server中将这些数据从SQL Server提取到应用程序代码中可能无法正确处理。应用程序代码(如果需要模拟SQL Server比较行为)。可以通过转换为来模拟该行为SqlGuid
,但是开发人员会知道这样做吗?
第二:基于以下陈述
它对包括主键在内的所有内容执行此操作。
通常,我会通过将GUID用作PK而不是替代键以及同时使用an INT
或什BIGINT
至PK 来关注系统性能。甚至更担心这些GUID PK是否为聚簇索引。
OP对@Rob的回答提出的以下评论引起了另外一个关注:
它是从我认为MySQL迁移过来的
GUID可以2种不同的二进制格式存储。因此,可能会引起关注,具体取决于:
生成二进制表示形式的问题与4个“字段”中前3个字节的字节顺序有关。如果您单击上面指向Wikipedia文章的链接,您将看到RFC 4122指定对所有4个字段使用“大尾数”编码,而Microsoft GUID指定使用“本机”尾数。嗯,Intel体系结构是Little Endian,因此前3个字段的字节顺序与遵循RFC(以及在Big Endian系统上生成的Microsoft样式的GUID)的系统相反。第一个字段“数据1”为4个字节。在一个字节序中,它将(假设地)表示为0x01020304
。但是在另一种Endianness中将会是0x04030201
。所以如果当前数据库BINARY(16)
该二进制表示是在遵循RFC的系统上生成的,然后将该BINARY(16)
字段中当前的数据转换为,UNIQUEIDENTIFIER
将导致GUID与最初创建的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提供给客户或在应用程序代码中,则可以通过获取一个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
二进制= 4 BE 3 23 2 ED 1 5F – 6 2C 5 E5 – 8 EE 7 40(在Windows / SQL Server中)
因此,在每个分组中,字节的顺序是相反的,但仅在Windows和SQL Server中是相反的。但是,在遵循RFC的系统上,二进制表示形式将镜像字符串表示形式,因为字节顺序不会发生任何反转。
如何将数据从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'));
返回值:
0x35464544323342452D453532432D3430
0x5FED23BEE52C40EE8F4549664C9472FD
0xBE23ED5F2CE5EE408F4549664C9472FD
假设它是直接二进制到二进制的(即上面的Convert#2),那么如果将GUID转换为实际的UNIQUEIDENTIFIER
,则将是:
SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);
返回值:
BE23ED5F-2CE5-EE40-8F45-49664C9472FD
哪有错 这给我们留下了三个问题: