I have a table with data as below:
Id
Value
7E38AD4A-4C6E-40D9-B805-08C1F4EE14D0
1,370
E48A0A4A-6E6C-41A8-B379-0E14452938C6
1,200
55FF871D-CB78-4A38-8817-154DFC723E06
1,124
915D7C86-71B9-4BC5-99EA-18B2FAB559FF
1,245
A80F6396-222C-4E9C-9782-298E43609586
1,000
55EE03CC-0825-43E8-A792-33BE5F01C594
1,029
51176178-7CCD-4984-82EE-342574D85742
1,206
C2FBFD1D-469D-43FF-9D9E-3F56BE9BCF34
1,243
I want to convert the value column (of type nvarchar(50)) to decimal(18, 2), but I get the following error :
Error converting data type nvarchar to numeric
while executing the query.
Here is my T-SQL:
SELECT
Id,
IIF(ISNUMERIC([Value]) = 1, CAST([Value] AS DECIMAL(18, 2)), 0) AS Value
FROM
[dbo].[AssetLD]
Please help, thanks
Unfortunately, conversions to decimal do not accept a , separator for thousands.
However a money conversion does accept it.
Note that you should always use TRY_CONVERT/CAST, rather than ISNUMERIC which has many issues
SELECT
Id,
ISNULL(CAST(TRY_CONVERT(money, [Value], 1) AS DECIMAL(18, 2)), 0) AS Value
FROM
[dbo].[AssetLD];
An even better solution is to use TRY_PARSE, which will use the correct formatting for a particular culture.
For example, if , actually means a decimal point (for example in de-DE culture), you can do
SELECT
Id,
ISNULL(TRY_PARSE([Value] AS DECIMAL(18, 2) USING 'de-DE'), 0) AS Value
FROM
[dbo].[AssetLD];
db<>fiddle
I have a table ConsoleGames wherein all columns are of type varchar(50). When I try to create a new table console_games by amending existing datatypes by using the query:
CREATE TABLE console_games
(
game_rank integer,
game_name varchar(1200),
platform_name varchar(1200),
game_year integer,
genre varchar(200),
publisher varchar(1200),
na_sales float,
eu_sales float,
jp_sales float,
other_sales float
)
INSERT INTO console_games
SELECT *
FROM [dbo].[RAWConsoleGames]
I get the following error message:
Msg 245, Level 16, State 1, Line 17
Conversion failed when converting the varchar value 'SAT' to data type int.
When I look into the data in the table the value 'SAT' is in a column for which I am not changing the datatype. 'SAT' value exists in the Platform column which is of varchar type and I am not trying to change the type to int.
Any help will be appreciated.
Thanks
Clearly 'SAT' is not and will never convert to an INT.
Always best to specify the columns to insert ... things change
Now, if the source data is suspect, add a try_convert(). If the conversion fails, a null value will be returned
I don't know the column names of your source, so I substituted SomeColN
INSERT INTO console_games
SELECT try_convert(integer ,SomeCol1)
,try_convert(varchar(1200),SomeCol2)
,try_convert(varchar(1200),SomeCol3)
,try_convert(integer ,SomeCol4)
,try_convert(varchar(200) ,SomeCol5)
,try_convert(varchar(1200),SomeCol6)
,try_convert(float ,SomeCol7)
,try_convert(float ,SomeCol8)
,try_convert(float ,SomeCol9)
,try_convert(float ,SomeCol10)
FROM [dbo].[RAWConsoleGames]
Just for fun, try:
Select try_convert(int,'SAT')
Select try_convert(int,'25.25')
Select try_convert(int,'25')
You should always define the list of columns you're inserting into, and you should also always define the list of columns you're selecting from. Furthermore, I'd recommend to explicitly do any type conversions instead of leaving that up to SQL Server - if you do it yourself, you know when and what you're doing.
So I'd write that statement like this:
-- **DEFINE** the list of columns you're inserting into
INSERT INTO console_games (rank, name, Platform, year, genre, publisher,
sales, eu_sales, jp_sales, other_sales)
-- **DEFINE** the list of columns you're selecting, and any conversions
SELECT
game_rank, game_name, platform_name,
CAST(game_year AS VARCHAR(50)), genre,
publisher,
CAST(na_sales AS VARCHAR(50)),
CAST(eu_sales AS VARCHAR(50)),
CAST(jp_sales AS VARCHAR(50)),
CAST(other_sales AS VARCHAR(50))
FROM
[dbo].[RAWConsoleGames]
Why would the following query return "Error converting data type varchar to bigint"? Doesn't IsNumeric make the CAST safe? I've tried every numeric datatype in the cast and get the same "Error converting..." error. I don't believe the size of the resulting number is a problem because overflow is a different error.
The interesting thing is, in management studio, the results actually show up in the results pane for a split second before the error comes back.
SELECT CAST(myVarcharColumn AS bigint)
FROM myTable
WHERE IsNumeric(myVarcharColumn) = 1 AND myVarcharColumn IS NOT NULL
GROUP BY myVarcharColumn
Any thoughts?
IsNumeric returns 1 if the varchar value can be converted to ANY number type. This includes int, bigint, decimal, numeric, real & float.
Scientific notation could be causing you a problem. For example:
Declare #Temp Table(Data VarChar(20))
Insert Into #Temp Values(NULL)
Insert Into #Temp Values('1')
Insert Into #Temp Values('1e4')
Insert Into #Temp Values('Not a number')
Select Cast(Data as bigint)
From #Temp
Where IsNumeric(Data) = 1 And Data Is Not NULL
There is a trick you can use with IsNumeric so that it returns 0 for numbers with scientific notation. You can apply a similar trick to prevent decimal values.
IsNumeric(YourColumn + 'e0')
IsNumeric(YourColumn + '.0e0')
Try it out.
SELECT CAST(myVarcharColumn AS bigint)
FROM myTable
WHERE IsNumeric(myVarcharColumn + '.0e0') = 1 AND myVarcharColumn IS NOT NULL
GROUP BY myVarcharColumn
Background:
I use a 3rd Party database which constantly recieves new data from other 3rd party vendors.
It's my job to parse out a horrendous varchar field used to store results.
We want to parse out as much data as possible, and this solution shows you how you can "clean up" the data so that valid entries do not get overlooked.
Some results are free-texted.
Some are Enumerations (Yes, No, Blue, Black, etc..).
Some are Integers.
Others use decimals.
Many are percentages, which if converted to an integer could trip you up later.
If I need to query for a given decimal range (say -1.4 to 3.6 where applicable) my options are limited.
I updated my query below to use #GMastros suggestion to append 'e0'.
Thanks #GMastros, this saved me an extra 2 lines of logic.
Solution:
--NOTE: I'd recommend you use this to convert your numbers and store them in a separate table (or field).
-- This way you may reuse them when when working with legacy/3rd-party systems, instead of running these calculations on the fly each time.
SELECT Result.Type, Result.Value, Parsed.CleanValue, Converted.Number[Number - Decimal(38,4)],
(CASE WHEN Result.Value IN ('0', '1', 'True', 'False') THEN CAST(Result.Value as Bit) ELSE NULL END)[Bit],--Cannot convert 1.0 to Bit, it must be in Integer format already.
(CASE WHEN Converted.Number BETWEEN 0 AND 255 THEN CAST(Converted.Number as TinyInt) ELSE NULL END)[TinyInt],
(CASE WHEN Converted.Number BETWEEN -32768 AND 32767 AND Result.Value LIKE '%\%%' ESCAPE '\' THEN CAST(Converted.Number / 100.0 as Decimal(9,4)) ELSE NULL END)[Percent],
(CASE WHEN Converted.Number BETWEEN -32768 AND 32767 THEN CAST(Converted.Number as SmallInt) ELSE NULL END)[SmallInt],
(CASE WHEN Converted.Number BETWEEN -214748.3648 AND 214748.3647 THEN CAST(Converted.Number as SmallMoney) ELSE NULL END)[SmallMoney],
(CASE WHEN Converted.Number BETWEEN -2147483648 AND 2147483647 THEN CAST(Converted.Number as Int) ELSE NULL END)[Int],
(CASE WHEN Converted.Number BETWEEN -2147483648 AND 2147483647 THEN CAST(CAST(Converted.Number as Decimal(10)) as Int) ELSE NULL END)[RoundInt],--Round Up or Down instead of Truncate.
(CASE WHEN Converted.Number BETWEEN -922337203685477.5808 AND 922337203685477.5807 THEN CAST(Converted.Number as Money) ELSE NULL END)[Money],
(CASE WHEN Converted.Number BETWEEN -9223372036854775808 AND 9223372036854775807 THEN CAST(Converted.Number as BigInt) ELSE NULL END)[BigInt],
(CASE WHEN Parsed.CleanValue IN ('1', 'True', 'Yes', 'Y', 'Positive', 'Normal') THEN CAST(1 as Bit)
WHEN Parsed.CleanValue IN ('0', 'False', 'No', 'N', 'Negative', 'Abnormal') THEN CAST(0 as Bit) ELSE NULL END)[Enum],
--I couln't use just Parsed.CleanValue LIKE '%e%' here because that would match on "True" and "Negative", so I also had to match on only allowable characters. - 02/13/2014 - MCR.
(CASE WHEN ISNUMERIC(Parsed.CleanValue) = 1 AND Parsed.CleanValue LIKE '%e%' THEN Parsed.CleanValue ELSE NULL END)[Exponent]
FROM
(
VALUES ('Null', NULL), ('EmptyString', ''), ('Spaces', ' - 2 . 8 % '),--Tabs and spaces mess up IsNumeric().
('Bit', '0'), ('TinyInt', '123'), ('Int', '123456789'), ('BigInt', '1234567890123456'),
--('VeryLong', '12345678901234567890.1234567890'),
('VeryBig', '-1234567890123456789012345678901234.5678'),
('TooBig', '-12345678901234567890123456789012345678.'),--34 (38-4) is the Longest length of an Integer supported by this query.
('VeryLong', '-1.2345678901234567890123456789012345678'),
('TooLong', '-12345678901234567890.1234567890123456789'),--38 Digits is the Longest length of a Number supported by the Decimal data type.
('VeryLong', '000000000000000000000000000000000000001.0000000000000000000000000000000000000'),--Works because Casting ignores leading zeroes.
('TooLong', '.000000000000000000000000000000000000000'),--Exceeds the 38 Digit limit for all Decimal types after the decimal-point.
--Dot(.), Plus(+), Minus(-), Comma(,), DollarSign($), BackSlash(\), Tab(0x09), and Letter-E(e) all yeild false-posotives with IsNumeric().
('Decimal', '.'), ('Decimal', '.0'), ('Decimal', '3.99'),
('Positive', '+'), ('Positive', '+20'),
('Negative', '-'), ('Negative', '-45'), ('Negative', '- 1.23'),
('Comma', ','), ('Comma', '1,000'),
('Money', '$'), ('Money', '$10'),
('Percent', '%'), ('Percent', '110%'),--IsNumeric will kick out Percent(%) signs.
('BkSlash', '\'), ('Tab', CHAR(0x09)),--I've actually seen tab characters in our data.
('Exponent', 'e0'), ('Exponent', '100e-999'),--No SQL-Server datatype could hold this number, though it is real.
('Enum', 'True'), ('Enum', 'Negative')
) AS Result(Type, Value)--O is for Observation.
CROSS APPLY
( --This Step is Optional. If you have Very Long numbers with tons of leading zeros, then this is useful. Otherwise is overkill if all the numbers you want have 38 or less digits.
--Casting of trailing zeros count towards the max 38 digits Decimal can handle, yet Cast ignores leading-zeros. This also cleans up leading/trailing spaces. - 02/25/2014 - MCR.
SELECT LTRIM(RTRIM(SUBSTRING(Result.Value, PATINDEX('%[^0]%', Result.Value + '.'), LEN(Result.Value))))[Value]
) AS Trimmed
CROSS APPLY
(
SELECT --You will need to filter out other Non-Keyboard ASCII characters (before Space(0x20) and after Lower-Case-z(0x7A)) if you still want them to be Cast as Numbers. - 02/15/2014 - MCR.
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(Trimmed.Value,--LTRIM(RTRIM(Result.Value)),
(CHAR(0x0D) + CHAR(0x0A)), ''),--Believe it or not, we have people that press carriage return after entering in the value.
CHAR(0x09), ''),--Apparently, as people tab through controls on a page, some of them inadvertently entered Tab's for values.
' ', ''),--By replacing spaces for values (like '- 2' to work), you open the door to values like '00 12 3' - your choice.
'$', ''), ',', ''), '+', ''), '%', ''), '/', '')[CleanValue]
) AS Parsed--P is for Parsed.
CROSS APPLY
( --NOTE: I do not like my Cross-Applies to feed into each other.
-- I'm paranoid it might affect performance, but you may move this into the select above if you like. - 02/13/2014 - MCR.
SELECT (CASE WHEN ISNUMERIC(Parsed.CleanValue + 'e0') = 1--By concatenating 'e0', I do not need to check for: Parsed.CleanValue NOT LIKE '%e%' AND Parsed.CleanValue NOT IN ('.', '-')
-- If you never plan to work with big numbers, then could use Decimal(19,4) would be best as it only uses 9 storage bytes compared to the 17 bytes that 38 precision requires.
-- This might help with performance, especially when converting a lot of data.
AND CHARINDEX('.', REPLACE(Parsed.CleanValue, '-', '')) - 1 <= (38-4)--This is the Longest Integer supported by Decimal(38,4)).
AND LEN(REPLACE(REPLACE(Parsed.CleanValue, '-', ''), '.', '')) <= 38--When casting to a Decimal (of any Precision) you cannot exceed 38 Digits. - 02/13/2014 - MCR.
THEN CAST(Parsed.CleanValue as Decimal(38,4))--Scale of 4 used is the max that Money has. This is the biggest number SQL Server can hold.
ELSE NULL END)[Number]
) AS Converted--C is for Converted.
Output:
The screenshot below was formatted and cut down to fit on StackOverflow.
The actual results have more columns.
Research:
Next to each query is the result.
It's interesting to see IsNumeric's shortcomings as well as CASTing's limitations.
I show this so you may see the background research that went into writing the query above.
It's important to understand each design decision (in case you're thinking of cutting anything out).
SELECT ISNUMERIC('')--0. This is understandable, but your logic may want to default these to zero.
SELECT ISNUMERIC(' ')--0. This is understandable, but your logic may want to default these to zero.
SELECT ISNUMERIC('%')--0.
SELECT ISNUMERIC('1%')--0.
SELECT ISNUMERIC('e')--0.
SELECT ISNUMERIC(' ')--1. --Tab.
SELECT ISNUMERIC(CHAR(0x09))--1. --Tab.
SELECT ISNUMERIC(',')--1.
SELECT ISNUMERIC('.')--1.
SELECT ISNUMERIC('-')--1.
SELECT ISNUMERIC('+')--1.
SELECT ISNUMERIC('$')--1.
SELECT ISNUMERIC('\')--1. '
SELECT ISNUMERIC('e0')--1.
SELECT ISNUMERIC('100e-999')--1. No SQL-Server datatype could hold this number, though it is real.
SELECT ISNUMERIC('3000000000')--1. This is bigger than what an Int could hold, so code for these too.
SELECT ISNUMERIC('1234567890123456789012345678901234567890')--1. Note: This is larger than what the biggest Decimal(38) can hold.
SELECT ISNUMERIC('- 1')--1.
SELECT ISNUMERIC(' 1 ')--1.
SELECT ISNUMERIC('True')--0.
SELECT ISNUMERIC('1/2')--0. No love for fractions.
SELECT CAST('e0' as Int)--0. Surpise! Casting to Decimal errors, but for Int is gives us zero, which is wrong.
SELECT CAST('0e0' as Int)--0. Surpise! Casting to Decimal errors, but for Int is gives us zero, which is wrong.
SELECT CAST(CHAR(0x09) as Decimal(12,2))--Error converting data type varchar to numeric. --Tab.
SELECT CAST(' 1' as Decimal(12,2))--Error converting data type varchar to numeric. --Tab.
SELECT CAST(REPLACE(' 1', CHAR(0x09), '') as Decimal(12,2))--Error converting data type varchar to numeric. --Tab.
SELECT CAST('' as Decimal(12,2))--Error converting data type varchar to numeric.
SELECT CAST('' as Int)--0. Surpise! Casting to Decimal errors, but for Int is gives us zero, which is wrong.
SELECT CAST(',' as Decimal(12,2))--Error converting data type varchar to numeric.
SELECT CAST('.' as Decimal(12,2))--Error converting data type varchar to numeric.
SELECT CAST('-' as Decimal(12,2))--Arithmetic overflow error converting varchar to data type numeric.
SELECT CAST('+' as Decimal(12,2))--Arithmetic overflow error converting varchar to data type numeric.
SELECT CAST('$' as Decimal(12,2))--Error converting data type varchar to numeric.
SELECT CAST('$1' as Decimal(12,2))--Error converting data type varchar to numeric.
SELECT CAST('1,000' as Decimal(12,2))--Error converting data type varchar to numeric.
SELECT CAST('- 1' as Decimal(12,2))--Error converting data type varchar to numeric. (Due to spaces).
SELECT CAST(' 1 ' as Decimal(12,2))--1.00 Leading and trailing spaces are okay.
SELECT CAST('1.' as Decimal(12,2))--1.00
SELECT CAST('.1' as Decimal(12,2))--0.10
SELECT CAST('-1' as Decimal(12,2))--1.00
SELECT CAST('+1' as Decimal(12,2))--1.00
SELECT CAST('True' as Bit)--1
SELECT CAST('False' as Bit)--0
--Proof: The Casting to Decimal cannot exceed 38 Digits, even if the precision is well below 38.
SELECT CAST('1234.5678901234567890123456789012345678' as Decimal(8,4))--1234.5679
SELECT CAST('1234.56789012345678901234567890123456789' as Decimal(8,4))--Arithmetic overflow error converting varchar to data type numeric.
--Proof: Casting of trailing zeros count towards the max 38 digits Decimal can handle, yet it ignores leading-zeros.
SELECT CAST('.00000000000000000000000000000000000000' as Decimal(8,4))--0.0000 --38 Digits after the decimal point.
SELECT CAST('000.00000000000000000000000000000000000000' as Decimal(8,4))--0.0000 --38 Digits after the decimal point and 3 zeros before the decimal point.
SELECT CAST('.000000000000000000000000000000000000000' as Decimal(8,4))--Arithmetic overflow error converting varchar to data type numeric. --39 Digits after the decimal point.
SELECT CAST('1.00000000000000000000000000000000000000' as Decimal(8,4))--Arithmetic overflow error converting varchar to data type numeric. --38 Digits after the decimal point and 1 non-zero before the decimal point.
SELECT CAST('000000000000000000000000000000000000001.0000000000000000000000000000000000000' as Decimal(8,4))--1.0000
--Caveats: When casting to an Integer:
SELECT CAST('3.0' as Int)--Conversion failed when converting the varchar value '3.0' to data type int.
--NOTE: When converting from character data to Int, you may want to do a double-conversion like so (if you want to Round your results first):
SELECT CAST(CAST('3.5' as Decimal(10)) as Int)--4. Decimal(10) has no decimal precision, so it rounds it to 4 for us BEFORE converting to an Int.
SELECT CAST(CAST('3.5' as Decimal(11,1)) as Int)--3. Decimal (11,1) HAS decimal precision, so it stays 3.5 before converting to an Int, which then truncates it.
--These are the best ways to go if you simply want to Truncate or Round.
SELECT CAST(CAST('3.99' as Decimal(10)) as Int)--3. Good Example of Rounding.
SELECT CAST(FLOOR('3.99') as Int)--3. Good Example fo Truncating.
The best solution would be to stop storing integers in a varchar column. Clearly there is a data issue where the data is interpretable as a numeric but cannot be cast as such. You need to find the record(s) that is(are) the problem and fix them if the data is such that it can and should be fixed. Depending on what you are storing and why it is a varchar to begin with, you may need to fix the query instead of the data. But that will be easier to do also if you first find the records which are blowing up your current query.
How to do that is the issue. It is relatively easy to search for a decimal place in the data to see if you have decimals (other than.0 which would convert) using charindex. You could also look for any record containing e or $ or any other character that could be interpeted as numeric according to the sources already given. If you don't have a lot of records a quick visual scan of the data will probably find it, especially if you sort on that field first.
Sometimes when I've been stuck on finding the bad data that is blowing up a query, I've put the data into a temp table and then tried processing in batches (using interpolation) until I find the one it blows up on. Start with the first 1000 (don't forget to use order by or you won't get the same results when you delete the good records and 1000 is only a best guess if you have millions of records start with a larger number). If it passes, delete those 1000 records and select the next batch. Once it fails, select a smaller batch. Once you are down to a number that can easily be visually scanned, you will find the problem. I've been able to find problem records fairly quickly when I have millions of records and a wierd error that none of the queries I've tried (which are basically guesses as to what might be wrong) have found the issue.
Try this and see if you still get an error...
SELECT CAST(CASE
WHEN IsNumeric(myVarcharColumn) = 0
THEN 0
ELSE myVarcharColumn
END AS BIGINT)
FROM myTable
WHERE IsNumeric(myVarcharColumn) = 1
AND myVarcharColumn IS NOT NULL
GROUP BY myVarcharColumn
ISNUMERIC is just... stupid. You shouln'd use it at all.
All cases bellow return 1:
ISNUMERIC('-')
ISNUMERIC('.')
ISNUMERIC('-$.')
For any integer types instead using: ISNUMERIC(#Value) = 1
just use: (#Value NOT LIKE '[^0-9]') OR (#Value NOT LIKE '-[^0-9]'
The only good solution is not to use ISNUMERIC.
Try wrapping it in a case:
select CASE WHEN IsNumeric(mycolumn) = 1 THEN CAST(mycolumn as bigint) END
FROM stack_table
WHERE IsNumeric(mycolumn) = 1
GROUP BY mycolumn
According to BOL ISNUMERIC returns 1 when the input expression evaluates to a valid numeric data type; otherwise it returns 0.
Valid numeric data types include the following:
int
numeric
bigint
money
smallint
smallmoney
tinyint
float
decimal
real
So as others pointed out you will have some data that will pass ISNUMERIC test but fail on casting to bigint
I had the same Issue and I came up with the Scalar Function as Im on 2008 SQL
ALTER Function [dbo].[IsInteger](#Value VarChar(18))
Returns Bit
As
Begin
Return IsNull(
(Select Case When CharIndex('.', #Value) > 0
Then 0
Else 1
End
Where IsNumeric(#Value + 'e0') = 1), 0)
End
If you are on 2012 you could use TRY_CONVERT
I had the same issue in MSSQL 2014 triggered by a comma instead of full stop:
isnumeric('9090,23') gives 1;
cast('9090,23' as float) fails
I've replaced ',' with '.'
there are DAX functions (IsError or IfError) that could help in this situation but we don't have those on our SQL Server 2008 R2. Looks like some additional analysis package for SQL Server.
I came across this blog post that might help. I've not run into this issue before and not sure if it'll help you in this instance:
http://dotmad.blogspot.com/2007/02/cannot-call-methods-on-bigint-error.html