Converting varchar to int (a strange case?) - sql

I have scoured through all "Varchar to Int" posts but can't seem to find anyone with this issue (although, I am fairly new to SQL so I may be doing something fundamentally wrong):
SELECT *
FROM [TABLE]
WHERE CONVERT(INT,
CASE
WHEN NOT CONVERT(VARCHAR(12), dept_code) LIKE '%[^0-9]%' THEN 8900
END) < 9000;
It's a fairly simple query, where the goal is to filter out all the values in field "dept_code" so that only fully numeric values less than 9000 are kept; varchars and non-numeric values are fine to stay. When running the above I still get the error "Conversion failed when converting the varchar value 'E103' to data type int."
Any help would be appreciated.

You can simply this query by avoiding CASE and Regex like expression. You can use IsNumeric function to filter numeric rows and then apply the condition by converting dept_code of filtered rows to int, like below -
select * from tablex
where ISNUMERIC(dept_code) = 0 --alphanumeric code
OR(ISNUMERIC(dept_code) = 1 and Convert(int, dept_code) < 9000) -- numeric less than 9000
Example here

Use try_convert() or try_cast():
SELECT t.*
FROM [TABLE] t
WHERE TRY_CONVERT(int, dept_code) < 9000
If you want to speed this query, you can materialize a computed column and add an index:
alter table [table] add dept_code_int as (try_convert(int, dept_code)) persisted;
create index idx_table_dept_code_int on [table](dept_code_int);

You are missing an else in your case statement ... Secondly do your numeric dept_codes get to be really big ... this thing will choke on that.
SELECT *
FROM [TABLE]
WHERE CONVERT(INT,
CASE
WHEN NOT CONVERT(VARCHAR(12), dept_code) LIKE '%[^0-9]%' THEN 8900 ELSE dept_code
END) < 9000;

Try this: http://sqlfiddle.com/#!18/bb6b7/17
;WITH TABLE_ENHANCED AS
(
SELECT
t.*
, dept_code_numeric =
CASE
WHEN CONVERT(VARCHAR(12), dept_code) NOT LIKE '%[^0-9]%'
THEN CONVERT(INT, dept_code)
ELSE 0
END
FROM [TABLE] t
)
SELECT
*
FROM TABLE_ENHANCED
WHERE dept_code_numeric < 9000

Try below Script
SELECT *
FROM [TABLE]
WHERE isnumeric(dept_code)=1
and dept_code<9000;

This should work. The conversion to int is implicit.
SELECT *
FROM test
WHERE (ISNUMERIC(dept_code)=1 and dept_code<9000)
or (ISNUMERIC(dept_code) = 0)

Related

Define where condition between specified interval in SQL?

I want select between 1700300000 to 1700500000 also type of MemberID is nvarchar(50)
select * from tblmember
where cast(MenmberID as bigint) > 1700300000 and cast(MemberID as bigint) < 1700500000
error: Msg 8114, Level 16, State 5, Line 25
Error converting data type nvarchar to bigint.
Use try_cast():
select *
from tblmember
where try_cast(MemberID as bigint) > 1700300000 and
try_cast(MemberID as bigint) < 1700500000
I don't know what your data looks like, but you might be able to use string comparisons:
select *
from tblmember
where MemberID > '1700300000' and
MemberID < '1700500000'
Your problem is that you have non-numerical data in the table.
If the data is non-numerical, it fails the conversion.
In addition to Gordon's fine answers, you could try the following:
select * from tblmember
where
(case when isnumeric(MemberID) = 1 then cast(MemberID as bigint) end) > 1700300000 and
(case when isnumeric(MemberID) = 1 then cast(MemberID as bigint) end) < 1700500000
;
select * from tblmember
where case when isNumeric(MemberID) then cast(MenmberID as bigint)
else 0 end between 1700300000 and 1700500000;
something like that? (if you want/don't want the edge cases then you can play with the between -1/+1 on the values). What about 1700524234.3? Is that ok with you or does it have to be an integer?

How to sort a column in ascending order where the column contains varchar and numeric, using SQL?

Actually my column is in varchar, and it has numeric and varchar type data, i just wanted to sort numeric first then varchar type.
I refered and got this:
SELECT
...
ORDER BY
CASE
WHEN ISNUMERIC(value) = 1 THEN CONVERT(INT, value)
ELSE 9999999 -- or something huge
END,
value
It works, but why we need to use ELSE 9999999 here, instead what we can replace...?
Any solution for this...!
You could use:
SELECT *
FROM tab
ORDER BY IIF(TRY_CAST(val AS INT) IS NULL, 1, 0),TRY_CAST(val AS INT);
DBFiddle Demo
You can try this as an alternative solution.
SELECT
...
ORDER BY
ISNUMERIC(value) DESC
, CASE
WHEN ISNUMERIC(value) = 1 THEN CONVERT(INT, value)
END
,value

Filtered Out String Still Causes Error in SQL Statement

I am using SQL Server 2008 R2.
I am writing a part number generator app. Our part numbers consist of a nine digit numeric value, such as 914602001. Before creating a new number, there are several sources that need to be checked if it already exists. To try and save time, I've created a simple union of some of these sources. The union is as follows:
SELECT DISTINCT ItemNumber
FROM dbo.EngPartNumbers
WHERE (ItemNumber NOT LIKE '%[^0-9]%')
UNION
SELECT DISTINCT ValueText COLLATE SQL_Latin1_General_CP1_CI_AS AS ItemNumber
FROM [PDMWE-Bel-ArtProductsDocManagement].dbo.VariableValue AS vv
WHERE (ValueText NOT LIKE '%[^0-9]%') AND (LEN(ValueText) = 9)
The first table, EngPartNumbers, was an Excel file that was imported into SQL. It contains one column, ItemNumber, and is of varchar data type. It has to be varchar because there was a point in time where we used letters in our naming convention.
The second table is looking to our EPDM, where VariableValue is the table that stores all of the values to our variables, which is housed in the Variables table. The ValueText column is a varchar that holds all of the variable values. In my case, I'm only concerned about 9 digit numeric values, so I applied the last line:
WHERE (ValueText NOT LIKE '%[^0-9]%') AND (LEN(ValueText) = 9)
The results of the union is what I would expect; only numbers:
This is where my problem is. Because I would like to get the next available number, I want to work with the int data type, not the varchar. When I select everything from my view, CAST the column as an int, and add a WHERE clause, like so:
SELECT ItemNumber
FROM (
SELECT CAST(ItemNumber AS int) AS ItemNumber
FROM vw_PDM_Union_Items
) AS x
WHERE ItemNumber < 800900000
I get the following error:
Conversion failed when converting the nvarchar value '"SW-Revision"' to data type int.
After research, I've noticed the "SW-Revision" refers to a value of a variable, which is stored in the ValueText column of the VariableValue table. To me, this shouldn't matter since I'm looking at my view which has already filtered out this bad data. I've even tried wrapping my view in a select statement that CASTs the column as an int, like so:
SELECT CAST(ItemNumber AS int) AS ItemNumber
FROM (SELECT DISTINCT ItemNumber
FROM dbo.EngPartNumbers
WHERE (ItemNumber NOT LIKE '%[^0-9]%')
UNION
SELECT DISTINCT ValueText
COLLATE SQL_Latin1_General_CP1_CI_AS AS ItemNumber
FROM [PDMWE-Bel-ArtProductsDocManagement].dbo.VariableValue AS vv
WHERE (ValueText NOT LIKE '%[^0-9]%')
AND (LEN(ValueText) = 9)) AS item
WHERE (ItemNumber NOT LIKE '%[^0-9]%')
but I still get the same error. Why is SQL acting this way? What is happening in the background that causes it to look at the original table? If anyone can shed light on this situation and give me a better way to accomplish this, it would be appreciated. For manipulation sake, I would like to work with an int column, rather than a varchar.
Thank you in advance.
You can recreate this error very easily
SELECT *
FROM ( SELECT ValueText
FROM (VALUES ('A'), ('1')) t (ValueText)
WHERE t.ValueText NOT LIKE '%[^0-9]%'
) t
WHERE ValueText < 10;
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'A' to data type int.
The reason is because regardless of whether you use a view, or just a normal query, you cannot control the order in which SQL Server applies the WHERE predicates.
Because of the error, we can't see what SQL server is doing by inspecting the execution plan, but a quick change to the query (SQL Server 2012+):
SELECT *
FROM ( SELECT ValueText
FROM (VALUES ('A'), ('1')) t (ValueText)
WHERE t.ValueText NOT LIKE '%[^0-9]%'
) t
WHERE TRY_CONVERT(INT, ValueText) < 10;
Gives the following execution plan:
You can see, that SQL Server effectively simplifies the query this up to:
SELECT ValueText
FROM (VALUES ('A'), ('1')) t (ValueText)
WHERE TRY_CONVERT(INT, ValueText) < 10;
AND t.ValueText NOT LIKE '%[^0-9]%';
It does the same with an implicit convert, so in the initial query you are just executing:
SELECT ValueText
FROM (VALUES ('A'), ('1')) t (ValueText)
WHERE ValueText < 10;
AND t.ValueText NOT LIKE '%[^0-9]%';
Therefore, you get an error when evaluating A < 10 since SQL Server tries to implicitly convert A to an integer in order to compare it with 10.
What you need to solve this is intermediate materialisation, that is to say, to force SQL Server to evaluate the subquery first, store the result, and then apply the outer predicates. This is easier said than done though. There is a connect item open to request this as a query hint, but, for now there are two main workarounds.
1. Use a temporary table/table variable/Multi step TVF to materialise the results.
DECLARE #T TABLE (ValueText INT)
INSERT #T (ValueText)
SELECT ValueText
FROM (VALUES ('A'), ('1')) t (ValueText)
WHERE t.ValueText NOT LIKE '%[^0-9]%';
SELECT *
FROM #T
WHERE ValueText < 10;
This is obviously not ideal for you since you want to use a view.
2. Use TOP 2147483647 (Massive Hack)
SELECT ValueText
FROM ( SELECT TOP 2147483647 ValueText
FROM (VALUES ('A'), ('1')) t (ValueText)
WHERE t.ValueText NOT LIKE '%[^0-9]%'
) t
WHERE ValueText < 10;
It is a hack, and not guaranteed to work (although does in most cases I have had to use it), but using TOP and a number much bigger than you need will usually force the intermediate materialization of the results.
3. Use a CASE expression to remove records
SELECT *
FROM ( SELECT ValueText = CASE WHEN ValueText NOT LIKE '%[^0-9]%' THEN ValueText END
FROM (VALUES ('A'), ('1')) t (ValueText)
WHERE t.ValueText NOT LIKE '%[^0-9]%'
) t
WHERE ValueText < 10;
Again, this works, but I can't see that it would be guaranteed to work, there is no reason a future update would not recognise that the where predicate and the case predicate are the same and optimise the case expression away.
Use bigint or decimal:
SELECT ItemNumber
FROM (SELECT CAST(ItemNumber AS decimal(38)) AS ItemNumber
FROM vw_PDM_Union_Items
) x
WHERE ItemNumber < 800900000; -- large values are treated as numeric/decimal
EDIT:
You probably have values that are larger than expected as ItemNumber. Does making the decimal size larger fix the problem?
You can check what the largest value is:
SELECT TOP 1 ItemNumber
FROM vw_PDM_Union_Items
ORDER BY LENGTH(ItemNumber) DESC, ItemNumber DESC;
I also note that you have a length restriction in the subquery. Because of the way that SQL Server works, this may get invoked after attempting to convert the value.
You can enforce the order of evaluation by using case:
SELECT ItemNumber
FROM (SELECT CAST(CASE WHEN ItemNumber NOT LIKE '%[^0-9]%'
THEN LEFT(ItemNumber, 9)
END) AS decimal(10)) AS ItemNumber
FROM vw_PDM_Union_Items
) x
WHERE ItemNumber < 800900000; -- large values are treated as numeric/decimal
I might suggest that you actually move this logic into the view.
Now have reproduced it.
select *
from (
SELECT CAST(valuetext AS int) AS ItemNumber
FROM (
select valuetext='SW-Revision'
union
select '123456789'
) AS vv
WHERE (ValueText NOT LIKE '%[^0-9]%')
) wrapper
where ItemNumber > 0;
I guess this is due to predicate pushing. As a workaround
SELECT *
FROM (
SELECT CAST(CASE WHEN valuetext NOT LIKE '%[^0-9]%' THEN valuetext END AS int) AS ItemNumber
FROM (
select valuetext='SW-Revision'
union
select '123456789'
) AS vv
WHERE (ValueText NOT LIKE '%[^0-9]%')
) wrapper
WHERE ItemNumber > 0
I'm curious if this would work...
SELECT ItemNumber
FROM (
SELECT case isnumeric(ItemNumber)
when 1 then CAST(ItemNumber AS int)
else -999999999
end
AS ItemNumber
FROM vw_PDM_Union_Items
) AS x
WHERE ItemNumber < 800900000
and ItemNumber>-999999999

SQL Server: Compare two columns

I have two columns in a SQL table as follow. I need to compare these two columns for mismatches but due to extra decimals i am getting false results. When i try to convert the first column it gives the error
"Error converting data type varchar to numeric."
How to solve this issue? The length of first column varies.
Column01(varchar) Column02(Decimal)
0.01 0.010000
0.255 0.255000
You have data in Column01 that cannot be casted to DECIMAL.
With SQL Server 2012+ I would use TRY_PARSE:
SELECT *
FROM your_table
WHERE Column02 = TRY_PARSE(Column01 AS DECIMAL(38,18));
LiveDemo
When value from column cannot be casted safely you get NULL.
For SQL Server 2008 you can use:
SELECT *
FROM #tab
WHERE (CASE
WHEN ISNUMERIC(Column01) = 1 THEN CAST(Column01 AS DECIMAL(38,18))
ELSE NULL
END) = Column02;
EDIT:
If you need it at column level use:
SELECT Column01, Column02,
CASE WHEN Column02 = TRY_PARSE(Column01 AS DECIMAL(38,18))
OR (Column02 IS NULL AND Column01 IS NULL)
THEN 'true'
ELSE 'false'
END AS [IsEqual]
FROM #tab;
LiveDemo2
You can do this using self join and conversion function
SELECT x.Column01, y.Column02
FROM table1 x, table1 y
WHERE x.Column02 = try_parse(y.Column01 as decimal(38,18))
Since I cannot comment, I like to thank lad2025 for showing live demo and introducing to data.stackexchange for composing queries
One other way of doing it:
create table #temp(col1 varchar(10),col2 decimal(10,6))
insert into #temp values(0.01,0.010000 ),(0.255,0.255000),(0.25,25),(0.555,10.0)
select * from #temp where REPLACE(REPLACE(col2,col1,''),0,'') = ''
select * from #temp where REPLACE(REPLACE(col2,col1,''),0,'') <> ''

Sql Server display decimals only if they are bigger than 0?

I have this select: select isnull(Pricea,0)-isnull(Priceb,0) as Differences
The format of the columns is decimal(12,4).
My question is: I could somehow to return decimals only if they are bigger than 0?
It seems to be confusing if the result will be for e.g 4.0000 so I would want to display the decimals only if they are bigger than 0. Is this possible?
When ceiling(Num) = floor(Num), the number is a integer
select case when ceiling(Num) = floor(Num)
then CONVERT(varchar, CAST(Num as decimal))
else CONVERT(varchar, Num)
end
It's just the kind of beeing displayed in MangementStudio for the Datatypes.
For just adapting the display kind you could do something like
Declare #a table (a decimal(12,4),b decimal(12,4))
insert into #a Values(12.45,10.45)
insert into #a Values(12.45,10.4512)
insert into #a Values(12.4512,10.4500)
Select Cast(Case when a-b<>Floor(a-b) then Cast(a-b as float) else a-b end as Varchar(30)) as Diff
from #a