Dennis Forbes on Pragmatic Software Development
Subscribe to RSS
 
Monday, October 17 2005

[NOTE: For fun and giggles I updated this "tool", creating a "story" home for it, which you can find here: yaflaGUID - Create Sequential GUIDs in SQL Server]

As discussed in the entry on using GUIDs in your database, GUIDs in SQL Server 2000 are, at least from the user's perspective, "random". This can lead to a fragmentation and splits in your data, and it's a common reason to avoid GUIDs in the first place.

leaves2

Of course, like most problems, there are a number of possible solutions. SQL Server 2005 offers a solution in the form of NEWSEQUENTIALID() (though it's limited to being the default on a table, among other limitations).

Coincidentally I happened to be mucking around in the disassembly of rpcrt4.dll today, trying to once and for all nail down the current algorithm used for UUIDCreate (which is used behind the scenes for CoCreateGuid, which itself is used by NEWID() and System.Guid.NewGuid()), when I noticed UUIDCreateSequential in the exports. I'd never noticed this function before, and the docs verified that indeed it does create GUIDs the old sk00l way, starting with the unique MAC+time foundation, and then sequentially incrementing on each generation.

"This is like 3-lines and a minute or two to create an extended stored procedure!" think I, even though I infrequently use or advocate the use of GUIDs. Before I did that, though, I thought I'd look around to see what exists, and sure enough someone solved this problem before.

Nonetheless, for such a trivial component, especially for something that can adversely affect the stability of SQL Server, I'm prone to not trusting binaries from micro-outfits on random pages on the web. I looked for the source, and for whatever reason the source to XPGUID isn't released. I cannot overstate how ridiculously trivial this is (even adding some padding functions to make it seem more substantial). In essence it is two credible lines of code over and above the VS.NET 2003 Wizard created extended stored procedure project.

As such, I've made this available for download, source-code and all, at http://www.yafla.com/downloads/yaflaSQLGUID.zip. In it you'll find the source and a compiled Release binary, yaflaGUID.dll. You can place this (or a new build that you made yourself) in your SQL Server \binn directory and run the following command

EXEC sp_addextendedproc 'xp_yaflaGUID2005', 'yaflaGUID.DLL'

(of course you can remove it with sp_dropextendedproc)

If you want, wrap it in a User-Defined Function for some inline scalar goodness.

CREATE FUNCTION dbo.SNEWID()
RETURNS uniqueidentifier AS 
BEGIN
  DECLARE @uuid uniqueidentifier
  EXEC master..xp_yaflaGUID2005 @uuid OUTPUT
  RETURN (@uuid)
END

Voila, the old style of quasi-sequential GUIDs, with far fewer page splits (the value still does jump around, but for any closely time-related sequence of GUIDs it is sequential). Theoretically the generation of the GUID should, on average, be faster given that many are just sequentially created, however the extra indirection of the XP makes it slightly slower from a pure execution time perspective than NEWID(), but you should easily make that up in the DML calls.

  SQL 

Reader Comments

Unfortunately, it's not quite that simple (trust me, I've spent the last several days deciphering this, and am now bald in the process).

SQL Server orders 'uniqueidentifier's by doing a reversed byte comparison. It starts at the right side of the UUID and works its way left. However, sequential GUIDs are generated by incrementing from the right of each grouping. What this means is that for each group, SQL Server is going to sort on the more frequently changing byte FIRST, and then on the less frequently changing byte second.

An example:

00000000-5232-0000-0000-000000000000
00000000-551A-0000-0000-000000000000

These two GUID fragments were created by UuidCreateSequential(). We would sort these according to the (52) and (55) numbers, then by the (32) and (1A) numbers, since the 52/55 changes less frequently than 32/1A and therefore has a higher precedence for sorting. However, since SQL Server works right-to-left, it will order them by 32/1A before it even considers the 52/55. Thus, the order of the two GUIDs in SQL Server will be swapped.

The resolution to this is simple: if you give sequential GUIDs to SQL Server that are produced via UuidCreateSequential(), you need to byteswap the first ten bytes (in groups of 4, 2, 2, and 2, starting from the left).
Kramer @ 11/15/2005 12:01:23 PM
A very good day to you Kramer.

Indeed, in my tests I discovered that byte ordering issue, so I added some reordering logic to conform with what SQL Server is expecting. Anyone who downloading the xp from day 1 will get the reordered bytes that will sort properly in SQL Server.
Dennis Forbes @ 11/18/2005 8:01:50 AM
I should note, for the record, that I misspoke. Only the first eight bytes should be byteswapped (in 4, 2, and 2 byte groups), not the first ten.
Kramer @ 11/18/2005 11:21:41 AM

Add Comment

Name *:

Email Address:

(your email address is not displayed)
Website:

Comment *:


Dennis Forbes - Dennis Forbes is a Toronto-based software architect and technology writer