数据库:如何对GUID进行排序?

8

我的主键使用GUID。

如何对GUID进行排序?

如果我创建一个日期时间列并记录日期时间戳,我可以按日期时间排序吗?这是最好的方法吗?还是有更好的方法?


阅读这篇文章:http://sqlblog.com/blogs/alberto_ferrari/archive/2007/08/31/how-are-guids-sorted-by-sql-server.aspx,了解GUID在SQL Server中是如何排序的。我不明白为什么需要这种类型的排序,因为GUID值没有任何意义 :) 关于日期时间排序 - 我无法理解问题所在:是的,即使您使用时间戳,也可以按日期时间排序。 - Alex_L
你想要实现什么目标?我们只知道你尝试了什么,但不知道你想要实现什么,所以很难建议如何做。 - Rune FS
5个回答

7
SELECT * 
FROM myTable
ORDER BY CAST(myGuid AS VARCHAR(36))

如果 GUID 数据类型已经是 VARCHAR,(理论上应该是),希望不必进行 CAST - Shabar

1
一个Guid就是它的名字所暗示的,一个唯一标识符。身份并不意味着顺序,它只是给你一种确定两个事物是否应该相同的方法。为了排序,你需要确定什么意味着比其他东西更大或更小。从你的问题来看,似乎排序应该基于创建时间;Guids对此没有帮助。

1

死灵法术。
GUID只是随机数,它们没有顺序性(除非你使用sequentialuid - 但一旦计算机重新启动,它就会重新开始,所以它几乎没有意义)。
这就是GUID实际排序的方式:
代码说明了一切,神奇的部分在于:

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;
}

完整代码:

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);
        }


    }


}

1
Guid不是随机数,它们是唯一的数字。这是你唯一可以依赖的。 - Enigmativity
1
@Enigmativity:不正确。GUID是随机数,严格来说它们并不是唯一的。但是,如果数据集相对于2^128足够小,则碰撞的概率是微不足道的。然而,GUID是128位的,因此最大可能的数字是2^128-如果您有2^128+1个条目,则碰撞的概率为100%。GUID曾经使用MAC地址和系统时间。但由于这样,攻击者可以从GUID中推断出服务器的MAC地址。这是极其不安全的,这就是为什么它被更改为完全随机的数字的原因。 - Stefan Steiger
实际上,GUID比随机数更糟糕,因为它将4位硬编码为“4”——表示uuid v4——并且使用2或3位来指示变体(分别用于变体1和2的10或110)。因此,对于变体1(即大多数UUID),随机版本4 UUID将具有6个预定的变体和版本位,留下122位用于随机生成部分,总共有2^122,即5.3x10E36(5.3不可思议)个可能的版本4变体1 UUID。因此,在GUID中,碰撞概率为2^-122,而在随机数中,它将是2^-128。因此,随机数比GUID/uuid-v4更加独特。 - Stefan Steiger
2
@Enigmativity:由SQL服务器(或System.Guid.NewGuid())发出的GUID-v4不能保证唯一性,只能保证随机性 - 这是您所依赖的全部。它甚至不是加密随机的 - 它只是“可预测”的随机数。要创建一个加密随机的Guid,请参见https://dev59.com/p1oU5IYBdhLWcg3wu4ni。 - Stefan Steiger

0
我会选择设置为自增的 int(或 bigint)列。每次插入一行时,标识将递增。您可以按此列排序以按插入顺序获取行。

0
你想做什么?按插入日期排序吗?为此,确实需要一个日期时间(或其变体)字段,因为GUID和自增键都不能保证顺序,只能保证唯一性。
阅读此内容以获取更多信息:主键排序

自动递增字段本身不能保证顺序,但是当与ORDER BY语句结合使用时,它们应该能够保证顺序。这里有什么我漏掉的吗? - Bradley Uffner
@BradleyUffner - 请考虑以下情况:导出数据、事务和链接服务器。所有情况都不能保证主键代表插入日期。 - Polity
有道理...不过,根据任务的复杂性、大小和环境,这仍然是一种有效的处理方式。如果没有更多提问者的具体信息,很难确定是否适当。 - Bradley Uffner
@BradleyUffner - 我同意,但这仍然是一种不好的做法,只有在有充分理由时才应该使用它(例如性能方面)。由于提问者似乎不是SQL专家,我不太愿意教授不良实践 :) - Polity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接