Truncate and cast into varchar - sql

I have a number I'm trying to cast into a varchar, and I don't care if it gets truncated.
My problem is that I keep getting these errors, depending on what I try:
String or binary data would be truncated.
Arithmetic overflow error for type varchar, value = 0.000052
Here is my current attempts:
declare #a as varchar(10), #b as varchar(10), #c as varchar(10)
set #a =cast(#b as float) / cast(#c as float)
DECLARE #temp TABLE
(
[read] varchar(20),
[percent] varchar(10)
)
insert into #temp ([read],[percent])
VALUES('text', #a)
I've also tried some of the following for the second line:
set #a = cast( cast(#b as float) / cast(#c as float) as varchar())
One oddity is that it works some of the time, but not all of the time. I've been able to discover that it only works when #a is large enough and not negative. When it is either negative or small, I get some of the above errors.

If you want to convert floating point numbers to characters strings, I highly recommend the str() function, which is documented here.
In your case, it would look something like:
set #a = str( cast(#b as float) / cast(#c as float), 10, 4)
where 10 and 4 are whatever scale and precision that you want.
By the way, when using varchar() in SQL, you should always include a length. Do not depend on the defaults, because they can change depending on where the expression is used.

Related

Error converting data type varchar to float on non varchar data type

I've come across an issue (that I've partially solved) but can't seem to find a reason behind the failing in the first place.
I have a field in a table which holds a combination of alpha and numerical values. The field is a char(20) data type (which is wrong, but unchangeable) and holds either a NULL value, 'Unknown' or the "numbers" 0, 50, 100. The char field pads the values with trailing white space. This is a known and we can't do a thing about it.
To remove the Unknown values, we have a series of coalesce statements in place, and these two return the error message as per the title.
,coalesce(DHMCC.[HESA Module Total Proportion Taught], 'Missing')
,cast(isnull(DHMCC.[HESA Module Total Proportion Taught] ,'Missing') as varchar(10))
The query I have is why am I getting this error when I'm not converting a data type of varchar to float (or am I?)
Does anyone have an idea as to where to look next to try to fix this error?
The STR() function accepts a float datatype as the first argument, therefore SQL Server is implicitly converting whatever you pass to this function, which in your case is the CHAR(20) column. Since unknown can't be converted to a float, you get the error.
If you run the following with the actual execution plan enabled:
DECLARE #T TABLE (Col CHAR(20));
INSERT #T VALUES (NULL);
SELECT Result = ISNULL(STR(Col, 25, 0), 'Missing')
FROM #T
Then checkthe execution plan XML you will see the implicit conversion:
<ScalarOperator ScalarString="isnull(str(CONVERT_IMPLICIT(float(53),[Col],0),(25),(0)),'Missing')">
The simplest solution is probably to use a case expression and not bother with any conversion at all (only if you know you will only have the 5 values you listed:
DECLARE #T TABLE (Col CHAR(20));
INSERT #T VALUES (NULL), ('0'), ('50'), ('100');--, ('Unknown');
SELECT Result = CASE WHEN Col IS NULL OR Col = 'Unknown' THEN 'Missing' ELSE Col END
FROM #T;
Result
---------
Missing
0
50
100
Missing
If you really want the STR() function, you can make the conversion explicit, but use TRY_CONVERT() so that anything that is not a float simply returns NULL:
DECLARE #T TABLE (Col CHAR(20));
INSERT #T VALUES (NULL), ('0'), ('50'), ('100');--, ('Unknown');
SELECT Result = ISNULL(STR(TRY_CONVERT(FLOAT, Col), 25, 0), 'Missing')
FROM #T
Result
------------
Missing
0
50
100
Missing
Although, since you the numbers you have stated are integers, I would be inclined to convert them to integers rather than floats:
DECLARE #T TABLE (Col CHAR(20));
INSERT #T VALUES (NULL), ('0'), ('50'), ('100'), ('Unknown');
SELECT Result = ISNULL(CONVERT(VARCHAR(10), TRY_CONVERT(INT, Col)), 'Missing')
FROM #T;
Result
---------
Missing
0
50
100
Missing
Thanks to #GarethD
I've only just come across TRY_CONVERT and this seems like the better option, so thanks him for that pointer, also trying with TRY_CAST as well.
The data really should be held in a varchar field, it's referential and not for calculation, and this seems to work equally as well,
-- Declare #varText as varchar(16) = '10 '
-- Declare #varText as char(16) = 'Unknown'
-- Declare #varText as char(16) = ''
SELECT
ISNULL(NULLIF(TRY_CAST(LTRIM(RTRIM(#varText)) as varchar(16)), ''), 'Missing') AS HESA
I've created this test scenario which works ok.

'LIKE' issues with FLOAT: SQL query needed to find values >= 4 decimal places

I have a conundrum....
There is a table with one NVARCHAR(50) Float column that has many rows with many numbers of various decimal lengths:
'3304.063'
'3304.0625'
'39.53'
'39.2'
I need to write a query to find only numbers with decimal places >= 4
First the query I wrote was:
SELECT
Column
FROM Tablename
WHERE Column LIKE '%.[0-9][0-9]%'
The above code finds all numbers with decimal places >= 2:
'3304.063'
'3304.0625'
'39.53'
Perfect! Now, I just need to increase the [0-9] by 2...
SELECT
Column
FROM Tablename
WHERE Column LIKE '%.[0-9][0-9][0-9][0-9]%'
this returned nothing! What?
Does anyone have an explanation as to what went wrong as well and/or a possible solution? I'm kind of stumped and my hunch is that it is some sort of 'LIKE' limitation..
Any help would be appreciated!
Thanks.
After your edit, you stated you are using FLOAT which is an approximate value stored as 4 or 8 bytes, or 7 or 15 digits of precision. The documents explicitly state that not all values in the data type range can be represented exactly. It also states you can use the STR() function when converting it which you'll need to get your formatting right. Here is how:
declare #table table (columnName float)
insert into #table
values
('3304.063'),
('3304.0625'),
('39.53'),
('39.2')
--see the conversion
select * , str(columnName,20,4)
from #table
--now use it in a where clause.
--Return all values where the last digit isn't 0 from STR() the conversion
select *
from #table
where right(str(columnName,20,4),1) != 0
OLD ANSWER
Your LIKE statement would do it, and here is another way just to show they both work.
declare #table table (columnName varchar(64))
insert into #table
values
('3304.063'),
('3304.0625'),
('39.53'),
('39.2')
select *
from #table
where len(right(columnName,len(columnName) - charindex('.',columnName))) >= 4
select *
from #table
where columnName like '%.[0-9][0-9][0-9][0-9]%'
One thing that could be causing this is a space in the number somewhere... since you said the column type was VARCHAR this is a possibility, and could be avoided by storing the value as DECIMAL
declare #table table (columnName varchar(64))
insert into #table
values
('3304.063'),
('3304. 0625'), --notice the space here
('39.53'),
('39.2')
--this would return nothing
select *
from #table
where columnName like '%.[0-9][0-9][0-9][0-9]%'
How to find out if this is the case?
select *
from #table
where columnName like '% %'
Or, anything but numbers and decimals:
select *
from #table
where columnName like '%[^.0-9]%'
The following is working fine for me:
declare #tab table (val varchar(50))
insert into #tab
select '3304.063'
union select '3304.0625'
union select '39.53'
union select '39.2'
select * from #tab
where val like '%.[0-9][0-9][0-9][0-9]%'
Assuming your table only has numerical data, you can cast them to decimal and then compare:
SELECT COLUMN
FROM tablename
WHERE CAST(COLUMN AS DECIMAL(19,4)) <> CAST(COLUMN AS DECIMAL(19,3))
You'd want to test the performance of this against using the character data type solutions that others have already suggested.
You can use REVERSE:
declare #vals table ([Val] nvarchar(50))
insert into #vals values ('3304.063'), ('3304.0625'), ('39.53'), ('39.2')
select [Val]
from #Vals
where charindex('.',reverse([Val]))>4

SQL ABS() is returning unexpected decimal when applied on divisible data

On applying abs on divisible data, whole number quotient is converted to decimal.
create table #data ( ConditionValue money, ConditionRateNbr real)
insert into #data values(9665.77,37.61)
select abs(conditionvalue/conditionrateNbr) Using_Abs ,*
from #data
--Using_Abs ConditionValue ConditionRateNbr
--256.999969482422 9665.77 37.61
Why this happens and how to solve?
It has to do with the "real" data type being an "approximate-number", see https://msdn.microsoft.com/en-us/library/ms173773.aspx. My knowledge of SQL is not deep enough to give a detailed explanation but I do have a solution, use the decimal data type i/o real:
create table #data ( ConditionValue money, ConditionRateNbr decimal(38,20))
insert into #data values(9665.77,37.61)
select abs(conditionvalue/conditionrateNbr) Using_Abs ,*
from #data
IMO it's usually smarter to use decimal than real to avoid this kind of issue.
Assuming sql server, but if you run the following script:
CREATE TABLE #data
(
ConditionValue MONEY
, ConditionRateNbr REAL
)
INSERT INTO #data
VALUES ( 9665.77, 37.61 )
SELECT *
INTO data
FROM (
SELECT ABS(ConditionValue / ConditionRateNbr) Using_Abs
, conditionValue / ConditionRateNbr test
, *
FROM #data
)a
SELECT * FROM INFORMATION_SCHEMA.COLUMNS AS C WHERE C.TABLE_NAME = 'data'
DROP TABLE #data
DROP TABLE data
You'll see that the result of the division is a real. The ISO synonym for real is float(24). The ABS(numeric_expression) function Returns the same type as numeric_expression. Apparently, the ABS function returns a default float, which is float(53). float(53) contains 15 digits, so you get 15 digits returned.
So the returned datatype is not, as you might at first think, a decimal, but instead another float, defaulted to its max size (so to speak).
If you define your fields as decimals, see here what you are likely to get.
What I have seen using money is more reliable than decimal.
Here is another example.
Why on earth decimal considers 3/1.5 as 1.5, I do not know !!!
declare #real real = 1.5, #realup money=3
select #realUp numerator, #real denominator, #realup /cast (#real as money) money, #realup /cast(#real as decimal) decimal
--numerator denominator money decimal
--3.00 1.5 2.00 1.50000000000000000000000

casting to Integer is not working properly?

execute these and check the result why is it so ?
declare #a decimal(8,3) =235.363
declare #b int =1
select case #b
when 1 then cast(#a as int)
when 2 then CAST(#a as decimal(8,3))
end
Result : 235.000
declare #a decimal(8,3) =235.363
declare #b int =1
select case #b
when 1 then cast(#a as int)
--when 2 then CAST(#a as decimal(8,3))
end
Result : 235
declare #a decimal(8,3) =235.363
declare #b int =1
select case #b
when 1 then cast(#a as tinyint)
when 2 then CAST(#a as float)
end
Result : 235
What you see is not what you get.
For the column type, SQL Server picks the correct, more wide type (float over tinyint, decimal over int). You can verify that by doing select into instead of just select.
It's just the display rules that are different.
When the selected column type is float, you don't see the trailing .000 when there is no fractional part.
For decimal with explicit positions set, such as decimal(8,3), you will see the trailing .000 even if there's no fractional part. If you remove the specifier and only leave decimal as the column type, the .000 will disappear.
All that does not affect the actual column type, which is always the widest one.
This behaviour is documented in the BOL entry for CASE
Return Types
Returns the highest precedence type from the set of types in
result_expressions and the optional else_result_expression. For more
information, see Data Type Precedence (Transact-SQL).
If you follow the link to data type precedence you will see that float has higher precedence than decimal which in turn has higher precedence than tinyint so this behaviour is expected.
Probably cast operation will cast all the options to a bigger type.
From MSDN:
The data types of input_expression and each when_expression must be
the same or must be an implicit conversion.
http://msdn.microsoft.com/en-us/library/ms181765.aspx
Casting to Integer is not working properly.
Your statement is not correct!
In CASE statement, you can only return one type of data, so according to your statement you can return either INT or decimal(8,3), since your case statement has decimal(8,3) so here INT data is implicitly converted to decimal! Please see below example:, always try to use same return type in CASE statement to get proper and expected result, thanks.
1.
select case #b
when 1 then CAST(#a as int) -- return type INT
when 2 then CAST(#a as int) -- return type INT
end
2.
select case #b
when 1 then CAST(#a as int) -- return type INT and then converted to decimal(8,3)
when 2 then CAST(#a as decimal(8,3)) -- return type return type INT
end

Using SQL 2005 trying to cast 16 digit Varchar as Bigint error converting

First, thanks for all your help! You really make a difference, and I GREATLY appreciate it.
So I have a Varchar column and it holds a 16 digit number, example: 1000550152872026
select *
FROM Orders
where isnumeric([ord_no]) = 0
returns: 0 rows
select cast([ord_no] as bigint)
FROM Progression_PreCall_Orders o
order by [ord_no]
returns: Error converting data type varchar to bigint.
How do I get this 16 digit number into a math datatype so I can add and subtract another column from it?
UPDATE: Found scientific notation stored as varchar ex: 1.00054E+15
How do I convert that back into a number then?
DECIMAL datatype seems to work fine:
DECLARE #myVarchar AS VARCHAR(32)
SET #myVarchar = '1000550152872026'
DECLARE #myDecimal AS DECIMAL(38,0)
SET #myDecimal = CAST(#myVarchar AS DECIMAL(38,0))
SELECT #myDecimal + 1
Also, here's a quick example where IsNumeric returns 1 but converting to DECIMAL fails:
DECLARE #myVarchar AS VARCHAR(32)
SET #myVarchar = '1000550152872026E10'
SELECT ISNUMERIC(#myVarchar)
DECLARE #myDecimal AS DECIMAL(38,0)
SET #myDecimal = CAST(#myVarchar AS DECIMAL(38,0)) --This statement will fail
EDIT
You could try to CONVERT to float if you're dealing with values written in scientific notation:
DECLARE #Orders AS TABLE(OrderNum NVARCHAR(64), [Date] DATETIME)
INSERT INTO #Orders VALUES('100055015287202', GETDATE())
INSERT INTO #Orders VALUES('100055015287203', GETDATE())
INSERT INTO #Orders VALUES('1.00055015287E+15', GETDATE()) --sci notation
SELECT
CONVERT(FLOAT, OrderNum, 2) +
CAST(REPLACE(CONVERT(VARCHAR(10), GETDATE(), 120), '-', '') AS FLOAT)
FROM #Orders
WITH validOrds AS
(
SELECT ord_no
FROM Orders
WHERE ord_no NOT LIKE '%[^0-9]%'
)
SELECT cast(validOrds.ord_no as bigint) as ord_no
FROM validOrds
LEFT JOIN Orders ords
ON ords.ord_no = validOrds.ord_no
WHERE ords.ord_no is null
Take a look at this link for an explanation of why isnumeric isn't functioning the way you are assuming it would: http://www.sqlservercentral.com/articles/IsNumeric/71512/
Take a look at this link for an SO post where a user has a similar problem as you:
Error converting data type varchar
hence, you should always use the correct datatype for each column unless you have a very specific reason to do so otherwise... Even then, you'll need to be extra careful when saving values to the column to ensure that they are indeed valid values