【问题标题】:Database: how do I sort GUID?数据库:如何对 GUID 进行排序?
【发布时间】:2011-12-10 14:07:39
【问题描述】:

我的主键使用 guid。

如何对 GUID 进行排序?

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

【问题讨论】:

  • 阅读这篇关于 GUID 排序的文章sqlblog.com/blogs/alberto_ferrari/archive/2007/08/31/…。我不明白你为什么需要这种类型的排序,因为 guid 值对日期时间排序没有任何意义:) - 我无法理解问题:是的,即使你使用时间戳,你也可以按日期时间排序。
  • 您希望完成什么?当我们只了解您的尝试但不知道您尝试完成什么时,很难建议如何去做

标签: c# sql-server database sql-server-2008 database-design


【解决方案1】:
SELECT * 
FROM myTable
ORDER BY CAST(myGuid AS VARCHAR(36))

【讨论】:

  • 如果GUID 数据类型已经是VARCHAR ,(理论上应该是)希望不必CAST
【解决方案2】:

Guid 顾名思义,是一个唯一标识符。身份并不意味着顺序,它只是为您提供了一种确定两件事是否应该相同的方法。为了进行排序,您需要确定比其他事物更大或更小意味着什么。从您的问题来看,排序似乎应该基于创建时间;指南不会帮助您。

【讨论】:

    【解决方案3】:

    死灵术。
    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);
            }
    
    
        }
    
    
    }
    

    【讨论】:

    • Guids 不是随机数 - 它们是唯一的数字。这就是您可以依靠的全部。
    • @Enigmativity:不正确。 GUID 是随机数,严格来说,它们不是唯一的。但是,给定相对于 2^128 足够小的数据集,碰撞的概率非常小。但是,GUID 是 128 位的,因此可能的最大数字是 2^128 - 如果您有 2^128+1 个条目,那么冲突的概率是 100%。 GUID 过去有 MAC 地址和系统时间。但正因为如此,攻击者可以从 GUID 推断服务器的 MAC 地址。这是非常不安全的,这就是为什么它被改为完全随机数。
    • 实际上,guid 比随机数更糟糕,因为它将 4 位硬编码为 '4' - 表示 uuid v4 - 以及 2 或 3 位来指示变体(10 或 110 表示分别为变体 1 和 2)。因此,对于变体 1(即大多数 UUID),随机版本 4 UUID 将具有 6 个预定变体和版本位,为随机生成的部分留下 122 位,总共 2^122 或 5.3x10E36(5.3 undecillion ) 可能的版本 4 变体 1 UUID。因此,在 guid 中,碰撞概率为 2^-122,而对于随机数,则为 2^-128。因此,随机数比 GUID/uuid-v4 更独特。
    • @Enigmativity:由 SQL 服务器(或 System.Guid.NewGuid())发布的 GUID-v4 不保证是唯一的,它只是保证是随机的 - 这就是你所能做的依靠。它甚至也不是加密随机的——它只是“可预测的”随机。有关加密随机 Guid,请参阅 stackoverflow.com/questions/37170388/…
    【解决方案4】:

    我会使用一个 int(或 bigint)列设置为标识。每次插入一行时,标识都会增加。您可以对此列进行排序以按插入顺序获取行。

    【讨论】:

      【解决方案5】:

      你想做什么?按插入日期排序?为此,您确实需要一个日期时间(或其变体之一)字段,因为 guid 和自动增量键永远不能保证顺序,只能保证唯一性

      阅读本文了解更多信息:Primary Key Sorting

      【讨论】:

      • Auto Increment 字段本身并不能保证顺序,但是当与 ORDER BY 语句结合使用时,它们应该。我在这里缺少什么吗?
      • @BradleyUffner - 考虑以下情况:导出数据、事务和链接服务器。所有情况都不能保证主键对插入日期具有代表性。
      • 有道理...尽管取决于任务的复杂性、大小和环境,它仍然可能是处理此问题的有效方法。如果没有来自提问者的更多细节,很难判断它是否合适。
      • @BradleyUffner - 同意,但它仍然是一个不好的做法,只有在有充分理由的情况下才应该使用它。 (性能?),因为作为 SQL 专家,这个问题并没有让我印象深刻,所以我不太热衷于教授不良做法 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-22
      • 2013-12-06
      • 1970-01-01
      • 2019-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多