How can I get only numbers from a string as a computed column in SQL Server without leading zeros?
Background
I currently have a function to get only numbers (no leading zeros) from a string (let's call two variables @alphaNumeric
and @strippedNumber
).
It turns out that it is more efficient to have a @strippedNumber
pre-calculated in the table I am querying from than to calculate it on the fly using my function (let's call it dbo.fnFormatNumeric
).
Setting up my calculated column I tried it like this:
ALTER TABLE dbo.Data
ADD StrippedNumber AS CONVERT(BIGINT, dbo.fnFormatNumeric(AlphaNumeric))
This is really effective when converting large amounts of data (on the order of 25,000,000 rows).
Problem occurs when trying to update my database (we are dropping and recreating all functionality). So I tried to exclude this feature from dumping or recreating.
Mistake. Now I cannot install the new database as it depends on a function that was not created.
Ideal scenario
I would like to be able to select all numbers from a row as a calculated column in my table without using a function.
Study
The Sql Authority Blog offers a function to use to get numeric values, which this answer is on SO . This is not good as the function is causing me problems.
Another answer on SO suggests using LEFT
and PATINDEX
, but that will only give me the first set of numbers, not all of them in the string.
This question on SO does not fit as it ISNUMERIC
only uses to get strings where everything is numeric.
I found a blog post where they use PATINDEX
to get numbers from a string, but that assumes all the numbers are together.
Inputs and expected outputs:
@alphaNumeric => @strippedNumber
-----------------------------------
FXADJ011016CR => 11016
15-June-2016 => 152016
708014 => 708014
FXRWECTB => (empty string)
Existing function:
Input @alphaNumeric varchar(255)
DECLARE @strtoCheckLength Int,
@strCount Int,
@code Int,
@StrippedNumber varchar(255)
-- Get and set length and loop variables
Set @strtoCheckLength = Len(@alphaNumeric)
Set @strCount = (0)
Set @StrippedNumber = ''
-- Make sure we only include Numerics
While @strCount <= @strtoCheckLength
Begin
set @code = Ascii(SubString(@alphaNumeric, @strCount, 1))
If (@code between 48 and 57)
Begin
set @StrippedNumber = @StrippedNumber + Substring(@alphaNumeric, @strCount, 1)
End
Set @strCount = (@strCount + 1)
End
-- Remove Leading Zeros
While (Len(@StrippedNumber) > 0) And (Left(@StrippedNumber, 1) = '0')
Begin
Set @StrippedNumber = Right(@StrippedNumber, Len(@StrippedNumber) - 1)
End
Return @StrippedNumber
source to share
Hopefully this snippet helps you define your computed column.
Basically, a table of numbers is used to split a string by indexing, then the query outputs the concatenation output to bigint to remove leading zeros before being processed into varchar (255)
In a production database, I would recommend that the table of numbers be predefined and populated. There is a lot of discussion about how to use them at www.SqlServerCentral.com
--set up a numbers table
select top 50 n=identity(int,1,1) into #temp_numbers from syscolumns
/*
FXADJ011016CR => 11016
15June2016 => 152016
708014 => 708014
FXRWECTB => Empty String
*/
declare @input varchar(50) = 'FXADJ011016CR'
declare @output varchar(50)
select isnull(cast(cast((
select numericValue as [text()] from (
select substring(@input, t.n, 1) as numericValue
from #temp_numbers t
where isnumeric(substring(@input, t.n, 1))=1
) tblResults for xml path('')
) as bigint) as varchar(255)),'')
drop table #temp_numbers
source to share
You can use this
DECLARE @SampleData AS TABLE
(
Value varchar(100)
)
INSERT INTO @SampleData
VALUES ('FXADJ011016CR'),('15June2016'),
('708014 '), ('FXRWECTB ')
DECLARE @RegexNonNumber VARCHAR(30) = '%[^0-9]%'
;WHILE (EXISTS (SELECT 1 FROM @SampleData sd WHERE PatIndex(@RegexNonNumber, sd.[Value]) > 0))
BEGIN
UPDATE @SampleData
SET
[Value] = Stuff([Value], PatIndex(@RegexNonNumber, [Value]), 1, '')
WHERE PatIndex(@RegexNonNumber, [Value]) > 0
END
SELECT * FROM @SampleData sd
Demo link: Rextester
source to share
Use functions Replace
to replace characters with blank values.
in the following way: -
select cast(replace(replace(replace(replace(replace(replace(replace
(replace(replace(replace(replace(replace(replace(replace(replace
(replace(replace(replace(replace(replace(replace(replace(replace
(replace(replace(replace(replace(replace(replace
(@str,'A',''),'B',''),'C',''),'D',''),'E',''),'F',''),'G',''),'H',''),'I',''),'J','')
,'K',''),'L',''),'M',''),'N',''),'O',''),'P',''),'Q',''),'R',''),'S',''),'T','')
,'U',''),'V',''),'W',''),'X',''),'Y',''),'Z',''),'$',''),',',''),' ','') as bigint)
Note: -
This solution is ugly and so bad from a performance standpoint, but I posted it as the last solution and hopefully get better than it shapes the rest of the answers.
source to share
If you have 25,000,000 records, I would guess the Denormalize
table is better .
Add a standard column bigint
.
ALTER TABLE dbo.Data
ADD StrippedNumber AS BIGINT NULL
And a UPDATE
column with data.
UPDATE dbo.Data SET StrippedNumber = CONVERT(BIGINT, dbo.fnFormatNumeric(AlphaNumeric))
The only drawback of this solution is that all inserts and updates have to manually update the column StrippedNumber
with data from the column AlphaNumeric
.
source to share