冷知識 - Guid 如何排序?
0 |
研究 SQL NEWSEQUENTIALID() 時引發一個疑問,Guid 在 .NET、SQL 的排序方式是否相同?是依據什麼規則決定大小?
答案是 .NET 與 SQL Server 排序規則不同。
例如隨機產生三個 Guid,在 .NET 的排序為
- 87e4077c-a7b7-48d5-b155-79dbdc09c398
- 391ff00e-c526-40ec-a87a-0301837cfa4b
- 63b8a83c-b580-4808-8858-6b4cb89eee88
在 SQL Server 則是
- 391ff00e-c526-40ec-a87a-0301837cfa4b
- 63b8a83c-b580-4808-8858-6b4cb89eee88
- 87e4077c-a7b7-48d5-b155-79dbdc09c398
好奇心起,想搞清楚這個順序是怎麼排出來的。
先看 .NET。
在 Github 可以找到 .NET Guid 的原始碼,很快得知 CompareTo() 邏輯如下:
////////////////////////////////////////////////////////////////////////////////
// Member variables
////////////////////////////////////////////////////////////////////////////////
private int _a;
private short _b;
private short _c;
private byte _d;
private byte _e;
private byte _f;
private byte _g;
private byte _h;
private byte _i;
private byte _j;
private byte _k;
public int CompareTo(Guid value)
{
if (value._a!=this._a) {
return GetResult((uint)this._a, (uint)value._a);
}
if (value._b!=this._b) {
return GetResult((uint)this._b, (uint)value._b);
}
if (value._c!=this._c) {
return GetResult((uint)this._c, (uint)value._c);
}
if (value._d!=this._d) {
return GetResult((uint)this._d, (uint)value._d);
}
if (value._e!=this._e) {
return GetResult((uint)this._e, (uint)value._e);
}
if (value._f!=this._f) {
return GetResult((uint)this._f, (uint)value._f);
}
if (value._g!=this._g) {
return GetResult((uint)this._g, (uint)value._g);
}
if (value._h!=this._h) {
return GetResult((uint)this._h, (uint)value._h);
}
if (value._i!=this._i) {
return GetResult((uint)this._i, (uint)value._i);
}
if (value._j!=this._j) {
return GetResult((uint)this._j, (uint)value._j);
}
if (value._k!=this._k) {
return GetResult((uint)this._k, (uint)value._k);
}
return 0;
}
翻譯一下,.NET 把 GUID 的 16 個 Bytes 拆解成 一個 int _a (4 Bytes)、兩個 short _b (2 Bytes) 及 _c (2 Bytes),再加上 8 個 byte _d 到 _k (8 Bytes),對映成 GUID 格式就是 aaaaaaaa-bbbb-cccc-ddee-ffgghhiijjkk,比對時先比 _a,再比 _b,再比 _c,一直比到 _k。
SQL 的比對邏輯則不同,我在 stackoverflow 找到這篇 SQL Uniqueidentifier 排序規則討論,依據 SQL Server RD,SQL Server 排序 Uniqueidentifier 的依據是先比 10-15 Bytes,再比 8-9 Bytes,6-7 Bytes、4-5 Bytes、0-3 Bytes,若對映到 .NET,是將 _f ~ _k 6 Bytes 當成一個數字、_d _e (當成 short)、_c (short)、_b (short)、_a (int) 逐一比序。
來驗證一下,.NET 排序為
- 87e4077c-a7b7-48d5-b155-79dbdc09c398
- 391ff00e-c526-40ec-a87a-0301837cfa4b
- 63b8a83c-b580-4808-8858-6b4cb89eee88
比較 _a 分別為 0x87e4077c = -2015099012 (注意:int 為有號數,超過 0x8... 時為負數) < 0x391ff00e < 0x63b8a83c,依此決定排序。
SQL 排序為
- 391ff00e-c526-40ec-a87a-0301837cfa4b
- 63b8a83c-b580-4808-8858-6b4cb89eee88
- 87e4077c-a7b7-48d5-b155-79dbdc09c398
先看最後一節,0301837cfa4b < 6b4cb89eee88 < 79dbdc09c398 (最後 6 Bytes 直接比對 byte[] 轉 16 進位字串),排完收工。
原則蠻簡單的,以後遇到 Guid 排序就不會一頭霧水了。(其實不知道也沒差)
This article explains how .NET and MSSQL sort GUID.
Comments
Be the first to post a comment