2015-01-16 115 views
4

.NET有两种GUID数据类型:Guid,它以“自然”方式排序GUID,SqlGuid,它认为最后一个短划线之后的六个字节是最重要的。这个差异在这里详细解释:MSDN: Comparing GUID and uniqueidentifier ValuesGUID比较奇怪

然而,在这两种情况下,下面应持有(假设所有... s为相等):

57d0affe-... < 57d0afff-... < 57d0b000-... 

如果是这样的话,为什么我得到下面的输出(见注释)?

using System; 
using System.Data.SqlTypes; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var g1 = new SqlGuid("57d0affe-9d9d-11e4-bec2-e840f2ad1632"); 
     var g2 = new SqlGuid("57d0afff-9d9d-11e4-bec2-e840f2ad1632"); 
     var g3 = new SqlGuid("57d0b000-9d9d-11e4-bec2-e840f2ad1632"); 

     Console.WriteLine(g1 < g2); // prints True 
     Console.WriteLine(g2 < g3); // prints False <- ? 
     Console.ReadLine(); 
    } 
} 

据我所知,g2 < g3也应该产生True。我误解了一些东西,或者这是框架中的一些错误吗?如使用普通Guid而不是SqlGuid,则输出为预期的两倍True

+1

你为什么要摆在首位比较GUID值?我不确定是否有订购GUID的标准。如果您希望可订列使用整数或GUID以外的其他值。 –

+0

@DStanley:在SQL Server中使用GUID作为主键(或聚簇索引)时,排序*很重要(性能方面)。这就是为什么像'newsequentialid()'这样的东西存在。 – Heinzi

+0

如果您需要更多上下文:我们使用Win32 API函数UuidCreateSequential创建将来插入数据库的记录的ID。我们的.NET包装到UuidCreateSequential的单元测试开始出现故障,这让我意识到了这个问题。 – Heinzi

回答

2

实际上,比较逐字节地进行。所以你要问的第一件事是“0xff小于0x00?”显然,它不是:)

这当然是为什么SQL GUID表示法是“怪异” - 它是“两次倒置”。我不知道为什么这决定在MS SQL中是这样的uniqueidentifier(我认为它允许更好的哈希或什么),但SqlGuid必须具有相同的行为,所以它只是得到整个byte[]并去了一个字节后另一个。第一个字节是第一组的最后一个字节,第二个字节是下一个到最后第一组等

编辑

要添加更多的信息,微软的标准GUID结构被定义为这样的:

typedef struct _GUID { 
    DWORD Data1; 
    WORD Data2; 
    WORD Data3; 
    BYTE Data4[8]; 
} GUID; 

当使用逐字节的比较,前三组获得本机字节序,而最后一个总是大端。

这是SQL服务器中使用的GUID,也是SqlGuid模拟的GUID。本地.NET System.Guid在它的CompareTo方法中做了同样的事情。

RFCC 4122 GUID实际上在big-endian计算机上显示相同的行为,唯一的区别是它对所有组都是高端的。据我所知,它没有被描述为有任何设计的排序。

比较任何东西,但平等的GUID没有多大意义。如果你需要这样做,那么就有一些特殊的GUID(比如顺序或基于时间的GUID),它们给你一个更合理的值分配,同时仍然是合理独特的。

由于它没有被定义,它并没有太多的意义,比较逐字节或字符逐字符是最明显的两个 - 人类会希望逐字符,因为这是我们如何看待价值,但对于计算机而言,逐字节更合理。但更合理的是逐场 - 这就是发生在那里的事情。

+0

所以你声称在SqlGuid/uniqueidentifier中反转* first *组件的字节顺序,而不是Guid/WinApi? – Heinzi

+2

@Heinzi其实不只是第一个。 Microsoft标准的GUID具有本地排序的前三组(即x86上的小端),只有最后一组是大端。 RFC标准的GUID将所有组作为big-endian。原因基本上是Microsoft GUID结构实际上定义为'DWORD,WORD,WORD,BYTE [8]',所以它的字节顺序部分来自计算机本地字节顺序。 – Luaan

+0

我明白了。如果你使用这些信息来改善你的答案(SqlGuid/uniqueidentifier与Microsoft GUID有什么不同,以及它对排序有什么影响 - 我猜都是“逐字节比较”,所以这不是真正的区别),我会很乐意接受它。 – Heinzi

2

比较有点复杂。

Firstofall有定义一些命令:

private static readonly int[] x_rgiGuidOrder = new int[16] 
    {10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3}; 

再有就是这种方法

private static EComparison Compare(SqlGuid x, SqlGuid y) { 
     //Swap to the correct order to be compared 
     for (int i = 0; i < SizeOfGuid; i++) { 
      byte b1, b2; 

      b1 = x.m_value [x_rgiGuidOrder[i]]; 
      b2 = y.m_value [x_rgiGuidOrder[i]]; 
      if (b1 != b2) 
       return(b1 < b2) ? EComparison.LT : EComparison.GT; 
     } 
     return EComparison.EQ; 
    } 

然而,这并不是故事的全部,真正的原因是从字符串构造函数:

public SqlGuid(String s) { 
     m_value = (new Guid(s)).ToByteArray(); 
    } 

它创建一个新的GUID,然后使用它的字节表示。

,这给了我们后面的字节值:

g2 : 255 175 208 87 157 157 228 17 190 194 232 64 242 173 22 50 
g3 : 0 176 208 87 157 157 228 17 190 194 232 64 242 173 22 50 

还有,我们可以看到,255是大于0,而不是反过来。

你可以找到完整的源here 小提琴如何得到字节表示是here

+0

这是如何解释输出? “af”(g1中的位置12)小于“b0”(g2中的位置12)。 – Heinzi

+0

是的,我刚刚看到。它没有。但这可能会增加理解发生了什么。 – CSharpie

+0

@Heinzi现在它解释。 – CSharpie