Search code examples
c#guidnewsequentialid

How to sort sequential GUIDs in C#?


Sequential GUIDs are unique but are created with an order; that order is slightly unusual and is different to the order achieved when using the standard .NET Guid comparer.

I'm looking for a C# Guid comparer that will sort by the rules of sequential GUIDs.

== UPDATE==

I'm specifically referring to sequential GUIDs created by NewSequentialId() in SQL Server, although I realise now that the standard Win32 API call UuidCreateSequential() uses a different scheme to SQL Server (I assumed they were the same when I wrote the question).

== UPDATE 2==

petelids gives the answer below, using e.g. List<System.Data.SqlGuid>.Sort() gives the following sequence (using an initial list of GUIDs with a 1 in each 4 bit location)...

01000000-0000-0000-0000-000000000000
10000000-0000-0000-0000-000000000000
00010000-0000-0000-0000-000000000000
00100000-0000-0000-0000-000000000000
00000100-0000-0000-0000-000000000000
00001000-0000-0000-0000-000000000000
00000001-0000-0000-0000-000000000000
00000010-0000-0000-0000-000000000000
00000000-0100-0000-0000-000000000000
00000000-1000-0000-0000-000000000000
00000000-0001-0000-0000-000000000000
00000000-0010-0000-0000-000000000000
00000000-0000-0100-0000-000000000000
00000000-0000-1000-0000-000000000000
00000000-0000-0001-0000-000000000000
00000000-0000-0010-0000-000000000000
00000000-0000-0000-0001-000000000000
00000000-0000-0000-0010-000000000000
00000000-0000-0000-0100-000000000000
00000000-0000-0000-1000-000000000000
00000000-0000-0000-0000-000000000001
00000000-0000-0000-0000-000000000010
00000000-0000-0000-0000-000000000100
00000000-0000-0000-0000-000000001000
00000000-0000-0000-0000-000000010000
00000000-0000-0000-0000-000000100000
00000000-0000-0000-0000-000001000000
00000000-0000-0000-0000-000010000000
00000000-0000-0000-0000-000100000000
00000000-0000-0000-0000-001000000000
00000000-0000-0000-0000-010000000000
00000000-0000-0000-0000-100000000000

As opposed to the following order returned by List<System.Guid>.Sort()

00000000-0000-0000-0000-000000000001
00000000-0000-0000-0000-000000000010
00000000-0000-0000-0000-000000000100
00000000-0000-0000-0000-000000001000
00000000-0000-0000-0000-000000010000
00000000-0000-0000-0000-000000100000
00000000-0000-0000-0000-000001000000
00000000-0000-0000-0000-000010000000
00000000-0000-0000-0000-000100000000
00000000-0000-0000-0000-001000000000
00000000-0000-0000-0000-010000000000
00000000-0000-0000-0000-100000000000
00000000-0000-0000-0001-000000000000
00000000-0000-0000-0010-000000000000
00000000-0000-0000-0100-000000000000
00000000-0000-0000-1000-000000000000
00000000-0000-0001-0000-000000000000
00000000-0000-0010-0000-000000000000
00000000-0000-0100-0000-000000000000
00000000-0000-1000-0000-000000000000
00000000-0001-0000-0000-000000000000
00000000-0010-0000-0000-000000000000
00000000-0100-0000-0000-000000000000
00000000-1000-0000-0000-000000000000
00000001-0000-0000-0000-000000000000
00000010-0000-0000-0000-000000000000
00000100-0000-0000-0000-000000000000
00001000-0000-0000-0000-000000000000
00010000-0000-0000-0000-000000000000
00100000-0000-0000-0000-000000000000
01000000-0000-0000-0000-000000000000
10000000-0000-0000-0000-000000000000

Solution

  • There is a difference between the way Sql server and .NET sort guids.

    There is a struct in the .NET framework called SqlGuid that should behave the same way as guids in Sql Server.

    Consider the following example adapted from here:

    List<Guid> a = new List<Guid>();
    a.Add(new Guid("3AAAAAAA-BBBB-CCCC-DDDD-2EEEEEEEEEEE"));
    a.Add(new Guid("2AAAAAAA-BBBB-CCCC-DDDD-1EEEEEEEEEEE"));
    a.Add(new Guid("1AAAAAAA-BBBB-CCCC-DDDD-3EEEEEEEEEEE"));
    Console.WriteLine("--Unsorted Guids--");
    foreach (Guid g in a)
    {
        Console.WriteLine("{0}", g);
    }
    a.Sort();
    Console.WriteLine("--Sorted Guids--");
    foreach (Guid g in a)
    {
        Console.WriteLine("{0}", g);
    }
    
    List<SqlGuid> b = new List<SqlGuid>();
    b.Add(new SqlGuid("3AAAAAAA-BBBB-CCCC-DDDD-2EEEEEEEEEEE"));
    b.Add(new SqlGuid("2AAAAAAA-BBBB-CCCC-DDDD-1EEEEEEEEEEE"));
    b.Add(new SqlGuid("1AAAAAAA-BBBB-CCCC-DDDD-3EEEEEEEEEEE"));
    b.Sort();
    Console.WriteLine("--Sorted SqlGuids--");
    foreach (SqlGuid sg in b)
    {
        Console.WriteLine("{0}", sg);
    }
    

    This produces the output:

    --Unsorted Guids--
    3aaaaaaa-bbbb-cccc-dddd-2eeeeeeeeeee
    2aaaaaaa-bbbb-cccc-dddd-1eeeeeeeeeee
    1aaaaaaa-bbbb-cccc-dddd-3eeeeeeeeeee
    --Sorted Guids--
    1aaaaaaa-bbbb-cccc-dddd-3eeeeeeeeeee
    2aaaaaaa-bbbb-cccc-dddd-1eeeeeeeeeee
    3aaaaaaa-bbbb-cccc-dddd-2eeeeeeeeeee
    --Sorted SqlGuids--
    2aaaaaaa-bbbb-cccc-dddd-1eeeeeeeeeee
    3aaaaaaa-bbbb-cccc-dddd-2eeeeeeeeeee
    1aaaaaaa-bbbb-cccc-dddd-3eeeeeeeeeee

    The SqlGuid class has a constructor that takes a Guid and casting from one to the other also works so converting between them should be easy enough. Adding the following to the above code for example:

    List<SqlGuid> c = a.Select(g => new SqlGuid(g)).ToList();
    c.Sort();
    Console.WriteLine("--Sorted SqlGuids 2--");
    foreach (SqlGuid sg2 in c)
    {
        Console.WriteLine("{0}", sg2);
    }
    

    Adds the output:

    --Sorted SqlGuids 2--
    2aaaaaaa-bbbb-cccc-dddd-1eeeeeeeeeee
    3aaaaaaa-bbbb-cccc-dddd-2eeeeeeeeeee
    1aaaaaaa-bbbb-cccc-dddd-3eeeeeeeeeee