sql like operator to get the numbers only - sql

This is I think a simple problem but not getting the solution yet. I would like to get the valid numbers only from a column as explained here.
Lets say we have a varchar column with following values
ABC
Italy
Apple
234.62
2:234:43:22
France
6435.23
2
Lions
Here the problem is to select numbers only
select * from tbl where answer like '%[0-9]%' would have done it but it returns
234.62
2:234:43:22
6435.23
2
Here, obviously, 2:234:43:22 is not desired as it is not valid number.
The desired result is
234.62
6435.23
2
Is there a way to do this?

You can use the following to only include valid characters:
SQL
SELECT * FROM #Table
WHERE Col NOT LIKE '%[^0-9.]%'
Results
Col
---------
234.62
6435.23
2

You can try this
ISNUMERIC (Transact-SQL)
ISNUMERIC returns 1 when the input
expression evaluates to a valid
numeric data type; otherwise it
returns 0.
DECLARE #Table TABLE(
Col VARCHAR(50)
)
INSERT INTO #Table SELECT 'ABC'
INSERT INTO #Table SELECT 'Italy'
INSERT INTO #Table SELECT 'Apple'
INSERT INTO #Table SELECT '234.62'
INSERT INTO #Table SELECT '2:234:43:22'
INSERT INTO #Table SELECT 'France'
INSERT INTO #Table SELECT '6435.23'
INSERT INTO #Table SELECT '2'
INSERT INTO #Table SELECT 'Lions'
SELECT *
FROM #Table
WHERE ISNUMERIC(Col) = 1

Try something like this - it works for the cases you have mentioned.
select * from tbl
where answer like '%[0-9]%'
and answer not like '%[:]%'
and answer not like '%[A-Z]%'

With SQL 2012 and later, you could use TRY_CAST/TRY_CONVERT to try converting to a numeric type, e.g. TRY_CAST(answer AS float) IS NOT NULL -- note though that this will match scientific notation too (1+E34). (If you use decimal, then scientific notation won't match)

what might get you where you want in plain SQL92:
select * from tbl where lower(answer) = upper(answer)
or, if you also want to be robust for leading/trailing spaces:
select * from tbl where lower(answer) = trim(upper(answer))

Related

how to check and change custom string in sql server

i have problem in my sql query code
i have one column for my codes and structure of code like this
3digit-1to3digit-5to7digit-1to2digit
xxx-xxx-xxxxxx-xx
in code column user add code like
1-1486414-305-115 --mistake
116-500-325663-1 --ok
116-2-2244880-1 --ok
121-512-2623075-1 --ok
122-500-1944261-3 --ok
2-2651274-500-147 --mistake
1-2551671-305-147 --mistake
124-500-329130-1 --ok
how to check and fix the mistake codes.
thanks for read my problem
Alternatively, instead of a load of LIKE expressions, you could split the parts and inspect their lengths, and follow up by checking the string only contains digits and hyphens with a LIKE. As your string specifically has 4 parts, I've used PARSENAME here, rather than a "splitter" function.
SELECT *
FROM (VALUES ('1-1486414-305-115'),
('116-500-325663-1'),
('116-2-2244880-1'),
('121-512-2623075-1'),
('122-500-1944261-3'),
('2-2651274-500-147'),
('1-2551671-305-147'),
('116-ba-2244880-1'),
('124-500-329130-1'))V(Code)
CROSS APPLY (VALUES(PARSENAME(REPLACE(V.code,'-','.'),4),
PARSENAME(REPLACE(V.code,'-','.'),3),
PARSENAME(REPLACE(V.code,'-','.'),2),
PARSENAME(REPLACE(V.code,'-','.'),1))) PN(P1, P2, P3, P4)
WHERE LEN(P1) != 3
OR NOT(LEN(P2) BETWEEN 1 AND 3)
OR NOT(LEN(P3) BETWEEN 5 AND 7)
OR NOT(LEN(P4) BETWEEN 1 AND 2)
OR V.Code LIKE '%[^0-9\-]%' ESCAPE '\';
What a pain, because SQL Server does not support regular expressions.
One method is 6 like comparisons:
where col like '[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]'
Otherwise, you could count the -s, check the positions, and characters. So:
where col not like '[^-0-9]' and -- only has digits and -
col not like '%-%-%-%-%' and -- does not have 4 hyphens
col like '___-___-%-%[0-9]' and -- first two hyphens in the right place and ends in digit
'-' in (substring(col, 14, 1), substring(col, 15, 1), substring(col, 16, 1)) -- last hyphen in the right place
Here is the complete code which can achieve the required result
1) store the splitted string into a table #myvalues (I have written a solution to split a string into many rows using Recusrsivity in this Link )
2) Store the conditions in Table #tabcheck (length of each string)
3) Make a jointure between #myvalues and #tabcheck to get the result
declare #str as nvarchar(max)
set #str='116-500-325663-1';
declare #separator as char(1)
set #separator='-';
declare #tabcheck as table(id int,fromval int ,toval int)
insert into #tabcheck values(1,3,3),(2,1,3),(3,5,7),(4,1,2);
declare #myvalues as table(id int identity(1,1),myval varchar(100));
with cte as(
select #str [mystr],
cast(1 as int) [Start],
charindex(#separator,#str)as Nd
union all
select substring(#str,nd+1,len(#str)),cast(Nd+1 as int),charindex(#separator,#str,Nd+1) from cte
where nd>0
)
insert into #myvalues(myval)
select case when nd>0 then substring(#str,start,Nd-start)
else substring(#str,start,len(#str)) end [splitted]
from cte OPTION (MAXRECURSION 1000);
declare #result as int;
with mytab as(
select t1.id,t1.myval,len(t1.myval) L,t2.fromval,t2.toval,
case when len(t1.myval)>=t2.fromval and len(t1.myval)<=t2.toval then 1 else 0 end [result]
from #myvalues t1 inner join #tabcheck t2 on t1.id=t2.id)
select #result=count(1) from mytab where result=0 ;
select case #result when 0 then 'OK' else 'Mistake' end [result]

Select numbers with more than 4 decimal places

I have a SQL table with one float column populated with values like these:
1.4313
3.35
2.55467
6.22456
3.325
I need to select rows containing only values with more than 4 decimals. In this case, the select must return:
2.55467
6.22456
Ideas? Thanks!
This is what I have tried so far
select *
from table
where CAST(LATITUDE AS DECIMAL(10,5)) - LATITUDE = 0
DECLARE #tbl TABLE (val float)
INSERT INTO #tbl SELECT 1234.567
INSERT INTO #tbl SELECT 1234.5678
INSERT INTO #tbl SELECT -1234.5678
INSERT INTO #tbl SELECT 1234.56789
SELECT *
from #tbl
where (((val*10000) - CONVERT(INT,(val*10000))) <> 0)
Why cant we make it simple by this query:-
SELECT * FROM table WHERE val LIKE '%.____%'
This selects what we want
Another solution also:
SELECT * from table
where (round(value,2) - round(value,4) <> 0)
Given answers did not work for me with MaxDb, but this did:
where FLOOR(value * 10000) != value * 10000
Source
Reduce/Increase 0`s for less/more precision.
This works on Postgres 11:
SELECT * FROM mytable WHERE mycolumn != ROUND(mycolumn::numeric,2)
http://sqlfiddle.com/#!3/abadc/3/0
Seems like something like this should work...
all it does is convert the number to an integer to drop off decimals after multiplying it * 10 to power of decimals you need then it compares that int version of the number to the base number after it too was multiplied by 10 to the power of # of decimals.
If the numbers don't match, then you have decimals beyond 4. If they do match, then it was 4 or fewer.
Select *
from foo
where cast(myNum*power(10,4) as int) <> myNum*power(10,4)
Please try something like:
select * from table
where RIGHT(CAST(value as DECIMAL(10,5)), value), 1) != 0
SELECT *
FROM table WHERE
(abs(val)*100000)%10 <> 0
It's an older question but it checks out.
select val
from table
where ((val * 100) % 1) > 0
Change 100 to your precision.
You can multiply it with 10000 and subtract it from the original number replacing . with ''.
Fiddle
select * from tablename
where replace(numcolumn,'.','') - numcolumn * 10000 > 0
Below is the Code that will check the precision for 4 decimal places:
Replace MyNum, with column you are checking for precision
Replace MyTbl, with the table you are using
Replace 4, with whatever precision you are checking for
Sql:
SELECT MyNum
, LEN(CAST (MyNum AS CHAR))
, -------1. length of decimal number, after conversion to CHAR
CHARINDEX('.', CAST (MyNum AS CHAR))
, ---2.length of numbers after the '.'
LEN(CAST (MyNum AS CHAR)) - CHARINDEX('.', CAST (MyNum AS CHAR)) -----subtracting 1-2, to get the length of numbers after decimal point '.'
FROM MyTbl
WHERE LEN(CAST(MyNum AS CHAR)) - CHARINDEX('.', CAST(MyNum AS CHAR)) > 4; --checking if there are more than 4 numbers after the decimal point '.'
Cast the number as text
Split the text using '.' as separator
Use the 2nd index and apply a length
Filter
--i.e. with postgreSQL.
--1)
select data_numeric, length(arr[2]) as str_length
from (
select data_numeric, regexp_split_to_array(data_numeric::text, '\.') as arr from TABLE
) temp;
--2)
with t1 as (
select data_numeric, regexp_split_to_array(data_numeric::text, '\.') as arr from TABLE
), t2 as (
select data_numeric, arr[2] as decimals, length(arr[2]) as length from t1
)
select * from t2;
Select numbers with more than 2 decimal places:
I had an example, where ((abs(val)*100) and CONVERT(INT,(abs(val)*100)) for value "2.32" in float type column returned two different values.
(abs(2.32)*100) = 232
CONVERT(INT,(abs(2.32)*100)) = 231
That caused wrong select query answers in case for comparing to 0.
I suppose that MSSQL CONVERT() function round numbers in such way that for some float number cases, posted solution would not work.
Here is how I did it for more than 2 decimal places:
DECLARE #tbl TABLE (val float)
INSERT INTO #tbl SELECT 2.32
INSERT INTO #tbl SELECT 1234.54
INSERT INTO #tbl SELECT 1234.545
INSERT INTO #tbl SELECT 1234.5456
INSERT INTO #tbl SELECT 1234.54567
select * from #tbl where abs(val-round((val),2)) > 0.001
You can use the scale function, since postgresql 9.6.
select LATITUDE FROM TABLE where scale(LATITUDE) > 4;
If your data type is float you will get SQL Error [42883]: ERROR: function scale(double precision) does not exist. Below would fix it.
select LATITUDE FROM TABLE where scale(cast(LATITUDE as numeric)) > 4;
This is the simplest solution, Use WITH (NOLOCK) if it's necessary in your case otherwise you can remove it. This will return the records having at least 4 decimal points in ColumnName table.
SELECT * FROM TableName WITH (NOLOCK) WHERE ColumnName LIKE '%.____'

Select substring of retrieved value

Consider the following table:
MyValue
--------------------
123
122_DELETED
45670
42425
43_NO_VIEW
4365463_CORRUPT
53
4335_NO_VIEW_ALLOWED
I'm trying to get only the numbers returned. In other words: string everything after the first underscore (_):
select
left(MyValue, charindex(('_', MyValue)-1)
from
DB.Table
However, this returns the error Invalid length parameter passed to the LEFT or SUBSTRING function. I believe this is because the value is NULL in case the current value has no underscore (for instance, 123).
How can I account for this exception? Any help is greatly appreciated.
I am on SQL Server 2008.
try this!
select myval,case when myval like '%[_]%' then
substring(myval,1,patindex('%[_]%',myval)-1) else myval end from t
##DEMO USING PATINDEX
##DEMO USING CHARINDEX
select SUBSTRING(MyValue,CHARINDEX('_',MyValue)+1,LEN(MyValue)) from DB.Table
try this
select
case when MyValue is null then '' --if null return empty string
when charindex('_', MyValue) > 0 then
left(MyValue, charindex('_', MyValue)-1)
else
MyValue --Return the field value if an underscore is not present
end as Result
from
DB.Table
Try out this one:
declare #st varchar(20)
set #st ='4365463_CORRU'
select #st, SUBSTRING(#st,CHARINDEX('_',#st)-LEN(#st),LEN(#st))
Check this .
declare #t table (myvalue varchar(50))
insert into #t
values ( '123'),('122_DELETED'),('45670'),('42425'),('43_NO_VIEW'),('4365463_CORRUPT'),('53'),('4335_NO_VIEW_ALLOWED')
select * from #t
;With cte as
(
select CHARINDEX( '_', myvalue)+1 d , myvalue from #t
)
select SUBSTRING(myvalue,d,LEN(myvalue) ) from cte

SQL: counting set elements after truncation

select THING from FOLDER where THING like '%-%' and ...
which returns things like:
abc097-01
def982-02
ghi13344-01
ghi13344-02
Now I need to change this query so that I can count distinct names, after truncating the part after the '-'.
So for the example above, I'd get 3 as a result ("abc097","def982" and "ghi13344").
How can I write that in SQL?
Declare #tmp table (field nvarchar(100))
insert into #tmp values ('abc097-01')
insert into #tmp values ('def982-02')
insert into #tmp values ('ghi13344-01')
insert into #tmp values ('ghi13344-02')
select COUNT(*),SUBSTRING(field,0,CHARINDEX('-',field))
from #tmp
group by SUBSTRING(field,0,CHARINDEX('-',field))
select distinct(SUBSTRING(field,0,CHARINDEX('-',field)))
from #tmp -- to get distinct records.
You can use CHARINDEX or PATINDEX functions to find the location of the '-' and then use the LEFT or SUBSTRING functions:
select COUNT(DISTINCT SUBSTRING(THING, 1, PATINDEX('%-%', THING) - 1))
from FOLDER
where THING like '%-%'

Define variable to use with IN operator (T-SQL)

I have a Transact-SQL query that uses the IN operator. Something like this:
select * from myTable where myColumn in (1,2,3,4)
Is there a way to define a variable to hold the entire list "(1,2,3,4)"? How should I define it?
declare #myList {data type}
set #myList = (1,2,3,4)
select * from myTable where myColumn in #myList
DECLARE #MyList TABLE (Value INT)
INSERT INTO #MyList VALUES (1)
INSERT INTO #MyList VALUES (2)
INSERT INTO #MyList VALUES (3)
INSERT INTO #MyList VALUES (4)
SELECT *
FROM MyTable
WHERE MyColumn IN (SELECT Value FROM #MyList)
DECLARE #mylist TABLE (Id int)
INSERT INTO #mylist
SELECT id FROM (VALUES (1),(2),(3),(4),(5)) AS tbl(id)
SELECT * FROM Mytable WHERE theColumn IN (select id from #mylist)
There are two ways to tackle dynamic csv lists for TSQL queries:
1) Using an inner select
SELECT * FROM myTable WHERE myColumn in (SELECT id FROM myIdTable WHERE id > 10)
2) Using dynamically concatenated TSQL
DECLARE #sql varchar(max)
declare #list varchar(256)
select #list = '1,2,3'
SELECT #sql = 'SELECT * FROM myTable WHERE myColumn in (' + #list + ')'
exec sp_executeSQL #sql
3) A possible third option is table variables. If you have SQl Server 2005 you can use a table variable. If your on Sql Server 2008 you can even pass whole table variables in as a parameter to stored procedures and use it in a join or as a subselect in the IN clause.
DECLARE #list TABLE (Id INT)
INSERT INTO #list(Id)
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
SELECT
*
FROM
myTable
JOIN #list l ON myTable.myColumn = l.Id
SELECT
*
FROM
myTable
WHERE
myColumn IN (SELECT Id FROM #list)
Use a function like this:
CREATE function [dbo].[list_to_table] (#list varchar(4000))
returns #tab table (item varchar(100))
begin
if CHARINDEX(',',#list) = 0 or CHARINDEX(',',#list) is null
begin
insert into #tab (item) values (#list);
return;
end
declare #c_pos int;
declare #n_pos int;
declare #l_pos int;
set #c_pos = 0;
set #n_pos = CHARINDEX(',',#list,#c_pos);
while #n_pos > 0
begin
insert into #tab (item) values (SUBSTRING(#list,#c_pos+1,#n_pos - #c_pos-1));
set #c_pos = #n_pos;
set #l_pos = #n_pos;
set #n_pos = CHARINDEX(',',#list,#c_pos+1);
end;
insert into #tab (item) values (SUBSTRING(#list,#l_pos+1,4000));
return;
end;
Instead of using like, you make an inner join with the table returned by the function:
select * from table_1 where id in ('a','b','c')
becomes
select * from table_1 a inner join [dbo].[list_to_table] ('a,b,c') b on (a.id = b.item)
In an unindexed 1M record table the second version took about half the time...
I know this is old now but TSQL => 2016, you can use STRING_SPLIT:
DECLARE #InList varchar(255) = 'This;Is;My;List';
WITH InList (Item) AS (
SELECT value FROM STRING_SPLIT(#InList, ';')
)
SELECT *
FROM [Table]
WHERE [Item] IN (SELECT Tag FROM InList)
Starting with SQL2017 you can use STRING_SPLIT and do this:
declare #myList nvarchar(MAX)
set #myList = '1,2,3,4'
select * from myTable where myColumn in (select value from STRING_SPLIT(#myList,','))
DECLARE #myList TABLE (Id BIGINT) INSERT INTO #myList(Id) VALUES (1),(2),(3),(4);
select * from myTable where myColumn in(select Id from #myList)
Please note that for long list or production systems it's not recommended to use this way as it may be much more slower than simple INoperator like someColumnName in (1,2,3,4) (tested using 8000+ items list)
slight improvement on #LukeH, there is no need to repeat the "INSERT INTO":
and #realPT's answer - no need to have the SELECT:
DECLARE #MyList TABLE (Value INT)
INSERT INTO #MyList VALUES (1),(2),(3),(4)
SELECT * FROM MyTable
WHERE MyColumn IN (SELECT Value FROM #MyList)
No, there is no such type. But there are some choices:
Dynamically generated queries (sp_executesql)
Temporary tables
Table-type variables (closest thing that there is to a list)
Create an XML string and then convert it to a table with the XML functions (really awkward and roundabout, unless you have an XML to start with)
None of these are really elegant, but that's the best there is.
If you want to do this without using a second table, you can do a LIKE comparison with a CAST:
DECLARE #myList varchar(15)
SET #myList = ',1,2,3,4,'
SELECT *
FROM myTable
WHERE #myList LIKE '%,' + CAST(myColumn AS varchar(15)) + ',%'
If the field you're comparing is already a string then you won't need to CAST.
Surrounding both the column match and each unique value in commas will ensure an exact match. Otherwise, a value of 1 would be found in a list containing ',4,2,15,'
As no one mentioned it before, starting from Sql Server 2016 you can also use json arrays and OPENJSON (Transact-SQL):
declare #filter nvarchar(max) = '[1,2]'
select *
from dbo.Test as t
where
exists (select * from openjson(#filter) as tt where tt.[value] = t.id)
You can test it in
sql fiddle demo
You can also cover more complicated cases with json easier - see Search list of values and range in SQL using WHERE IN clause with SQL variable?
This one uses PATINDEX to match ids from a table to a non-digit delimited integer list.
-- Given a string #myList containing character delimited integers
-- (supports any non digit delimiter)
DECLARE #myList VARCHAR(MAX) = '1,2,3,4,42'
SELECT * FROM [MyTable]
WHERE
-- When the Id is at the leftmost position
-- (nothing to its left and anything to its right after a non digit char)
PATINDEX(CAST([Id] AS VARCHAR)+'[^0-9]%', #myList)>0
OR
-- When the Id is at the rightmost position
-- (anything to its left before a non digit char and nothing to its right)
PATINDEX('%[^0-9]'+CAST([Id] AS VARCHAR), #myList)>0
OR
-- When the Id is between two delimiters
-- (anything to its left and right after two non digit chars)
PATINDEX('%[^0-9]'+CAST([Id] AS VARCHAR)+'[^0-9]%', #myList)>0
OR
-- When the Id is equal to the list
-- (if there is only one Id in the list)
CAST([Id] AS VARCHAR)=#myList
Notes:
when casting as varchar and not specifying byte size in parentheses the default length is 30
% (wildcard) will match any string of zero or more characters
^ (wildcard) not to match
[^0-9] will match any non digit character
PATINDEX is an SQL standard function that returns the position of a pattern in a string
DECLARE #StatusList varchar(MAX);
SET #StatusList='1,2,3,4';
DECLARE #Status SYS_INTEGERS;
INSERT INTO #Status
SELECT Value
FROM dbo.SYS_SPLITTOINTEGERS_FN(#StatusList, ',');
SELECT Value From #Status;
Most of these seem to focus on separating-out each INT into its own parenthetical, for example:
(1),(2),(3), and so on...
That isn't always convenient. Especially since, many times, you already start with a comma-separated list, for example:
(1,2,3,...) and so on...
In these situations, you may care to do something more like this:
DECLARE #ListOfIds TABLE (DocumentId INT);
INSERT INTO #ListOfIds
SELECT Id FROM [dbo].[Document] WHERE Id IN (206,235,255,257,267,365)
SELECT * FROM #ListOfIds
I like this method because, more often than not, I am trying to work with IDs that should already exist in a table.
My experience with a commonly proposed technique offered here,
SELECT * FROM Mytable WHERE myColumn IN (select id from #mylist)
is that it induces a major performance degradation if the primary data table (Mytable) includes a very large number of records. Presumably, that is because the IN operator’s list-subquery is re-executed for every record in the data table.
I’m not seeing any offered solution here that provides the same functional result by avoiding the IN operator entirely. The general problem isn’t a need for a parameterized IN operation, it’s a need for a parameterized inclusion constraint. My favored technique for that is to implement it using an (inner) join:
DECLARE #myList varchar(50) /* BEWARE: if too small, no error, just missing data! */
SET #myList = '1,2,3,4'
SELECT *
FROM myTable
JOIN STRING_SPLIT(#myList,',') MyList_Tbl
ON myColumn = MyList_Tbl.Value
It is so much faster because the generation of the constraint-list table (MyList_Tbl) is executed only once for the entire query execution. Typically, for large data sets, this technique executes at least five times faster than the functionally equivalent parameterized IN operator solutions, like those offered here.
I think you'll have to declare a string and then execute that SQL string.
Have a look at sp_executeSQL