9

I have an alphanumeric string as input and I want to get two results out of it:

  • a string where all the numerals are removed

    and

  • an integer that is the sum of all the digits in the input string.

For instance, for this input:

GR35hc7vdH35

I want the following output:

| Col1.         |        Col2    |
----------------------------------
| GRhcvdH       |        23      |

How can do this?

0

4 Answers 4

5

Try the following:

CREATE FUNCTION dbo.AlphaNumericSplitter 
(
    @string varchar(8000)
)
RETURNS TABLE
AS
RETURN  (
    WITH    Alphanumeric (col1)
    AS      (
            -- Put out string into a cte table
            SELECT @string
            ),
            Nmbrs (n)
    AS      (
            -- Numbers so we can split the string
            SELECT  TOP(LEN(@string))
                    ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
            FROM    sys.all_objects AS o1
            CROSS
            JOIN    sys.all_objects AS o2
            ),
            y
    AS      (
            SELECT  N.n
                    a.col1,
                    N.x,
                    -- Get the numbers only
                    Numbers = TRY_CONVERT(int, N.x)
            FROM    Alphanumeric AS a
            CROSS 
            APPLY   (SELECT [x] = SUBSTRING(a.col1, n, 1), Nmbrs.n FROM Nmbrs) AS N
            )
    SELECT  z.Col1,
            Col2 = SUM(y.Numbers)
    FROM    y
    --  Get the letters only
    CROSS
    APPLY   (SELECT (SELECT x + '' FROM y WHERE Numbers IS NULL ORDER BY y.n FOR XML PATH(''))) AS z (Col1)
    GROUP   BY
            z.Col1);
GO

SELECT * FROM AlphaNumericSplitter('GR35hc7vdH35');

Results:

enter image description here

0
8

SQL Server doesn't support replacing patterns of multiple characters - so to do it via REPLACE would take potentially 10 operations.

With that in mind one way of doing it would be a recursive CTE to process the digits 0-9 sequentially.

It does the replacement and then checks the length of the before and after strings to know how many characters of that number there were and what needs to be added on to the total.

DECLARE @Input VARCHAR(8000) = 'GR35hc7vdH35';

WITH R(Level,Input,Accumulator,StringLength)
     AS (SELECT 0,
                Input,
                0,
                DATALENGTH(Input)
         FROM   (SELECT REPLACE(@Input, '0', '')) D(Input)
         UNION ALL
         SELECT NewLevel,
                NewInput,
                Accumulator + NewLevel * ( StringLength - NewStringLength ),
                NewStringLength
         FROM   R
                CROSS APPLY (SELECT Level + 1) C(NewLevel)
                CROSS APPLY (SELECT REPLACE(Input, NewLevel, '')) C2(NewInput)
                CROSS APPLY (SELECT DATALENGTH(NewInput)) C3(NewStringLength)
         WHERE  NewLevel <= 9)
SELECT Input       AS Col1,
       Accumulator AS Col2
FROM   R
WHERE  Level = 9;

Or you could use CLR and regular expressions (SQL Server 2012 compatible version).

using System;
using System.Data.SqlTypes;
using System.Collections;
using System.Text.RegularExpressions;

public partial class UserDefinedFunctions
{
    private static readonly Regex digitRegex = new Regex(@"[\d]", RegexOptions.Compiled);

    [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "FillRow",
                                            TableDefinition = @"Stripped NVARCHAR(MAX),
                                                                Total INT")]

    public static IEnumerable ReplaceAndTotalise(SqlString input)
    {
        if (!input.IsNull)
        {
            int total = 0;
            string stripped = digitRegex.Replace((string)input, match =>
            {
                total += int.Parse(match.Value);
                return string.Empty;
            });

            yield return new Tuple<string, int>(stripped, total);
        }
    }

    public static void FillRow(object resultObject, out SqlString stripped, out SqlInt32 total)
    {
        var result = (Tuple<string, int>)resultObject;
        stripped = result.Item1;
        total = result.Item2;

    }
}

Example Usage

SELECT Stripped,
       Total
FROM   [dbo].[ReplaceAndTotalise]('GR35hc7vdH35') 
2

Kind of

... r.s ...
cross apply (
  select s = sum(cast(substring(mycol,i,1) as int)) 
  from (select top(len(mycol)) i = row_number() over(order by (select null))
    from sys.all_objects,sys.all_objects) tally
  where substring(mycol,i,1) like '[0-9]'
) r
0

Use following function to extract alphabets from string

CREATE FUNCTION dbo.udf_GetAlphabets
(@strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
DECLARE @intAlpha INT
SET @intAlpha = PATINDEX('%[a-zA-Z]%', @strAlphaNumeric)
BEGIN
WHILE @intAlpha > 0
BEGIN
SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
SET @intAlpha = PATINDEX('%[a-zA-Z]%', @strAlphaNumeric )
END
END
RETURN ISNULL(@strAlphaNumeric,0)
END
GO

Use following function to extract numbers from string

CREATE FUNCTION dbo.udf_GetNumeric
(@strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
DECLARE @intAlpha INT
SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric)
BEGIN
WHILE @intAlpha > 0
BEGIN
SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric )
END
END
RETURN ISNULL(@strAlphaNumeric,0)
END
GO

Use following query to extract both in a single command:

SELECT dbo.udf_GetNumeric(column_name) Number, dbo.udf_GetAlphabets(column_name) Chars
from table_name

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.