Sql server SQL Server GUID排序算法。为什么?
唯一标识符问题 我们有一个现有的数据库,它广泛使用UniqueIdentifier(不幸的是!)作为主键和一些表的一些可空列。我们遇到了这样一种情况:一些在这些表上运行的报表根据这些唯一标识符进行排序,因为表中没有其他列提供有意义的排序(这不是很讽刺!)。其目的是进行排序,以便按照项目插入的顺序显示项目,而不是使用Sql server SQL Server GUID排序算法。为什么?,sql-server,sql-server-2005,sql-server-2008,sorting,Sql Server,Sql Server 2005,Sql Server 2008,Sorting,唯一标识符问题 我们有一个现有的数据库,它广泛使用UniqueIdentifier(不幸的是!)作为主键和一些表的一些可空列。我们遇到了这样一种情况:一些在这些表上运行的报表根据这些唯一标识符进行排序,因为表中没有其他列提供有意义的排序(这不是很讽刺!)。其目的是进行排序,以便按照项目插入的顺序显示项目,而不是使用NewSequentialId()插入项目-因此浪费时间 关于排序算法的事实 无论如何,考虑到SQL Server基于字节组对唯一标识符进行排序,从结束的第5个字节组(6个字节)开始,
NewSequentialId()
插入项目-因此浪费时间
关于排序算法的事实
无论如何,考虑到SQL Server基于字节组对唯一标识符进行排序,从结束的第5个字节组(6个字节)开始,向第1个字节组(4个字节)移动,将第3个字节组(2个字节)的顺序从左到右颠倒
我的问题
我很想知道,在现实生活中,这种情况是否有帮助
SQL Server如何在内部存储uniqueidentifier,以提供有关
为什么它有这种古怪的排序算法
参考资料:
示例
在具有以下数据的uniqueidentifier列上使用Order By时,uniqueidentifier的排序如下所示
请注意,以下数据按升序排序,最高排序优先顺序是从第5字节组向第1字节组(向后)排序
代码:
Alberto的代码扩展为表示排序是在字节上,而不是在单个位上
With Test_UIDs As (-- 0 1 2 3 4 5 6 7 8 9 A B C D E F
Select ID = 1, UID = cast ('00000000-0000-0000-0000-100000000000' as uniqueidentifier)
Union Select ID = 2, UID = cast ('00000000-0000-0000-0000-010000000000' as uniqueidentifier)
Union Select ID = 3, UID = cast ('00000000-0000-0000-0000-001000000000' as uniqueidentifier)
Union Select ID = 4, UID = cast ('00000000-0000-0000-0000-000100000000' as uniqueidentifier)
Union Select ID = 5, UID = cast ('00000000-0000-0000-0000-000010000000' as uniqueidentifier)
Union Select ID = 6, UID = cast ('00000000-0000-0000-0000-000001000000' as uniqueidentifier)
Union Select ID = 7, UID = cast ('00000000-0000-0000-0000-000000100000' as uniqueidentifier)
Union Select ID = 8, UID = cast ('00000000-0000-0000-0000-000000010000' as uniqueidentifier)
Union Select ID = 9, UID = cast ('00000000-0000-0000-0000-000000001000' as uniqueidentifier)
Union Select ID = 10, UID = cast ('00000000-0000-0000-0000-000000000100' as uniqueidentifier)
Union Select ID = 11, UID = cast ('00000000-0000-0000-0000-000000000010' as uniqueidentifier)
Union Select ID = 12, UID = cast ('00000000-0000-0000-0000-000000000001' as uniqueidentifier)
Union Select ID = 13, UID = cast ('00000000-0000-0000-0001-000000000000' as uniqueidentifier)
Union Select ID = 14, UID = cast ('00000000-0000-0000-0010-000000000000' as uniqueidentifier)
Union Select ID = 15, UID = cast ('00000000-0000-0000-0100-000000000000' as uniqueidentifier)
Union Select ID = 16, UID = cast ('00000000-0000-0000-1000-000000000000' as uniqueidentifier)
Union Select ID = 17, UID = cast ('00000000-0000-0001-0000-000000000000' as uniqueidentifier)
Union Select ID = 18, UID = cast ('00000000-0000-0010-0000-000000000000' as uniqueidentifier)
Union Select ID = 19, UID = cast ('00000000-0000-0100-0000-000000000000' as uniqueidentifier)
Union Select ID = 20, UID = cast ('00000000-0000-1000-0000-000000000000' as uniqueidentifier)
Union Select ID = 21, UID = cast ('00000000-0001-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 22, UID = cast ('00000000-0010-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 23, UID = cast ('00000000-0100-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 24, UID = cast ('00000000-1000-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 25, UID = cast ('00000001-0000-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 26, UID = cast ('00000010-0000-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 27, UID = cast ('00000100-0000-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 28, UID = cast ('00001000-0000-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 29, UID = cast ('00010000-0000-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 30, UID = cast ('00100000-0000-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 31, UID = cast ('01000000-0000-0000-0000-000000000000' as uniqueidentifier)
Union Select ID = 32, UID = cast ('10000000-0000-0000-0000-000000000000' as uniqueidentifier)
)
Select * From Test_UIDs Order By UID, ID
该算法由SQL Server人员记录在这里:我在这里引用(因为这是一篇可能在几年内永远消失的老文章) 总的来说,平等性比较对我们来说很有意义 唯一标识符值。然而,如果你发现自己需要一般的 排序,那么您可能看到了错误的数据类型,应该 考虑不同的整数类型。 如果经过仔细考虑后,您决定订购唯一标识符 专栏,你可能会对你得到的东西感到惊讶 给定这两个唯一标识符值: @g1='55666BEE-B3A0-4BF5-81A7-86FF976E763F'@g2= “8DD5BCA5-6ABE-4F73-B4B7-393AE6BBB849” 许多人认为@g1比@g2小,因为“55666BEE”是 当然比“8DD5BCA5”小。然而,SQL Server并非如此 2005比较uniqueidentifier值 通过从右到左查看字节“组”进行比较,然后 字节“组”内从左到右。字节组是分隔的 用“-”字符。更严格地说,我们看字节{10到15} 首先是{8-9},然后是{6-7},然后是{4-5},最后是{0-3} 在这个具体示例中,我们将首先比较“86FF976E763F” 使用“393AE6BBB849”。我们马上看到@g2确实更大了 而不是g1 请注意,在.NET语言中,Guid值具有不同的默认排序 顺序比SQL Server中的顺序高。如果您发现需要订购阵列或 Guid列表使用SQL Server比较语义,可以使用 改为SqlGuid的数组或列表,它在 与SQL Server语义一致的方式 另外,排序遵循字节组endianness(请参见此处:)。组10-15和组8-9存储为big-endian(对应于wikipedia文章中的Data4),因此它们作为big-endian进行比较。其他组采用小端法进行比较。坏死术
为那些发现接受的答案有点模糊的人提供的特殊服务。
代码本身就说明了问题,神奇的部分是
System.Guid g
g.ToByteArray();
int[] m_byteOrder = new int[16] // 16 Bytes = 128 Bit
{10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3};
public int Compare(Guid x, Guid y)
{
byte byte1, byte2;
//Swap to the correct order to be compared
for (int i = 0; i < NUM_BYTES_IN_GUID; i++)
{
byte1 = x.ToByteArray()[m_byteOrder[i]];
byte2 = y.ToByteArray()[m_byteOrder[i]];
if (byte1 != byte2)
return (byte1 < byte2) ? (int)EComparison.LT : (int)EComparison.GT;
} // Next i
return (int)EComparison.EQ;
}
System.Guid g
g、 ToByteArray();
int[]m_byteOrder=new int[16]//16字节=128位
{10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3};
公共整数比较(Guid x,Guid y)
{
字节字节1,字节2;
//交换到要比较的正确顺序
for(int i=0;i
完整代码:
namespace BlueMine.Data
{
public class SqlGuid
: System.IComparable
, System.IComparable<SqlGuid>
, System.Collections.Generic.IComparer<SqlGuid>
, System.IEquatable<SqlGuid>
{
private const int NUM_BYTES_IN_GUID = 16;
// Comparison orders.
private static readonly int[] m_byteOrder = new int[16] // 16 Bytes = 128 Bit
{10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3};
private byte[] m_bytes; // the SqlGuid is null if m_value is null
public SqlGuid(byte[] guidBytes)
{
if (guidBytes == null || guidBytes.Length != NUM_BYTES_IN_GUID)
throw new System.ArgumentException("Invalid array size");
m_bytes = new byte[NUM_BYTES_IN_GUID];
guidBytes.CopyTo(m_bytes, 0);
}
public SqlGuid(System.Guid g)
{
m_bytes = g.ToByteArray();
}
public byte[] ToByteArray()
{
byte[] ret = new byte[NUM_BYTES_IN_GUID];
m_bytes.CopyTo(ret, 0);
return ret;
}
int CompareTo(object obj)
{
if (obj == null)
return 1; // https://msdn.microsoft.com/en-us/library/system.icomparable.compareto(v=vs.110).aspx
System.Type t = obj.GetType();
if (object.ReferenceEquals(t, typeof(System.DBNull)))
return 1;
if (object.ReferenceEquals(t, typeof(SqlGuid)))
{
SqlGuid ui = (SqlGuid)obj;
return this.Compare(this, ui);
} // End if (object.ReferenceEquals(t, typeof(UInt128)))
return 1;
} // End Function CompareTo(object obj)
int System.IComparable.CompareTo(object obj)
{
return this.CompareTo(obj);
}
int CompareTo(SqlGuid other)
{
return this.Compare(this, other);
}
int System.IComparable<SqlGuid>.CompareTo(SqlGuid other)
{
return this.Compare(this, other);
}
enum EComparison : int
{
LT = -1, // itemA precedes itemB in the sort order.
EQ = 0, // itemA occurs in the same position as itemB in the sort order.
GT = 1 // itemA follows itemB in the sort order.
}
public int Compare(SqlGuid x, SqlGuid y)
{
byte byte1, byte2;
//Swap to the correct order to be compared
for (int i = 0; i < NUM_BYTES_IN_GUID; i++)
{
byte1 = x.m_bytes[m_byteOrder[i]];
byte2 = y.m_bytes[m_byteOrder[i]];
if (byte1 != byte2)
return (byte1 < byte2) ? (int)EComparison.LT : (int)EComparison.GT;
} // Next i
return (int)EComparison.EQ;
}
int System.Collections.Generic.IComparer<SqlGuid>.Compare(SqlGuid x, SqlGuid y)
{
return this.Compare(x, y);
}
public bool Equals(SqlGuid other)
{
return Compare(this, other) == 0;
}
bool System.IEquatable<SqlGuid>.Equals(SqlGuid other)
{
return this.Equals(other);
}
}
}
namespace BlueMine.Data
{
公共类SqlGuid
:System.i可比较
,System.i可比较
,System.Collections.Generic.IComparer
,System.IEquatable
{
私有常量int NUM_BYTES_IN_GUID=16;
//比较顺序。
私有静态只读int[]m_byteOrder=new int[16]//16字节=128位
{10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3};
private byte[]m_bytes;//如果m_值为null,则SqlGuid为null
公共SqlGuid(字节[]guidBytes)
{
if(guidBytes==null | | guidBytes.Length!=NUM_BYTES_IN_GUID)
抛出新System.ArgumentException(“无效数组大小”);
m_bytes=新字节[NUM_bytes_IN_GUID];
CopyTo(m_字节,0);
}
公共SqlGuid(System.Guid g)
{
m_bytes=g.ToByteArray();
}
公共字节[]ToByteArray()
{
byte[]ret=新字节[NUM_BYTES_IN_GUID];
m_字节.CopyTo(ret,0);
返回ret;
}
int CompareTo(对象对象对象)
{
if(obj==null)
返回1;//https://msdn.microsoft.com/en-us/library/system.icomparable.compareto(v=vs.110).aspx
System.Type t=obj.GetType();
if(object.ReferenceEquals(t,typeof(System.DBNull)))
返回1;
if(object.ReferenceEquals(t,typeof(SqlGuid)))
{
SqlGuid ui=(SqlGuid)obj;
返回this.Compare(this,ui);
}//结束if(object.ReferenceEquals(t,typeof(UInt128)))
返回1;
} //
namespace BlueMine.Data
{
public class SqlGuid
: System.IComparable
, System.IComparable<SqlGuid>
, System.Collections.Generic.IComparer<SqlGuid>
, System.IEquatable<SqlGuid>
{
private const int NUM_BYTES_IN_GUID = 16;
// Comparison orders.
private static readonly int[] m_byteOrder = new int[16] // 16 Bytes = 128 Bit
{10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3};
private byte[] m_bytes; // the SqlGuid is null if m_value is null
public SqlGuid(byte[] guidBytes)
{
if (guidBytes == null || guidBytes.Length != NUM_BYTES_IN_GUID)
throw new System.ArgumentException("Invalid array size");
m_bytes = new byte[NUM_BYTES_IN_GUID];
guidBytes.CopyTo(m_bytes, 0);
}
public SqlGuid(System.Guid g)
{
m_bytes = g.ToByteArray();
}
public byte[] ToByteArray()
{
byte[] ret = new byte[NUM_BYTES_IN_GUID];
m_bytes.CopyTo(ret, 0);
return ret;
}
int CompareTo(object obj)
{
if (obj == null)
return 1; // https://msdn.microsoft.com/en-us/library/system.icomparable.compareto(v=vs.110).aspx
System.Type t = obj.GetType();
if (object.ReferenceEquals(t, typeof(System.DBNull)))
return 1;
if (object.ReferenceEquals(t, typeof(SqlGuid)))
{
SqlGuid ui = (SqlGuid)obj;
return this.Compare(this, ui);
} // End if (object.ReferenceEquals(t, typeof(UInt128)))
return 1;
} // End Function CompareTo(object obj)
int System.IComparable.CompareTo(object obj)
{
return this.CompareTo(obj);
}
int CompareTo(SqlGuid other)
{
return this.Compare(this, other);
}
int System.IComparable<SqlGuid>.CompareTo(SqlGuid other)
{
return this.Compare(this, other);
}
enum EComparison : int
{
LT = -1, // itemA precedes itemB in the sort order.
EQ = 0, // itemA occurs in the same position as itemB in the sort order.
GT = 1 // itemA follows itemB in the sort order.
}
public int Compare(SqlGuid x, SqlGuid y)
{
byte byte1, byte2;
//Swap to the correct order to be compared
for (int i = 0; i < NUM_BYTES_IN_GUID; i++)
{
byte1 = x.m_bytes[m_byteOrder[i]];
byte2 = y.m_bytes[m_byteOrder[i]];
if (byte1 != byte2)
return (byte1 < byte2) ? (int)EComparison.LT : (int)EComparison.GT;
} // Next i
return (int)EComparison.EQ;
}
int System.Collections.Generic.IComparer<SqlGuid>.Compare(SqlGuid x, SqlGuid y)
{
return this.Compare(x, y);
}
public bool Equals(SqlGuid other)
{
return Compare(this, other) == 0;
}
bool System.IEquatable<SqlGuid>.Equals(SqlGuid other)
{
return this.Equals(other);
}
}
}
function guidForComparison(guid) {
/*
character positions:
11111111112222222222333333
012345678901234567890123456789012345
00000000-0000-0000-0000-000000000000
byte positions:
111111111111
00112233 4455 6677 8899 001122334455
*/
return guid.substr(24, 12) +
guid.substr(19, 4) +
guid.substr(16, 2) +
guid.substr(14, 2) +
guid.substr(11, 2) +
guid.substr(9, 2) +
guid.substr(6, 2) +
guid.substr(4, 2) +
guid.substr(2, 2) +
guid.substr(0, 2);
};