MSSQL: Using newid() on a UDF
Last night I was playing around with a SQL Server database trying to figure out how to enable a field on a table to take a unique identifier value — however, I couldn't use newid() because I needed different formatting. newid() returns a GUID in the form 4DA89473-83F5-40E6-9D6A-345A6E6D987A and I needed the string to be exactly 32 characters long, so that meant dropping the hyphens to get 4DA8947383F540E69D6A345A6E6D987A. It's a long story, but anyway.
Not really being the most proficient data dweeb in the planet (databases for me are mostly fancy stateful storage engines) I thought I'd "go 1337" and create my first user-defined function to return the formatted GUID. If you're amused by this please understand the last time I wrote a stored procedure Bill Clinton was still president. Well, not quite. I mean, I can write sprocs, I just don't like them. They remove all possibility of writing portable data access code. But I digress. I fired up the mighty query analyzer and got right down to it.
In my relational hubris I coded a simple thing that would take no parameters, call newid(), use SUBSTRING to splice the GUID and then concatenate the parts. Hahaha — hmmmm. SQL Server complained (with error 403, no less) that I had a case of "invalid use of newid() in a user-defined function". I told myself "eh?" and Googled a bit. It turns out that you can't call non-deterministic built-in methods from UDFs. This includes GETDATE() and so on. WHAT THE HECK IS WRONG WITH THIS THING???
But then I said to myself, I said "d00d, you can always just pass whatever newid() returns to the UDF!!". Boy, I'm so smart. Right. So I ended up coding the thing like this:
CREATE FUNCTION udfGuid (@uid varchar(36))
RETURNS varchar(32) AS
BEGIN
declare @part1 varchar(8)
declare @part2 varchar(4)
declare @part3 varchar(4)
declare @part4 varchar(4)
declare @part5 varchar(12)
set @part1 = SUBSTRING(@uid, 1, 8)
set @part2 = SUBSTRING(@uid, 10, 4)
set @part3 = SUBSTRING(@uid, 15, 4)
set @part4 = SUBSTRING(@uid, 20, 4)
set @part5 = SUBSTRING(@uid, 25, 12)
return (@part1 + @part2 + @part3 + @part4 + @part5)
END
Then to incorporate it into the default value of a table field you'd do something like this:
([dbo].[udfGuid](newid()))
Where of course you're just passing te value returned by newid() to the UDF. A word of caution though — this introduces a dependency as far as MSSQL is concerned, so you won't be able to edit the UDF after you "bind" it to a table field. You've been warned.
I have absolutely no doubt someone out there has a better solution for this, but damn it, I feel proud of myself and I had to blog this <g>