<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>networkbucket.com</title>
	<atom:link href="http://networkbucket.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://networkbucket.com</link>
	<description>SQL Server, ASP.NET, C-sharp, WordPress, and more code discussions.</description>
	<lastBuildDate>Mon, 28 Nov 2011 19:16:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Create a Unique ID With MS SQL Server Using A Base33 Stored Procedure</title>
		<link>http://networkbucket.com/2011/11/27/create-a-unique-id-with-ms-sql-server-using-a-stored-procedure/</link>
		<comments>http://networkbucket.com/2011/11/27/create-a-unique-id-with-ms-sql-server-using-a-stored-procedure/#comments</comments>
		<pubDate>Sun, 27 Nov 2011 06:49:01 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[sql server]]></category>
		<category><![CDATA[stored procedures]]></category>

		<guid isPermaLink="false">http://networkbucket.com/?p=185</guid>
		<description><![CDATA[Ever want to create a unique ID for a record in Microsoft&#8217;s SQL Server? I do all the time but I hate using boring sequential numbers. And I believe that using auto-numbers can place some security risks just because people can &#8216;try&#8217; numbers around any number they have. Why Do This? Oh sure, you obviously [...]]]></description>
			<content:encoded><![CDATA[<p>Ever want to create a unique ID for a record in Microsoft&#8217;s SQL Server? I do all the time but I hate using boring sequential numbers. And I believe that using auto-numbers can place some security risks just because people can &#8216;try&#8217; numbers around any number they have.</p>
<h3>Why Do This?</h3>
<p>Oh sure, you obviously put other security measures in place but this method can easily add a little more difficulty for anyone trying to &#8216;guess&#8217; a transaction id. And every little bit helps.</p>
<p>So to spice things up a bit and come up with something a bit more difficult to guess, I use a &#8220;Base 33&#8243; code that creates a unique ID using the current &#8216;timestamp&#8217; down to the milliseconds. It uses the numerals 0-9 and capital letters from A-Z *EXCEPT* the letters &#8220;I&#8221;,&#8221;O&#8221;, and &#8220;X&#8221;.</p>
<p>(I don&#8217;t use &#8220;I&#8221; and &#8220;O&#8221; due to the possiblility of mistaking an &#8220;I&#8221; for a &#8220;1&#8243; or a &#8220;O&#8221; for a &#8220;0&#8243; (zero) and I don&#8217;t use &#8220;X&#8221; just because I frequently &#8216;reserve&#8217; &#8220;X&#8221; for other things in my code elsewhere.)</p>
<p>And you can easily adapt this for a &#8220;Base 36&#8243; ID and include them if you wish but for this article, we will work with &#8220;Base 33&#8243;.</p>
<p><OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab" id="Player_0781611e-11c8-4364-833f-b7424bc2d973"  WIDTH="600px" HEIGHT="200px"> <PARAM NAME="movie" VALUE="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&#038;MarketPlace=US&#038;ID=V20070822%2FUS%2Fad05-20%2F8010%2F0781611e-11c8-4364-833f-b7424bc2d973&#038;Operation=GetDisplayTemplate"><PARAM NAME="quality" VALUE="high"><PARAM NAME="bgcolor" VALUE="#FFFFFF"><PARAM NAME="allowscriptaccess" VALUE="always"><embed src="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&#038;MarketPlace=US&#038;ID=V20070822%2FUS%2Fad05-20%2F8010%2F0781611e-11c8-4364-833f-b7424bc2d973&#038;Operation=GetDisplayTemplate" id="Player_0781611e-11c8-4364-833f-b7424bc2d973" quality="high" bgcolor="#ffffff" name="Player_0781611e-11c8-4364-833f-b7424bc2d973" allowscriptaccess="always"  type="application/x-shockwave-flash" align="middle" height="200px" width="600px"></embed></OBJECT> <NOSCRIPT><A HREF="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&#038;MarketPlace=US&#038;ID=V20070822%2FUS%2Fad05-20%2F8010%2F0781611e-11c8-4364-833f-b7424bc2d973&#038;Operation=NoScript">Amazon.com Widgets</A></NOSCRIPT></p>
<h3>The Concepts</h3>
<p>The concept is based on starting with a very large number created by combining the year (4 digits), month (2 digits), day (2 digits), hour (2 digits), minute (2 digits), second (2 digits), and millisecond (3 digits) that the request is made.</p>
<p>So unless you have an extremely busy site (or you encounter the extremely unlikely chance that 2 transactions occur at the **exact same millisecond) we end up with a 17-digit number that should be unique.  That is a good place to start when looking for a unique ID.</p>
<p>(**OK, OK. I know purists will argue that Microsoft SQL Server does not get the exact millisecond but rather the last digit of the milliseconds will be either &#8217;0&#8242;, &#8217;3&#8242; or &#8217;7&#8242; but this should be fine for all but the most demanding situations.)</p>
<h3>The Code</h3>
<p>So without further delay, let&#8217;s see some code. Here is the &#8216;meat&#8217; of the ID creator &#8211; an MS SQL function. The function can be created like this:</p>
<div style="border: solid 1px #cccccc; padding: 10px;"><code>set ANSI_NULLS OFF<br />
set QUOTED_IDENTIFIER OFF<br />
GO<br />
CREATE FUNCTION [dbo].[funcGetBase33] (@intNumber as bigint )<br />
RETURNS varchar(15) AS<br />
BEGINDECLARE @strBase as char(33)<br />
DECLARE @intRemainder as bigint<br />
DECLARE @strResult as VARCHAR(15)</p>
<p>SET @strBase = '0123456789ABCDEFGHJKLMNPQRSTUVWYZ'<br />
SET @strResult = ''<br />
If @intNumber &lt; 33 SET @strResult = SUBSTRING(@strBase,@intNumber+1,1) Else WHILE @intNumber &gt; 0<br />
BEGIN<br />
SET @intRemainder = @intNumber % 33<br />
SET @strResult = SUBSTRING(@strBase,@intRemainder+1,1) + @strResult<br />
SET @intNumber = (@intNumber - @intRemainder) / 33<br />
END<br />
RETURN @strResult<br />
END</code>
</div>
<p>And it can be used in an SQL query like this:</p>
<div style="border: solid 1px #cccccc; padding: 10px;"><code>SELECT dbo.funcGetBase33(dbo.funcGetNowAsInt()) AS MyCodeString</code></div>
<p>The &#8216;funcGetBase33&#8242; is the actual function that converts the big integer into the &#8220;Base 33&#8243; code and in this example, I am creating the big integer using &#8216;funcGetNowAsInt&#8217;.</p>
<p>**NOTE:  &#8216;funcGetNowAsInt&#8217; is shown further down the page but essentially grabs all the &#8216;parts&#8217; of the timestamp (year to millisecond) and &#8216;pads&#8217; them with 0&#8242;s if necessary to get the 17-digit number representing the timestamp down to the millisecond.</p>
<h3>Operation Overview</h3>
<p>So the overview of what this function does is essentially this:</p>
<p>1. First, figure out the remainder of the bigint &#8216;timestamp&#8217; when dividing by 33.</p>
<p>2. Then get the character in the &#8216;remainder position&#8217; of the string &#8217;0123456789ABCDEFGHJKLMNPQRSTUVWYZ&#8217;.</p>
<p>3. Then subtract the remainder from the bigint and then divide by 33.</p>
<p>4. Repeat with the result until you only have a remainder.</p>
<h3>The Detailed Walk-Thru</h3>
<p>Now let&#8217;s walk thru the stored procedure using a real value. We&#8217;ll use the SQL query &#8220;SELECT dbo.funcGetBase33(dbo.funcGetNowAsInt()) AS MyCodeString&#8221; and for this example we&#8217;ll run this shortly after 7 PM on Nov 27th, 2011 &#8211; 29.437 seconds after 7 PM to be exact.</p>
<p>First we get the bigint that represents the timestamp using dbo.funcGetNowAsInt (see additional stored procedure below to see that function) and we get a bigint of 20111127190029437.  That represents the timestamp Nov 27, 2011 7:00:29.437 PM in a &#8216;modified&#8217; bigint.</p>
<p>We pass that bigint to our funcGetBase33 function. After declaring our variables in the procedure, we start the looping using:</p>
<div style="border: solid 1px #cccccc; padding: 10px;"><code>If @intNumber &lt; 33 SET @strResult = SUBSTRING(@strBase,@intNumber+1,1) Else WHILE @intNumber &gt; 0</code></div>
<p>This checks to see if we have reached the last item yet (if our @intNumber is less than 0). If we reached the last item, then get the string character and end the function.</p>
<p>If we are greater than 33, then we find the remainder after dividing by 33 using:</p>
<div style="border: solid 1px #cccccc; padding: 10px;"><code>@intRemainder = @intNumber % 33</code></div>
<p>We then find the string in the &#8216;remainder + 1&#8242; position of our @strBase and put that character in the FRONT of our result string using:</p>
<div style="border: solid 1px #cccccc; padding: 10px;"><code>@strResult = SUBSTRING(@strBase,@intRemainder+1,1) + @strResult</code></div>
<p>So for our first iteration the calculation looks like:</p>
<p>20111127190029437 / 33 = 609428096667558 remainder 23</p>
<p>Our first (which is actually the last) character of our result is in the 24th position of @strBase (&#8217;0123456789ABCDEFGHJKLMNPQRSTUVWYZ&#8217;) which is &#8216;P&#8217;.</p>
<p>Then we subtract the remainder from our number and divide by 33 to get our new number using <b>@intNumber = (@intNumber &#8211; @intRemainder) / 33</b>. This gives us the new number to use for our second iteration &#8211; 609428096667558.</p>
<p>The second iteration calculation looks like this:</p>
<p>609428096667558 / 33 = 18467518080835 r 3</p>
<p>and the 4th (3 + 1) character of @strBase is &#8217;3&#8242;. So we put &#8217;3&#8242; in the front of our @strResult. Now our @strResult looks like &#8217;3P&#8217;.</p>
<p>We repeat loops until our new number is less than 33, then grab that character and then exit the loop returning @strResult.</p>
<p>Make sense? The full calculation loops look like this:</p>
<p>20111127190029437 / 33 = 609428096667558 r 23 (@strBase pos 24 is &#8216;P&#8217; &#8211; @strResult = &#8216;P&#8217;)<br />
609428096667558 / 33 = 18467518080835 r 3 (@strBase pos 4 is &#8217;3&#8242; &#8211; @strResult = &#8217;3P&#8217;)<br />
18467518080835 / 33 = 559621760025 r 10 (@strBase pos 11 is &#8216;A&#8217; &#8211; @strResult = &#8216;A3P&#8217;)<br />
559621760025 / 33 = 16958235152 r 9 (@strBase pos 10 is &#8217;9&#8242; &#8211; @strResult = &#8217;9A3P&#8217;)<br />
16958235152 / 33 = 513885913 r 23 (@strBase pos 24 is &#8216;P&#8217; &#8211; @strResult = &#8216;P9A3P&#8217;)<br />
513885913 / 33 = 15572300 r 13 (@strBase pos 14 is &#8216;D&#8217; &#8211; @strResult = &#8216;DP9A3P&#8217;)<br />
15572300 / 33 = 471887 r 29 (@strBase pos 30 is &#8216;V&#8217; &#8211; @strResult = &#8216;VDP9A3P&#8217;)<br />
471887 / 33 = 14299 r 20 (@strBase pos 21 is &#8216;L&#8217; &#8211; @strResult = &#8216;LVDP9A3P&#8217;)<br />
14299 / 33 = 433 r 10 (@strBase pos 11 is &#8216;A&#8217; &#8211; @strResult = &#8216;ALVDP9A3P&#8217;)<br />
433 / 33 = 13 r 4 (@strBase pos 5 is &#8217;4&#8242; &#8211; @strResult = &#8217;4ALVDP9A3P&#8217;)<br />
13 / 33 = 0 r 13 (@strBase pos 14 is &#8216;D&#8217; &#8211; @strResult = &#8216;D4ALVDP9A3P&#8217;)</p>
<p>So for this particular timestamp that returns a bigint of 20111127190029437 , we get the string &#8216;D4ALVDP9A3P&#8217; for our Unique ID.</p>
<h3>Conclusions</h3>
<p>I find I use this function quite often for a variety of reasons and it is really not that difficult once you plop this into your MS SQL Server instance.  And if you think about it for a minute, I am sure you can come up with a few places to use it yourself.</p>
<p>If nothing else, it will get you thinking a bit more creatively when using Unique ID&#8217;s in your SQL Server applications and stored procedures.</p>
<p><OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab" id="Player_0781611e-11c8-4364-833f-b7424bc2d973"  WIDTH="600px" HEIGHT="200px"> <PARAM NAME="movie" VALUE="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&#038;MarketPlace=US&#038;ID=V20070822%2FUS%2Fad05-20%2F8010%2F0781611e-11c8-4364-833f-b7424bc2d973&#038;Operation=GetDisplayTemplate"><PARAM NAME="quality" VALUE="high"><PARAM NAME="bgcolor" VALUE="#FFFFFF"><PARAM NAME="allowscriptaccess" VALUE="always"><embed src="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&#038;MarketPlace=US&#038;ID=V20070822%2FUS%2Fad05-20%2F8010%2F0781611e-11c8-4364-833f-b7424bc2d973&#038;Operation=GetDisplayTemplate" id="Player_0781611e-11c8-4364-833f-b7424bc2d973" quality="high" bgcolor="#ffffff" name="Player_0781611e-11c8-4364-833f-b7424bc2d973" allowscriptaccess="always"  type="application/x-shockwave-flash" align="middle" height="200px" width="600px"></embed></OBJECT> <NOSCRIPT><A HREF="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&#038;MarketPlace=US&#038;ID=V20070822%2FUS%2Fad05-20%2F8010%2F0781611e-11c8-4364-833f-b7424bc2d973&#038;Operation=NoScript">Amazon.com Widgets</A></NOSCRIPT></p>
<h3>The Additional Info</h3>
<p>Here is the function to convert the &#8216;timestamp&#8217; to a 17-digit bigint based on the year (4 digits), month (2 digits), day (2 digits), hour (2 digits), minute (2 digits), second (2 digits), and millisecond (3 digits).</p>
<div style="border: solid 1px #cccccc; padding: 10px;"><code>CREATE FUNCTION [dbo].[funcGetNowAsInt] ()<br />
RETURNS varchar(30) AS<br />
BEGINDECLARE @strResult as VARCHAR(30)<br />
DECLARE @dteToday as datetime<br />
DECLARE @pad as varchar(17)</p>
<p>SET @dteToday = {fn NOW()}<br />
SET @strResult = ''<br />
SET @pad = '0000000000'</p>
<p>BEGIN<br />
SET @strResult = @strResult + (<br />
RIGHT(@pad + CAST(DATEPART(yyyy,CAST (@dteToday AS datetime)) As varchar),4)<br />
+ RIGHT(@pad + CAST(DATEPART(mm,CAST (@dteToday AS datetime)) As varchar),2) +<br />
+ RIGHT(@pad + CAST(DATEPART(dd,CAST (@dteToday AS datetime)) As varchar),2) +<br />
+ RIGHT(@pad + CAST(DATEPART(hh,CAST (@dteToday AS datetime)) As varchar),2) +<br />
+ RIGHT(@pad + CAST(DATEPART(n,CAST (@dteToday AS datetime)) As varchar),2) +<br />
+ RIGHT(@pad + CAST(DATEPART(ss,CAST (@dteToday AS datetime)) As varchar),2) +<br />
+ RIGHT(@pad + CAST(DATEPART(ms,CAST (@dteToday AS datetime)) As varchar),3)<br />
)</p>
<p>END<br />
RETURN @strResult</p>
<p>END<br />
GO<br />
</code></div>
<h3>Reversing It</h3>
<p>And, of course, here is a function to reverse the whole thing. For instance, if for some reason we want to find out when this ID was created we can call the below function using a query like this:</p>
<p>SELECT dbo.funcGetBase33Rev(&#8216;D4ALVDP9A3P&#8217;) AS MyCodeString</p>
<p>and it should return our original big int of 20111127190029437.</p>
<div style="border: solid 1px #cccccc; padding: 10px;"><code>set ANSI_NULLS OFF<br />
set QUOTED_IDENTIFIER OFF<br />
GO<br />
ALTER FUNCTION [dbo].[funcGetBase33Rev] (@strCode as varchar(30) )<br />
RETURNS bigint ASBEGIN</p>
<p>DECLARE @strBase as char(33)<br />
DECLARE @intBaseLength as bigint<br />
DECLARE @intResult as bigint</p>
<p>DECLARE @intPosition as bigint<br />
DECLARE @intBasePosition as bigint<br />
DECLARE @intLength as bigint<br />
DECLARE @strCharToFind as varchar(1)</p>
<p>SET @strBase = '0123456789ABCDEFGHJKLMNPQRSTUVWYZ'<br />
SET @intResult = CAST(0 As bigint)</p>
<p>SET @intLength = CAST(LEN(@strCode) As bigint)<br />
SET @intPosition = CAST(0 As bigint)</p>
<p>WHILE @intPosition &lt; @intLength<br />
BEGIN<br />
SET @strCharToFind = SUBSTRING(@strCode, @intPosition + 1, 1)<br />
SET @intBasePosition = CAST(CHARINDEX(@strCharToFind,@strBase) - 1 As bigint)</p>
<p>SET @intResult = (@intResult * 33) + @intBasePosition<br />
SET @intPosition = @intPosition + 1<br />
END</p>
<p>RETURN @intResult<br />
END<br />
</code></div>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://networkbucket.com/2011/11/27/create-a-unique-id-with-ms-sql-server-using-a-stored-procedure/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

