18

Users enter a search term in a box, and that value gets passed to a stored procedure and checked against a few different fields in the database. These fields are not always of the same data type.

One field (phone number) consists of all numbers, so when checking it strips out all non-numeric characters from the string using a .Net CLR function.

SELECT dbo.RegexReplace('(123)123-4567', '[^0-9]', '')

The problem is, this function abruptly stops working on occasion with the following error:

Msg 6533, Level 16, State 49, Line 2
AppDomain MyDBName.dbo[runtime].1575 was unloaded by escalation policy to ensure the 
consistency of your application. Out of memory happened while accessing a critical resource. 
System.Threading.ThreadAbortException: Exception of type 
'System.Threading.ThreadAbortException' was thrown.
System.Threading.ThreadAbortException: 

I've tried the suggestions posted on MSDN for this error, but am still getting the problem. At this time, switching to a 64-bit server is not an option for us.

I know restarting the server releases whatever memory it has held, but that is not a viable solution in a production environment.

Is there a way to strip non-numeric characters out of a string in SQL Server 2005 using T-SQL only?

Jon Seigel
16.9k6 gold badges46 silver badges86 bronze badges
asked Jul 19, 2012 at 15:03
0

4 Answers 4

21

I found this T-SQL function on SO that works to remove non-numeric characters from a string.

CREATE Function [fnRemoveNonNumericCharacters](@strText VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
 WHILE PATINDEX('%[^0-9]%', @strText) > 0
 BEGIN
 SET @strText = STUFF(@strText, PATINDEX('%[^0-9]%', @strText), 1, '')
 END
 RETURN @strText
END
answered Jul 19, 2012 at 15:50
3
  • My question was asking for a T-SQL alternative to using the CLR function. I only posted the extra CLR data because you asked for it in the comments, and I thought you knew of a way to fix the problem. I'd prefer to fix the CLR method, however my research has shown that the "fix" is to upgrade to a 64-bit server, which is not an available option to me at this time. I realize now that all the CLR info in the question can be misleading, so I removed it from my question entirely. Commented Jul 19, 2012 at 16:06
  • I thought that maybe the method you used to deploy the assembly or create the function might yield some clue. "Escalation policy" made me think it might have something to do with security or safe/unsafe access. Sorry I can't be of more help, but on a 32-bit server you may be best off using T-SQL instead. Commented Jul 19, 2012 at 16:27
  • @AaronBertrand No problem, thanks for the input :) We do hope to move to a 64-bit server within the next year or two, so hopefully that should clear up the CLR error completely. Commented Jul 19, 2012 at 16:39
6

This works much better:

SELECT
 (SELECT CAST(CAST((
 SELECT SUBSTRING(FieldToStrip, Number, 1)
 FROM master..spt_values
 WHERE Type='p' AND Number <= LEN(FieldToStrip) AND
 SUBSTRING(FieldToStrip, Number, 1) LIKE '[0-9]' FOR XML Path(''))
 AS xml) AS varchar(MAX)))
FROM
 SourceTable
Paul White
95.4k30 gold badges440 silver badges689 bronze badges
answered Aug 27, 2020 at 12:24
1
  • just keep in mind that this doesn't work in Azure Sql instances Commented Sep 11, 2024 at 20:04
1

I'm pretty confident in this solution. I'm not certain about performance but any opinions about this approach are definitely welcome! Basically for each character in the string @String if the ASCII value of the character is between the ASCII values of '0' and '9' then keep it, otherwise replace it with a blank.

CREATE FUNCTION [dbo].[fnStripNonNumerics](
 @String VARCHAR(500))
RETURNS VARCHAR(1000)
AS
BEGIN
 DECLARE
 @n INT = 1,
 @Return VARCHAR(100) = ''
 WHILE @n <= LEN(@String)
 BEGIN
 SET @Return = @Return + CASE
 WHEN ASCII(SUBSTRING(@String, @n, 1)) BETWEEN ASCII('0') AND ASCII('9')
 THEN SUBSTRING(@String, @n, 1)
 ELSE ''
 END
 SET @n = @n + 1
 END
 RETURN CASE
 WHEN @Return = ''
 THEN NULL
 ELSE @Return
 END
END
answered Jun 23, 2014 at 20:40
0

The WHILE is not as performant as the following, which also works on Azure SQL and caters for most INTs and protects against 'CAST' failures for BIGINTs (10+ characters long)

DECLARE @MaxColumnLen INT = 30;
DECLARE @Tally TABLE (n int);
INSERT INTO @Tally (n) SELECT TOP (@MaxColumnLen) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM sys.all_objects;
set @starttime=getdate();
SELECT count(1) as 'Number of valid INT fields'
FROM YourTable yt
WHERE
 TRY_CAST((
 SELECT SUBSTRING(yt.YourTextField, t.n, 1)
 FROM @Tally t
 WHERE t.n <= LEN(yt.YourTextField)
 AND SUBSTRING(yt.YourTextField, t.n, 1) LIKE '[0-9]'
 AND LEN(yt.YourTextField) <= 9 -- to avoid BIGINT overflow if the string contains a numerical value greater than '2147483647'
 FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(''' + cast(@MaxColumnLen as nvarchar(12)) + ''')') AS INT
 ) IS NOT NULL
;
set @endtime=getdate(); select DATEDIFF(ms,@starttime,@endtime) as 'Time in ms';
answered Mar 25 at 2:15

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.