-4

I need to get the first number(s) in a string:

  • 1-2-3 --> 1
  • 45-6 --> 45
  • 777 8 --> 777
  • 9ab10 --> 9

How can I do that with SQL?

kwv84
  • 943
  • 3
  • 11
  • 25
  • use `regular expression` – Bhargav Chudasama Nov 13 '17 at 11:27
  • 1
    T-SQL has rather poor *string manipulation* functionality. Is there a reason why this has to be done *in the database*? – Damien_The_Unbeliever Nov 13 '17 at 11:28
  • 1
    Possible duplicate of [Extract numbers from a text in SQL Server](https://stackoverflow.com/questions/9629880/extract-numbers-from-a-text-in-sql-server) – Frank Schmitt Nov 13 '17 at 11:29
  • 1
    `SELECT LEFT(@s, PATINDEX('%[^0-9]%', @s + ' ') - 1)`. T-SQL is bad at string manipulation, but this is just about up to what it can do. – Jeroen Mostert Nov 13 '17 at 11:35
  • Only if string starts with number. Otherwise you need to do that, cut it then do that again and make a substring from cut string up to that point. – DanteTheSmith Nov 13 '17 at 11:40
  • You might wanna do this in your back end code not DB (depending on what you need it for). And if you are gonna do it in DB (especially on more then 1 place) you could make the procedure that simply iterates over string it gets and checks if strings starts with a digit. If it does cut to the 1st nondigit if not find first digit then cut to 1st nondigit after that. Should work in any case and you can debug it step by step. – DanteTheSmith Nov 13 '17 at 11:42

4 Answers4

1

You can use function PATINDEX to search for the first non-numeric character

SELECT LEFT(s,ISNULL(NULLIF(PATINDEX('%[^0-9]%',s),0)-1,LEN(s)))
FROM
  (
    VALUES
      ('1-2-3'),
      ('45-6'),
      ('777 8'),
      ('9ab10'),
      ('123'),
      ('abc')
  ) v(s)

If string starts with non-numeric prefix then we can delete this prefix first of all

SELECT
  LEFT(s,ISNULL(NULLIF(PATINDEX('%[^0-9]%',s),0)-1,LEN(s)))
FROM
  (
    SELECT
      -- delete non-numberic prefix
      IIF(s NOT LIKE '[0-9]%',STUFF(s,1,PATINDEX('%[0-9]%',s)-1,''),s) s
    FROM
      (
        VALUES
          ('1-2-3'),
          ('45-6'),
          ('777 8'),
          ('9ab10'),
          ('123'),
          ('abc'),
          ('#12test'),
          ('No999-888')
      ) v(s)
  ) q
Sergey Menshov
  • 3,856
  • 2
  • 8
  • 19
0

see Turning a Comma Separated string into individual rows it can help you to separate the string' and then check by IsNumber

Racheli
  • 111
  • 9
0

You could also use of substring() with patindex() function in order to get first numeric values

SELECT SUBSTRING(DATA, 1, PATINDEX('%[^0-9]%', DATA)-1) FROM <table_name>

Result :

1
45
777
9
Yogesh Sharma
  • 49,870
  • 5
  • 26
  • 52
0

This can be achieved with a bit of string splitting and window functions, in a completely set based manner that utilises no User Defined Functions:

-- Test Data
declare @v table(v nvarchar(10));
insert into @v values('1-2-3'),('45-6'),('777 8'),('a9b10'),('abc9b10'),('123'),('abc');

            -- Create table with 10 rows in it
with t(t) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
            -- Self join this table 6 times to cover very large string lengths (10*10*10*10*10*10 = 1,000,000 rows) and row number to get incremental list
    ,n(n) as (select top (select max(len(v)) from @v) row_number() over (order by (select null)) from t t1,t t2,t t3,t t4,t t5,t t6)
            -- Split the strings using this list of incremental numbers and use window functions to determine:
    ,v    as (select v.v
                    ,n.n
                        -- The actual character at this position
                    ,substring(v.v,n.n,1) as l
                        -- Is it numeric?
                    ,case when substring(v.v,n.n,1) like '[0-9]' then 1 else 2 end as i
                        -- Order all the numeric values
                    ,rank() over (partition by v.v order by case when substring(v.v,n.n,1) like '[0-9]' then 1 else 2 end, n.n) as c
                        -- Group the numeric rows together by summing up the numeric checks
                    ,sum(case when substring(v.v,n.n,1) like '[0-9]' then 0 else 1 end) over (partition by v.v order by n.n) as g
              from n
                  join @v as v
                      on n.n <= len(v.v)
             )
select v.v
                -- Use a FOR XML statement to concatenate the additional values together
      ,v.l + isnull((select '' + v2.l
                    from v as v2
                    where v.v = v2.v
                        and v.n < v2.n
                        and (v2.c = 1
                            or v.g = v2.g
                            )
                    for xml path('')
                    ),'') as n
from v
where c = 1     -- Only select the first grouping of numbers
    and i = 1
order by v.v;

Output:

+---------+-----+
|    v    |  n  |
+---------+-----+
| 123     | 123 |
| 1-2-3   |   1 |
| 45-6    |  45 |
| 777 8   | 777 |
| a9b10   |   9 |
| abc9b10 |   9 |
+---------+-----+
iamdave
  • 12,023
  • 3
  • 24
  • 53