It is important to note that UUIDs created using UuidCreateSequential will not be ordered by SQL Server.
- SQL Server follows RFC when it comes to sorting UUIDs
- RFC was wrong.
UuidCreateSequential did it right.- but
UuidCreateSequential creates something different from what SQL Server expects
Background
Type 1 UUIDs created by UuidCreateSequential are not sorted in SQL Server.
SQL Server NewSequentialID uses UuidCreateSequential , with some byte-shuffling applied. From online books:
NEWSEQUENTIALID (Transact-SQL)
NEWSEQUENTIALID is a wrapper over the Windows UuidCreateSequential function, with some byte shuffling applied
which then links to the MSDN blog post:
How to create sequential GUIDs for SQL Server in .NET ( archive )
public static Guid NewSequentialId() { Guid guid; UuidCreateSequential(out guid); var s = guid.ToByteArray(); var t = new byte[16]; t[3] = s[0]; t[2] = s[1]; t[1] = s[2]; t[0] = s[3]; t[5] = s[4]; t[4] = s[5]; t[7] = s[6]; t[6] = s[7]; t[8] = s[8]; t[9] = s[9]; t[10] = s[10]; t[11] = s[11]; t[12] = s[12]; t[13] = s[13]; t[14] = s[14]; t[15] = s[15]; return new Guid(t); }
It all starts with the number of ticks from 1582-10-15 00:00:00 (October 15, 1592, the date of the Gregorian reform to the Christian calendar). Ticks are the number of intervals of 100 ns.
For example:
- 06/12/2017 4:09:39 UTC
- = 137 318 693 794 503 714 ticks
- =
0x01E7DA9FDCA45C22 ticks
The RFC says that we should divide this value into three parts:
- UInt32 low (4 bytes)
- Uint16 mid (2 bytes)
- UInt32 hi (2 bytes)
So we divided it:
0x01E7DA9FDCA45C22 | Hi | Mid | Low | |--------|--------|------------| | 0x01E7 | 0xDA9F | 0xDCA45C22 |
And then the RFC says that these three integers should be written in the following order:
- Low: 0xDCA45C22
- Mid: 0xDA9F
- High: 0x01E7
If you follow the RFC, these values should be written in big-endian format (otherwise called "network byte order"):
DC A4 5C 22 DA 9F x1 E7 xx xx xx xx xx xx xx xx
It was a bad design because you cannot take the first 8 bytes of UUIDs and treat them both at the large end of UInt64 and at low-intensity UInt64. This is an absolutely dumb encoding.
UuidCreateSequential gets it right
Microsoft followed all the same rules:
- Low: 0xDCA45C22
- Mid: 0xDA9F
- High: 0x1E7
But they write it in Intel's little-endian order:
22 5C A4 DC 9F DA E7 x1 xx xx xx xx xx xx xx xx
If you look at this, you just wrote out little-endian Int64 :
225CA4DC9FDAE701
Value:
- if you want to extract the timestamp
- or sort by timestamp
it is trivial; just treat the first 8 bytes as UInt64.
In RFC, you have no choice but to execute all kinds of bit-bits. Even on big end machines, you cannot treat a 64-bit timestamp as a 64-bit timestamp.
How to cancel it
Given the small endian guid from UuidCreateSequential :
DCA45C22-DA9F-11E7-DDDD-FFFFFFFFFFFF
with raw bytes:
22 5C A4 DC 9F DA E7 11 DD DD FF FF FF FF FF FF
This is decoded to:
Low Mid Version High -------- ---- ------- ---- ----------------- DCA45C22-DA9F-1 1E7 -DDDD-FFFFFFFFFFFF
- Low: 0xDCA45C22
- Mid: 0xDA9F
- High: 0x1E7
- Version: 1 (type 1)
We can write this back in RFC order:
DC A4 5C 22 DA 9F 11 E7 DD DD FF FF FF FF FF FF
Short version
| Swap | Swap | Swap | Copy as-is Start index | 0 1 2 3 | 4 5 | 6 7 | End index | 3 2 1 0 | 5 4 | 7 6 | ---------------|-------------|-------|-------|------------------------ Little-endian: | 22 5C A4 DC | 9F DA | E7 11 | DD DD FF FF FF FF FF FF Big-endian: | DC A4 5C 22 | DA 9F | 11 E7 | DD DD FF FF FF FF FF FF