73

Is there an easy way to figure out if a varchar is a number?

Examples:

abc123 --> no number

123 --> yes, its a number

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
grady
  • 12,281
  • 28
  • 71
  • 110
  • 4
    `isnumeric` but it [has a lot of quirks](https://connect.microsoft.com/SQLServer/feedback/details/302466/isnumeric-returns-true-for-and) – Martin Smith Jan 05 '11 at 11:01
  • 1
    I added a link above to a Microsoft Connect item with both some examples and the MS explanation. What number formats do you need to match? For example would you need to match something like `1e23`? – Martin Smith Jan 05 '11 at 11:03
  • In case of IsNumeric quirks, how about going for TRY_CAST(ReqCol As INT) IS NOT NULL? – Sai Oct 19 '15 at 04:39
  • 1
    pls accept damien's answer – greg121 Nov 19 '15 at 16:00

12 Answers12

155

ISNUMERIC will not do - it tells you that the string can be converted to any of the numeric types, which is almost always a pointless piece of information to know. For example, all of the following are numeric, according to ISNUMERIC:

£, $, 0d0

If you want to check for digits and only digits, a negative LIKE expression is what you want:

not Value like '%[^0-9]%'
Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • 2
    ISNUMERIC also considers a "-" to be numeric. – Atomic Star Aug 01 '14 at 13:34
  • 1
    This is a great method if you only want positive integers, but I've added some examples below if you need to allow other kinds of numeric types too if you're using SQL 2012+. – Dan Field Mar 18 '16 at 15:03
  • Definitely, better answer than ISNUMERIC! This should be the marked as correct answer. – iaforek Jun 17 '16 at 09:44
  • This and other similar answers will say the first example in the original question IS a number, because one of it's characters is a number. What about if you want to know that ALL characters are numbers. – RosieC Nov 08 '16 at 10:25
  • @RosieC - no, the example in my code, `not Value like '%[^0-9]%'`, is, exactly, that all characters are digits. It's a double-negative. It asserts that no character in `Value` is outside of the range `0-9`. – Damien_The_Unbeliever Nov 08 '16 at 11:07
  • 3
    @RosieC - or, to put it another way - `Value like '%[0-9]%'` would be "the string contains at least one digit character". `Value like '%[^0-9]%'` would be "the string contains at least one non-digit character". But, again, my test is `not Value like '%[^0-9]%'` which negates the previous test and is "the string contains no non-digit characters" – Damien_The_Unbeliever Nov 08 '16 at 13:36
38

ISNUMERIC will do

Check the NOTES section too in the article.

Sachin Shanbhag
  • 54,530
  • 11
  • 89
  • 103
  • 8
    @Grady - I think Damien's answer below is a better one as pointed out, IsNumeric does have some restrictions. Are you sure, you dont encounter any such restrictions? – Sachin Shanbhag Jan 05 '11 at 12:05
  • 3
    scroll down to notice the next answer. – greg121 Oct 19 '15 at 13:20
  • 3
    "select isnumeric('138D47')" returns 1 – marknuzz Oct 29 '15 at 01:34
  • 1
    -1 There are many things which isNumeric is true for, but which will exception if cast to INT or Float -- see https://www.brentozar.com/archive/2018/02/fifteen-things-hate-isnumeric for some examples - which shows the following 15 strings are all "IsNumeric"=1 '$' ,'£' ,',' ,'.' ,'0e+99' ,'2e2' ,'12D4' ,',1,1,1,1,1,1,1' ,'-' ,'+' ,CHAR(9) ,CHAR(10) ,CHAR(11) ,CHAR(12) ,CHAR(13) – Andrew Hill Feb 28 '18 at 20:13
  • @Nuzzolilo Because it seems to be numeric if it is an hexadeciaml thing, and d is part of hexa thing – Valentin C Jul 09 '18 at 15:36
  • I am still getting error on below query Declare @testVal as varchar(50)='ok' select CASE WHEN IsNUMERIC(@testVal)=1 THEN Cast(@testVal as numeric) ELSE 'n' End Error converting data type varchar to numeric. – Ketan Vaghasiya Oct 17 '18 at 08:26
31

You can check like this:

declare @vchar varchar(50)
set @vchar ='34343';
select case when @vchar not like '%[^0-9]%' then 'Number' else 'Not a Number' end
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Binil
  • 6,445
  • 3
  • 30
  • 40
24

Using SQL Server 2012+, you can use the TRY_* functions if you have specific needs. For example,

-- will fail for decimal values, but allow negative values
TRY_CAST(@value AS INT) IS NOT NULL 

-- will fail for non-positive integers; can be used with other examples below as well, or reversed if only negative desired
TRY_CAST(@value AS INT) > 0

-- will fail if a $ is used, but allow decimals to the specified precision
TRY_CAST(@value AS DECIMAL(10,2)) IS NOT NULL 

-- will allow valid currency
TRY_CAST(@value AS MONEY) IS NOT NULL  

-- will allow scientific notation to be used like 1.7E+3
TRY_CAST(@value AS FLOAT) IS NOT NULL 
Dan Field
  • 20,885
  • 5
  • 55
  • 71
  • 1
    This is the best solution. The solutions using %[^0-9.]% seem to return say it's a number when it contains a number even if there are also characters in the string. This TRY_CAST works for me. – RosieC Nov 08 '16 at 10:31
  • 2
    Just keep in mind it requires SQL Server 2012+ - won't work on 2008R2 or lower, and won't work in other RDBMS – Dan Field Nov 08 '16 at 13:05
9

I ran into the need to allow decimal values, so I used not Value like '%[^0-9.]%'

bluish
  • 26,356
  • 27
  • 122
  • 180
Wade73
  • 4,359
  • 3
  • 30
  • 46
  • This is not good. It would have matched with just `.`, `2.2.2...1....0`, `2.1.2` and so on. You would need a second condition to also exclude ones with more than one decimal point, and those where the decimal is not between two numbers. – The_Rafi Jun 27 '22 at 19:27
3

Wade73's answer for decimals doesn't quite work. I've modified it to allow only a single decimal point.

declare @MyTable table(MyVar nvarchar(10));
insert into @MyTable (MyVar)
values
    (N'1234')
    , (N'000005')
    , (N'1,000')
    , (N'293.8457')
    , (N'x')
    , (N'+')
    , (N'293.8457.')
    , (N'......');

-- This shows that Wade73's answer allows some non-numeric values to slip through.
select * from (
    select
        MyVar
        , case when MyVar not like N'%[^0-9.]%' then 1 else 0 end as IsNumber
    from
        @MyTable
) t order by IsNumber;

-- Notice the addition of "and MyVar not like N'%.%.%'".
select * from (
    select
        MyVar
        , case when MyVar not like N'%[^0-9.]%' and MyVar not like N'%.%.%' then 1 else 0 end as IsNumber
    from
        @MyTable
) t
order by IsNumber;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
neizan
  • 2,291
  • 2
  • 37
  • 52
2

Damien_The_Unbeliever noted that his was only good for digits

Wade73 added a bit to handle decimal points

neizan made an additional tweak as did notwhereuareat.

Unfortunately, none appear to handle negative values and they appear to have issues with a comma in the value...

Here's my tweak to pick up negative values and those with commas

declare @MyTable table(MyVar nvarchar(10));
insert into @MyTable (MyVar) 
values 
(N'1234')
, (N'000005')
, (N'1,000')
, (N'293.8457')
, (N'x')
, (N'+')
, (N'293.8457.')
, (N'......')
, (N'.')
, (N'-375.4')
, (N'-00003')
, (N'-2,000')
, (N'3-3')
, (N'3000-')
;

-- This shows that Neizan's answer allows "." to slip through.
select * from (
select 
    MyVar
    , case when MyVar not like N'%[^0-9.]%' then 1 else 0 end as IsNumber 
from 
    @MyTable
) t order by IsNumber;

-- Notice the addition of "and MyVar not like '.'".
select * from (
select 
    MyVar
    , case when MyVar not like N'%[^0-9.]%' and MyVar not like N'%.%.%' and MyVar not like '.' then 1 else 0 end as IsNumber 
from 
    @MyTable
) t 
order by IsNumber;

--Trying to tweak for negative values and the comma
--Modified when comparison
select * from (
select 
    MyVar
    , case 
        when MyVar not like N'%[^0-9.,-]%' and MyVar not like '.' and isnumeric(MyVar) = 1 then 1
        else 0 
    end as IsNumber 
from 
    @MyTable
) t 
order by IsNumber;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
M McDonald
  • 29
  • 2
1
DECLARE @A nvarchar(100) = '12'
IF(ISNUMERIC(@A) = 1)
BEGIN
    PRINT 'YES NUMERIC'
END
bluish
  • 26,356
  • 27
  • 122
  • 180
Abhishek Jaiswal
  • 1,161
  • 12
  • 6
1

Neizan's code lets values of just a "." through. At the risk of getting too pedantic, I added one more AND clause.

declare @MyTable table(MyVar nvarchar(10));
insert into @MyTable (MyVar) 
values 
    (N'1234')
    , (N'000005')
    , (N'1,000')
    , (N'293.8457')
    , (N'x')
    , (N'+')
    , (N'293.8457.')
    , (N'......')
    , (N'.')
    ;

-- This shows that Neizan's answer allows "." to slip through.
select * from (
    select 
        MyVar
        , case when MyVar not like N'%[^0-9.]%' then 1 else 0 end as IsNumber 
    from 
        @MyTable
) t order by IsNumber;

-- Notice the addition of "and MyVar not like '.'".
select * from (
    select 
        MyVar
        , case when MyVar not like N'%[^0-9.]%' and MyVar not like N'%.%.%' and MyVar not like '.' then 1 else 0 end as IsNumber 
    from 
        @MyTable
) t 
order by IsNumber;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0

Do not forget to exclude carriage returns from your data!

As in:

SELECT 
  Myotherval
  , CASE WHEN TRIM(REPLACE([MyVal], char(13) + char(10), '')) not like '%[^0-9]%' and RTRIM(REPLACE([MyVal], char(13) + char(10), '')) not like '.' and isnumeric(REPLACE([MyVal], char(13) + char(10), '')) = 1 THEN 'my number: ' +  [MyVal]
             ELSE ISNULL(Cast([MyVal] AS VARCHAR(8000)), '')
        END AS 'MyVal'
FROM MyTable
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mario Levesque
  • 1,017
  • 13
  • 13
0

In case you want to add a constraint on a field:

Positive integer with fixed length

ALTER TABLE dbo.BankBranchType  
ADD CONSTRAINT CK_TransitNumberMustBe5Digits 
CHECK (TransitNumber NOT like '%[^0-9]%'  
       AND LEN(TransitNumber) = 5)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Igleo
  • 1
0

To check the Number, Currency, and Amount, use the below SQL fragment.

@value NOT LIKE '%[^0-9.,]%'

For a quick win, refer to the below example:

Function example:

CREATE FUNCTION [dbo].[fnCheckValueIsNumber](
    @value NVARCHAR(255)=NULL
)RETURNS INT  AS BEGIN
    DECLARE @ReturnValue INT=0
    IF EXISTS (SELECT * WHERE @value NOT LIKE '%[^0-9.,]%') SELECT @ReturnValue=1
RETURN @ReturnValue;

Execution result

SELECT [dbo].[fnCheckValueIsNumber]('12345')
RESULT = 1

SELECT [dbo].[fnCheckValueIsNumber]('10020.25')
RESULT = 1

SELECT [dbo].[fnCheckValueIsNumber]('10,020.25')
RESULT = 1

SELECT [dbo].[fnCheckValueIsNumber]('12,345ABCD')
RESULT = 0
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Haseeb
  • 746
  • 7
  • 22