THE SQL Server Blog Spot on the Web
Welcome to SQLblog.com - The SQL Server blog spot on the web Sign in | Join | Help
in Search

Peter DeBetta's SQL Programming Blog

Peter DeBetta's blog about programming in SQL Server 2008, 2005, etc. using technologies such as T-SQL, .NET, CLR, C#, VB, Visual Studio, and SQL Server Management Studio.

T-SQL UrlEncode

I was perusing the newsgroups when I came across a request in the microsoft.public.sqlserver.clr newsgroup to see if there was a way to use System.Web.HttpUtility.UrlEncode in T-SQL. I know using the CLR is an option, but I decided to write a T-SQL equivalent. I reflected the code to figure out the exact rules for UrlEncoding. Then, I discovered much to my delight, a new system function in SQL Server 2005 that converts a string to its hexadecimal equivalent as a string– sys.fn_varbintohexstr. I knew there was an extended stored procedure in prior versions of SQL Server to do this conversion, but this function made writing this UrlEncode function even easier.

CREATE FUNCTION dbo.UrlEncode(@url varchar(1024))
RETURNS varchar(3072)
AS
 BEGIN
    DECLARE @count int, @c char(1), @i int, @urlReturn varchar(3072)
    SET @count = Len(@url)
    SET @i = 1
    SET @urlReturn = ''
    WHILE (@i <= @count)      BEGIN         SET @c = substring(@url, @i, 1)         IF @c LIKE '[A-Za-z0-9()''*-._! ]'          BEGIN             SET @urlReturn = @urlReturn + @c          END         ELSE          BEGIN             SET @urlReturn =                 @urlReturn +                 '%' +                 SUBSTRING(sys.fn_varbintohexstr(CAST(@c as varbinary(max))),3,2)          END         SET @i = @i +1      END    RETURN @urlReturn END GO

This function works in a very similar fashion to the UrlEncodeBytesToBytesInternal found in the .NET Framework (in System.Web.HttpUtility) except that the function I wrote does not convert the URL to a byte array. If you have the latest version of Reflector, you can view the reflected code for UrlEncodeBytesToBytesInternal by going to this URL:

code://System.Web:2.0.0.0:b03f5f7f11d50a3a/System.Web.HttpUtility/UrlEncodeBytesToBytesInternal(Byte%5b%5d,Int32,Int32,Boolean):Byte%5b%5d

And speaking of Reflector – if you have the latest version, you can copy a URL link to a method, class, property, and so on by select the item and pressing Ctrl-Alt-C (which is how I got the URL to lookup the UrlEncodeBytesToBytesInternal method in Reflector).


Published Friday, March 09, 2007 12:08 PM by Peter DeBetta
Filed under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Phil Factor said:

/*

Could one use a number 'helper' table to avoid the use of iteration? Here is an alternative method which can also be used in SQL Server 2000 (use 8000 instead of MAX)

*/

create FUNCTION ufsURLEncoded

(

@String varchar(max)

)

RETURNS varchar(max)

BEGIN

Declare @URLEncodedString varchar(max)

select @URLEncodedString=''

select @URLEncodedString=@URLEncodedString+

case when theChar LIKE '[A-Za-z0-9()''*-._!]'

then theChar

else '%'

+ substring ('0123456789ABCDEF',

(ascii(theChar) / 16)+1,1)

+  substring ('0123456789ABCDEF',

(ascii(theChar) % 16)+1,1)

end

from

(

select [theChar]=substring(@string,number,1)

from numbers

where number <= LEN(@String) ) Characterarray

-- Return the result of the function

RETURN @URLEncodedString

END

/* on a largeish string of 6000 characters it outperforms your function by at least 100%, usually a lot more. On the downside, it needs a NUMBERS table, which surely everyone uses anyway. here is code to put it in if it isn't there....

Create procedure spMaybeBuildNumberTable

as

BEGIN

IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[Numbers]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)

BEGIN

CREATE TABLE [dbo].[Numbers](

[number] [int],

CONSTRAINT [Index_Numbers] PRIMARY KEY CLUSTERED

(

[number] ASC

) ON [PRIMARY]

) ON [PRIMARY]

Declare @ii int

Select @ii=10000

SET NOCOUNT ON

while (@ii>0)

BEGIN

INSERT INTO NUMBERS(NUMBER) SELECT @II

SELECT @II=@II-1

END

end

end

March 12, 2007 5:23 AM
 

Microsoft » Blog Archives » Microsoft Press Indian Reprints : WP Publishers and Distributors - Our said:

May 3, 2007 2:49 AM
 

Microsoft » Blog Archives » MS SQL Server Database - Administration and T-SQL Programming Tips said:

June 15, 2007 9:25 AM
 

Paul Tiemann said:

I discovered a small difference between the way each version (Peter's and Phil's) works and the way that System.Web.HttpUtility.UrlEncode works. The .NET function converts spaces to +. Here is a modified version of Phi's that does that. It is also modified as Phil suggested for SQL Server 2000. I also discovered by making the same change (MAX to 8000) Peter's function works fine on SQL 2000.

Our client also requested that all URL's be lower case so my version does that as well. Thanks for the great post, it saved me a lot of time.

/* Note: this function has been modified to lower case each character that is not encoded

by changing theChar to LOWER(theChar) */

ALTER   FUNCTION dbo.fn_URLEncodeLC

(@String varchar(8000))

RETURNS varchar(8000)

BEGIN

DECLARE @URLEncodedString varchar(8000)

SELECT @URLEncodedString=''

SELECT @URLEncodedString=@URLEncodedString+

CASE WHEN

theChar LIKE '[A-Za-z0-9()''*-._!]'

THEN LOWER(theChar)

       WHEN

               theChar = ' '

       THEN '+'

ELSE '%' + substring ('0123456789ABCDEF', (ascii(theChar) / 16)+1,1) +  substring('0123456789ABCDEF', (ascii(theChar) % 16)+1,1)

END

FROM

(SELECT [theChar]=substring(@string,number,1)

FROM [master].[dbo].[NUMBERS]

WHERE number <= LEN(@String) ) Characterarray

-- Return the result of the function

RETURN @URLEncodedString

END

August 3, 2007 10:25 AM
 

Microsoft » Blog Archives » T-SQL Programming Part 5 - Using the CASE Function said:

August 21, 2007 6:37 AM
 

Microsoft » Blog Archives » … how-to guides, code samples and sample applications, said:

August 21, 2007 11:38 PM
 

Microsoft » Blog Archives » T-SQL: Search Logic said:

September 9, 2007 5:04 AM
 

hilla said:

the regex needs to be '[-A-Za-z0-9()''*._! ]'

the " - " char has to be first to apply.

February 4, 2008 5:04 AM
 

Peter DeBetta said:

Hilla, I checked and '[A-Za-z0-9()''*-._! ]' worked fine and no different than '[-A-Za-z0-9()''*._! ]'. Do you have an example where my version fails. Perhaps you are using a different collation than I am? I'm using SQL_Latin1_General_CP1_CI_AS.

February 4, 2008 2:26 PM
 

smoovbcaltex said:

I think hilla may be on to something.

The original pattern seems to consider *-. to mean "characters between * and ." which includes '+' and ','.  I noticed this because when I tested out the function, my plus and comma characters weren't being encoded.  Moving the dash to the beginning or the end of the pattern, as hilla suggested, seems to fix this problem.

February 19, 2008 8:07 PM
 

http://sqlblog.com/blogs/peter_debetta/archive/2007/03/09/t-sql-urlencode.aspx said:

March 24, 2008 3:57 AM
 

snoochybooches said:

the "-" in the LIKE clause needs to be escaped. And add "~".

'[A-Za-z0-9()''*\-._!~]' ESCAPE '\'

May 28, 2008 8:21 PM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About Peter DeBetta

Peter DeBetta is an independent consultant specializing in design, development, implementation, and deployment of Microsoft SQL Server, Microsoft SharePoint Server, and .NET solutions. Peter writes courseware, articles, and books – most notably the title Introducing SQL Server 2005 for Developers from Microsoft Press. Peter speaks at conferences around the world, including TechEd, SQL PASS Community Summit, DevTeach, SQL Connections, DevWeek, and VSLive!

Peter is a Microsoft MVP for SQL Server, an MCP, President of the North Texas SQL Server User Group, and a member of PASS.

When Peter isn’t working, you can find him singing and playing guitar (click here to hear an original song by Peter), taking pictures, or simply enjoying life with his wife, son, and new daughter.
Powered by Community Server (Commercial Edition), by Telligent Systems
  Privacy Statement