简体   繁体   中英

GUID comparison weirdness

.NET has two GUID data types: Guid , which orders GUIDs in the "natural" way, and SqlGuid , which considers the six bytes after the final dash to be most significant. This difference is explained in detail here: MSDN: Comparing GUID and uniqueidentifier Values .

However, in both cases, the following should hold (assuming that all ... s are equal):

57d0affe-... < 57d0afff-... < 57d0b000-...

If that is the case, why do I get the following output (see comments)?

using System;
using System.Data.SqlTypes;

class Program
{
    static void Main(string[] args)
    {
        var g1 = new SqlGuid("57d0affe-9d9d-11e4-bec2-e840f2ad1632");
        var g2 = new SqlGuid("57d0afff-9d9d-11e4-bec2-e840f2ad1632");
        var g3 = new SqlGuid("57d0b000-9d9d-11e4-bec2-e840f2ad1632");

        Console.WriteLine(g1 < g2); // prints True
        Console.WriteLine(g2 < g3); // prints False <- ?
        Console.ReadLine();
    }
}

It is my understanding that g2 < g3 should yield True as well. Did I misunderstand something or is this some bug in the framework? When using plain Guid instead of SqlGuid , the output is twice True , as expected.

Actually, the comparison goes byte by byte. So the first thing you're asking is "is 0xff smaller than 0x00?" Obviously, it's not :)

That's of course why the SQL guid notation is "weird" - it's "twice inverted". I don't know why this was decided to be so for uniqueidentifier in MS SQL (I assume it allowed for better hashing or something), but SqlGuid has to have the same behaviour, so it just gets the whole byte[] and goes one byte after another. The first byte is the last byte of the first group, the second byte is the next-to-last of the first group etc.

EDIT :

To add more information, the Microsoft standard GUID structure is defined as this:

typedef struct _GUID {
  DWORD Data1;
  WORD  Data2;
  WORD  Data3;
  BYTE  Data4[8];
} GUID;

When using byte-by-byte comparison, the first three groups get native-endianness, while the last one is always big-endian.

This is the GUID used in SQL server, and also the one that SqlGuid emulates. The native .NET System.Guid does the same thing in it's CompareTo method.

The RFCC 4122 GUID actually displays the same behaviour on big-endian machines, the only difference is that it's always big-endian for all the groups. As far as I'm aware, it's not described to have any designed ordering at all.

Comparing GUIDs for anything but equality doesn't really make much sense. If you need to do that, there's special GUIDs (like sequential or time-based GUIDs) that give you a more reasonable distribution of values while still being reasonably unique.

Since it's not defined and it doesn't really make too much sense, comparison byte-by-byte or char-by-char are the two most obvious to have - human would expect char-by-char, because that's how we see the value, but for the computer, byte-by-byte is more reasonable. But what's even more reasonable is field-by-field - and that's what's happening there.

The Comparison is a bit more complex.

Firstofall there is some order defined:

  private static readonly int[] x_rgiGuidOrder = new int[16]
    {10, 11, 12, 13, 14, 15, 8, 9, 6, 7, 4, 5, 0, 1, 2, 3};

then there is this method

private static EComparison Compare(SqlGuid x, SqlGuid y) {
        //Swap to the correct order to be compared
        for (int i = 0; i < SizeOfGuid; i++) {
            byte    b1, b2;

            b1 = x.m_value [x_rgiGuidOrder[i]];
            b2 = y.m_value [x_rgiGuidOrder[i]];
            if (b1 != b2)
                return(b1 < b2) ? EComparison.LT : EComparison.GT;
        }
        return EComparison.EQ;
    } 

However this is not the whole story, what really is the reason is the constructor from string:

    public SqlGuid(String s) {
        m_value = (new Guid(s)).ToByteArray();
    }

It creates a new GUID and then uses its byte-representation.

and this gives us the following byte-values:

g2 : 255 175 208 87 157 157 228 17 190 194 232 64 242 173 22 50
g3 : 0   176 208 87 157 157 228 17 190 194 232 64 242 173 22 50

And there we can see that the 255 is bigger than 0 and not the other way round.

You can find the full source here Fiddle how to get the byte-representation is here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM