2015-01-16 113 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