14

I have a column that contains numbers and other string values (like "?", "???", etc.)

Is it possible to add an "is number" condition to the where clause in SQLite? Something like:

select * from mytable where isnumber(mycolumn)
Chin
  • 19,717
  • 37
  • 107
  • 164
  • Use a regular expression. sqllite support those according to this. http://stackoverflow.com/questions/5071601/how-do-i-use-regex-in-a-sqlite-query – Dan Bracuk Sep 11 '15 at 17:22
  • Are those numbers actual numbers, or strings containing a number? – CL. Sep 12 '15 at 08:17

10 Answers10

23

From the documentation,

The typeof(X) function returns a string that indicates the datatype of the expression X: "null", "integer", "real", "text", or "blob".

You can use where typeof(mycolumn) = "integer"

Adobe
  • 12,967
  • 10
  • 85
  • 126
Vamsi Prabhala
  • 48,685
  • 4
  • 36
  • 58
16

You could try something like this also:

select * from mytable where printf("%d", field1) = field1;

In case your column is text and contains numeric and string, this might be somewhat helpful in extracting integer data.

Example:

CREATE TABLE mytable (field1 text);
insert into mytable values (1);
insert into mytable values ('a');

select * from mytable where printf("%d", field1) = field1;
field1
----------
1
zedfoxus
  • 35,121
  • 5
  • 64
  • 63
9
SELECT * 
FROM mytable
WHERE columnNumeric  GLOB '*[0-9]*'
Meysam Chegini
  • 942
  • 12
  • 12
8
select * from mytable where abs(mycolumn) <> 0.0 or mycolumn = '0'

http://sqlfiddle.com/#!5/f1081/2

Based on this answer

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • 1
    This not reliable, e.g. this non-numeric string: `select abs('1abc235xx');` will return 1, so is considered numeric by using abs(mycolumn) method. Unless you can absolutely guarantee that your string data never starts with a numeral. – rayzinnz Mar 27 '18 at 00:18
  • 1
    If mycolumn has 0 values, this condition eliminates those values. I do not think this is correct. – Joe C May 03 '18 at 18:19
8

To test whether the column contains exclusively an integer with no other alphanumeric characters, use:

NOT myColumn GLOB '*[^0-9]*' AND myColumn LIKE '_%'

I.e., we test whether the column contains anything else than a digit and invert the result. Additionally we test whether it contains at least one character.

Note that GLOB '*[0-9]*' will find digits nested between other characters as well. The function typeof() will return 'text' for a column typed as TEXT, even if the text represents a number. As @rayzinnz mentioned, the abs() function is not reliable as well.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • 1
    This is the best option, it seems to be the only one that is able to handle numbers left-padded with naughts. It doesn't handle negative numbers, but that can easily be checked with an or with substring. – v010dya Dec 14 '21 at 14:02
2

For integer strings, test whether the roundtrip CAST matches the original string:

SELECT * FROM mytable WHERE cast(cast(mycolumn AS INTEGER) AS TEXT) = mycolumn

For consistently-formatted real strings (for example, currency):

SELECT * FROM mytable WHERE printf("%.2f", cast(mycolumn AS REAL)) = mycolumn

Input values:

  • Can't have leading zeroes
  • Must format negatives as -number rather than (number).
DenverCR
  • 1,301
  • 1
  • 7
  • 6
2

As SQLite and MySQL follow the same syntax and loose datatypes.

The query below is also possible

SELECT 
   <data>
 , (
     LENGTH(CAST(<data> AS UNSIGNED))
   )
     =
  CASE WHEN CAST(<data> AS UNSIGNED) = 0
  THEN CAST(<data> AS UNSIGNED)
  ELSE (LENGTH(<data>)
  ) END AS is_int;

Note the <data> is BNF you would have the replace those values.

This answer is based on mine other answer

Running SQLite demo

Raymond Nijland
  • 11,488
  • 2
  • 22
  • 34
0

You can use the result of the function CAST( field as INTEGER) for numbers greater than zero and the simple condition like '0' per numbers equal to zero

SELECT *
FROM tableName
WHERE CAST(fieldName AS INTEGER) > 0
UNION
SELECT *
FROM tableName
WHERE fieldName like '0';
Seb
  • 525
  • 1
  • 5
  • 25
0

This answer is comprehensive and eliminates the shortcomings of all other answers. The only caveat is that it isn't sql standard... but neither is SQLite. If you manage to break this code please comment below, and I will patch it.

Figured this out accidentally. You can check for equality with the CAST value.

CASE   {TEXT_field}
    WHEN CAST({TEXT_field} AS INTEGER)                THEN 'Integer'     -- 'Number'
    WHEN CAST({TEXT_field} AS REAL)                   THEN 'Real'        -- 'Number'
    ELSE                                                   'Character'
END

OR

CASE   
    WHEN {TEXT_field} = CAST({TEXT_field} AS INTEGER) THEN 'Integer'     --'Number'
    WHEN {TEXT_field} = CAST({TEXT_field} AS Real)    THEN 'Real'        --'Number'
    ELSE                                                   'Character'
END

(It's the same thing just different syntax.)

  • Note the order of execution. REAL must come after INTEGER.
  • Perhaps their is some implicit casting of values prior to checking for equality so that the right-side is re-CAST to TEXT before comparison to left-side.

Updated for comment: @SimonWillison
I have added a check for 'Real' values
'1 frog' evaluated to 'Character' for me; which is correct
'0' evaluated to 'Integer' for me; which is correct

I am using SQLite version 3.31.1 with python sqlite3 version 2.6.0. The python element should not affect how a query executes.

DonkeyKong
  • 1,005
  • 14
  • 18
  • Worth noting that `cast('1.2' as integer)` returns 1, so this won't differentiate between integers and floats. Likewise `cast('1 frog' as integer)` also returns 1. And `cast('0' as integer)` returns 0, the same value as `cast('text' as integer)` – Simon Willison Sep 25 '20 at 21:45
  • @SimonWillison I tested your assertions and none of my results concurred with yours. I updated the answer to test for REAL/float, but previously a float would return a False - not True - value when converted to integer and checked for equality with the input. I'm pretty sure you made these assertions without actually checking for truthfulness using the posted code. If you can adjust your comment to represent truthful assertions that would be helpful for the community. I added a note describing the possible underlying behavior which makes this solution valid. – DonkeyKong Oct 02 '20 at 16:17
0

Just adding this in case it helps people quickly test various permutations. For me: the round-trip cast int|text -> int -> text approach from DenverCR works in enough cases to be useful for my use-cases.

WITH
    def(input,expected) AS ( VALUES
        (0,             true),
        ('0',           true),
        ('00',          true),
        (1,             true),
        ('1',           true),
        (10,            true),
        ('10',          true),
        ('a',           false),
        (1.1,           false),
        ('1.1',         false),
        (-1,            true),
        ('-1',          true),
        ('1abc235xx',   false),
        ('1 frog',      false),
        ('7up',         false),
        ('up4it',       false),
        (NULL,          false)),
    test(input, expected, result) AS (
        SELECT 
            input, expected,
            CAST(CAST(input AS INTEGER) AS TEXT)
        FROM def)
SELECT input, result, result==input, expected FROM test
input result result==input expected
0 0 1 1
0 0 1 1
00 0 0 1
1 1 1 1
1 1 1 1
10 10 1 1
10 10 1 1
a 0 0 0
1.1 1 0 0
1.1 1 0 0
-1 -1 1 1
-1 -1 1 1
1abc235xx 1 0 0
1 frog 1 0 0
7up 7 0 0
up4it 0 0 0
0
monojohnny
  • 5,894
  • 16
  • 59
  • 83