8

My primary key uses guid.

How do I sort GUID?

What about I create a datetime column and record a datetime stamp, I could then sort by datetime? is this the best way to do it? or are there better ways?

001
  • 62,807
  • 94
  • 230
  • 350
  • What do you wish to accomplish? It's hard to suggest how to do it when we only have your thoughts on how you've tried but not know what you've tried to accomplish – Rune FS Oct 19 '11 at 04:57
  • read this artice http://sqlblog.com/blogs/alberto_ferrari/archive/2007/08/31/how-are-guids-sorted-by-sql-server.aspx about sorting of GUIDs. I cannot see why you need this type of sorting, because the guid value means nothing :) about datetime sorting - I cannot understand the problem: yes, you can sort by datetime even you use timestamps. – Alex_L Oct 19 '11 at 02:58

5 Answers5

7
SELECT * 
FROM myTable
ORDER BY CAST(myGuid AS VARCHAR(36))
josliber
  • 43,891
  • 12
  • 98
  • 133
Mark
  • 877
  • 9
  • 4
  • If the `GUID ` data type is already `VARCHAR `, (In theory it should be) hope don't have to `CAST ` – Shabar Nov 21 '21 at 21:35
1

Necromancing.
GUIDs are just random numbers, there is no sequentiality in them (unless you use sequentialuid - but it restarts once the computer restarts, so it's pretty much pointless).
This is how GUIDs are actually sorted:
The code speaks for itselfs, the magical parts are:

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

Full code:

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


    }


}
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • 1
    Guids are not random numbers - they are unique numbers. That's all you can rely on. – Enigmativity Mar 19 '18 at 11:42
  • 1
    @Enigmativity: Incorrect. GUIDs are random numbers, they are - strictly speaking - not unique. But the probability of collision is infinitesimally small, given a sufficiently small dataset relative to 2^128. However, a GUID is 128 bit, so the highest possible number is 2^128 - if you have 2^128+1 entries, the probabiliy of collision is 100%. GUIDs used to have MAC-Address and system time. But because of that, an attacker could infer the MAC address of the server from a GUID. That was extremely insecure, and that's why it was changed to entirely random number. – Stefan Steiger Mar 19 '18 at 12:31
  • Actually, a guid is worse than a random number, because it hard-codes 4 bits as '4' - to signify uuid v4 - and 2 or 3 bits to indicate the variant (10 or 110 for variants 1 and 2, respectively). Thus, for variant 1 (that is, most UUIDs) a random version 4 UUID will have 6 predetermined variant and version bits, leaving 122 bits for the randomly-generated part, for a total of 2^122, or 5.3x10E36 (5.3 undecillion) possible version 4 variant 1 UUID. So in a guid the collision probablity is 2^-122, while for a random number it would be 2^-128. So a random number is more unqiue than a GUID/uuid-v4. – Stefan Steiger Mar 19 '18 at 12:40
  • 2
    @Enigmativity: A GUID-v4 as issued by SQL-server (or System.Guid.NewGuid()) is not guaranteed to be unique, it's just guaranteed to be random - and that's all you can rely on. It's not even cryptographically random either - it's just "predictably" random. For a cryptographically random Guid, see https://stackoverflow.com/questions/37170388/create-a-cryptographically-secure-random-guid-in-net – Stefan Steiger Mar 19 '18 at 12:43
1

A Guid is just what the name implies, a unique identifier. Identity doesn't imply order, it just gives you a way to determine whether 2 things are supposed to be identical. In order to sort, you need to determine what it means to be greater or maller than something else. From your question, it seems that sorting should be based on creation time; Guids won't help you with that.

Mathias
  • 15,191
  • 9
  • 60
  • 92
0

I would go with an int (or bigint) column set up as an identity. Every time a row is inserted the identity will increment. You can sort on this column to get rows in the order they were inserted.

Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76
0

What are you trying to do? sort by insert date? for that you indeed do need a datetime (or one of its variants) field since both guids and auto incr keys can never guarantee order, only uniqueness

Read this for more information: Primary Key Sorting

Community
  • 1
  • 1
Polity
  • 14,734
  • 2
  • 40
  • 40
  • Auto Increment fields alone don't guarantee order, but when combined with an ORDER BY statement they should. Is there something I'm missing here? – Bradley Uffner Oct 19 '11 at 03:09
  • @BradleyUffner - Think about the following cases: Exporting data, transactions and linking servers. All situations can't guarantee that the primary key is representitive for the insertion date. – Polity Oct 19 '11 at 03:13
  • Makes sense... Though depending on the complexity of the task, it's size, and the environment it could still be a valid way of handling this. It's hard to tell if it's appropriate without more specifics from the person asking the question. – Bradley Uffner Oct 19 '11 at 06:21
  • @BradleyUffner - Agreed but its still a bad practice which should only be used when there is a good reason to it. (performance?), Since the one asking doesnt strike me as a SQL expert, I'm not very kean of teaching bad practices :) – Polity Oct 19 '11 at 06:30