I have a simple view that takes a varchar and converts it to a float.
Select CAST(TRIM(Measurement) AS float) as Measurement
from MyTable
where Type = 'Some Value'
Now the view sees the column as a float, however when i try to query my view with a where clasue of Measurement = 10, then I get the error:
Error converting data type varchar to float.
I know the string value will always be a number (I have checked many times). I also got this through multiple rounds of testing before this error popped up in prod. My guess is that there are other measurements in the table that are not part of my results, but are causing this error (those measurements may not have existing during the testing).
Is there a way to clean up the query so the results are always treated as a float?
Thanks
You clearly have bad values in the column. You can find them using a regular expression (in most databases) or a try_ function in SQL Server.
For instance:
Select Measurement
from MyTable
where Type = 'Some Value' AND try_convert(float, measurement) is null;
Or:
where type = 'Some Value' and
not measurement ~ '^-?[0-9]*[.]?[0-9]+$'
Th ~ is Postgres for a regular expression match. Other databases have similar functionality with different syntax.
Related
I wanted to return a value formatted with commas at every thousand if a number or just the value if it wasn't a number
I used the following statement which returned the error:
Conversion failed when converting the nvarchar value '1,000' to data type int.
Declare #QuantityToDelete int = 1000
SELECT CASE
WHEN ISNUMERIC(#QuantityToDelete)=1
THEN format(cast(#QuantityToDelete as int),'N0')
ELSE #QuantityToDelete
END [Result]
I can get it to work by using the following
SELECT CASE
WHEN ISNUMERIC(#QuantityToDelete)=1
THEN format(cast(#QuantityToDelete as int),'N0')
ELSE cast(#QuantityToDelete as varchar)
END [Result]
Result=1,000
Why doesn't the first example work when the ELSE #QuantityToDelete part of the statement isn't returned?
If I use the below switching the logic condition
SELECT CASE
WHEN ISNUMERIC(#QuantityToDelete)=0
THEN format(cast(#QuantityToDelete as int),'N0')
ELSE #QuantityToDelete
END [Result]
Result=1000
Which is expected, but no error, the case statement still has unmatched return types an nvarchar and an int as in the first example just different logic?
The important point to note is that a case expression returns a single scalar value, and that value has a single data type.
A case expression is fixed, it must evaluate the same and work the same for that query at runtime no matter what data flows through the query - in other words, the result of the case expression cannot be an int for some rows and a string for others.
Remember that the result of a query can be thought of, and used as, a table - so just like a table where you you define a column as being a specific data type, you cannot have a column where the data type can be different for rows of data.
Therefore with a case expression, SQL Server must determine at compile time what the resulting data type will be, which it does (if necessary) using data type precedence. If the case expression has different data types returned in different execution paths then it will attempt to implicitly cast them to the type with the highest precedence.
Hence your case expression that attempts to return two different data types fails because it's trying to return both a nvarchar and int and SQL Server is implicitly casting the nvarchar value to an int - and failing.
The second one works because you are controlling the casting and both paths result in the same varchar data type which works fine.
Also note that when defining a varchar it's good practice to define its length also, you can easily get complacent as it works here because the default length is 30 when casting however the default is 1 otherwise.
See the relevant part of the documentation
I've written the following SQL query to return all sites having "id" equal to 2.
SELECT * FROM `sites` WHERE id = '2'
And it works well. The problem is that even if I add some characters after "2" like this :
SELECT * FROM `sites` WHERE id = '2etyupp-7852-trG78'
It returns the same results as above.
How to avoid this ? that's to say return none on the second query ?
Thanks
The reason is that you are mixing types:
where id = '2'
------^ number
-----------^ string
What is a SQL engine supposed to do? Well, the standard approach is to convert the string to a number. So this is run as:
where id = 2
What happens when the string is not a number? In most databases, you get a type conversion error. However, MySQL does implicit conversion, converting the leading digits to a number. Hence, your second string just comes 2.
From this, I hope you learn not to mix data types. Compare numbers to numbers. Compare strings to strings.
I'm a newbie learning my way around T-SQL using the AdventureWorks2012 database. I'm using SQL Server 2014, though a solution that would also work with 2008 would be great. I've been given the below exercise:
Write a query using the Sales.SpecialOffer table. Display the difference between the MinQty and MaxQty columns along with the SpecialOfferID and Description columns.
Thing is, MaxQty allows for null values, so I'm trying to come up with a real world solution for an output that doesn't involve leaving nulls in there. However, when I try to use coalesce to return 'No Max' (yes, I get that I could just leave NULL in there but I'm trying to see if I can figure this out), I get the message that the varchar value 'No Max' couldn't be converted to data type int. I'm assuming this is because MaxQty - MinQty as an int takes precedence?
select
specialofferid
, description
, coalesce((maxqty - minqty),'No Max') 'Qty_Difference'
from
sales.specialoffer;
Error:
Msg 245, Level 16, State 1, Line 135
Conversion failed when converting the varchar value 'No max' to data type int.
I thought about just returning a nonsense integer (0 or a negative) but that doesn't seem perfect - if return 0 I'm obscuring situations where the result is actually zero, etc.
Thoughts?
You just need to make sure that all the parameters of the COALESCE function call have consistent data types. Because you can't get around the fact No Max is a string, then you have to make sure that the maxqty - minqty part is also treated as a string by casting the expression.
select specialofferid
, description
, coalesce(cast(maxqty - minqty as varchar),'No Max') 'Qty_Difference'
from sales.specialoffer;
EDIT: A few more details on the cause of the error
Without the explicit cast, the reason why the COALESCE function attempts to convert the No Max string to an int can be explained by the following documented rule:
Data type determination of the resulting expression is different. ISNULL uses the data type of the first parameter, COALESCE follows the CASE expression rules and returns the data type of value with the highest precedence.
And if you check the precedence of the different types, as documented here, then you will see that int has higher precedence than varchar.
So as soon as you have a mix of data types in the call to COALESCE, SQL Server will try to convert all mismatching parameters to the data type with highest precedence, in this case int. To override that default behavior, explicit type casting is required.
I would use a case statement to so you can do stuff you want.
select specialofferid
, description
, CASE
WHEN maxqty is null THEN 'No Max'
ELSE (maxqty - minqty) 'Qty_Difference'
END
from sales.specialoffer;
following situation:
a column xy is defined as varchar(25). In a view (SQL Server Mgmt Studio 2008) I filtered all values with letters (-> is not like '%[A-Z]%') and converted it to int (cast(xy as int)).
If I now try to make comprisons with that column (e.g. where xy < 1000), I'm getting a conversion error. And the message contains a value that should have been filtered with "is not like '%[A-Z]%'". Whats wrong??
thanks for help in advance...
this works (it folters out for example value 'G8111'):
SELECT unid
FROM CD_UNITS AS a INNER JOIN DEF_STATION AS b ON a.STATION = b.STATION
WHERE (b.CURENT = 'T') and UNID like '%[A-Z]%'
but when i put that in a view, an make select on it:
select * from my_view where xy < 3000
system says 'Conversion failed when converting the varchar value 'G8111' to data type int.' but 'G8111' should be filtered out in query above...
The optimizer does crazy things at times, so despite the fact that an "inner" filter1 "should" protect you, the optimizer may still push the conversion lower down than the filter and cause such errors.
The only semi-documented place where it will not do this is within a CASE expression:
The CASE statement(sic) evaluates its conditions sequentially and stops with the first condition whose condition is satisfied. In some situations, an expression is evaluated before a CASE statement receives the results of the expression as its input.
...
You should only depend on order of evaluation of the WHEN conditions for scalar expressions (including non-correlated sub-queries that return scalars), not for aggregate expressions
So the only way that should currently work would be:
CASE WHEN xy NOT LIKE '%[^0-9]%' THEN CONVERT(int,xy) END < 1000
This also uses a double-negative with LIKE to ensure that it only attempts the conversion when the value only contains digits.
1Whether this be in a subquery, a CTE, a View, or even just considering the logical processing order of SELECT and WHERE clauses. Within a single query, the optimizer can and will push conversion operations past filters.
SELECT logicalTime, traceValue, unitType, entName
FROM vwSimProjAgentTrace
WHERE valueType = 10
AND agentName ='AtisMesafesi'
AND ( entName = 'Hawk-1')
AND simName IN ('TipSenaryo1_0')
AND logicalTime IN (
SELECT logicalTime
FROM vwSimProjAgentTrace
WHERE valueType = 10 AND agentName ='AtisIrtifasi'
AND ( entName = 'Hawk-1')
AND simName IN ('TipSenaryo1_0')
AND CONVERT(FLOAT , traceValue) > 123
) ORDER BY simName, logicalTime
This is my sql command and table is a view table...
each time i put "convert(float...) part " i get
Msg 8114, Level 16, State 5, Line 1
Error converting data type nvarchar to float.
this error...
One (or more) of the rows has data in the traceValue field that cannot be converted to a float.
Make sure you've used the right combination of dots and commas to signal floating point values, as well as making sure you don't have pure invalid data (text for instance) in that field.
You can try this SQL to find the invalid rows, but there might be cases it won't handle:
SELECT * FROM vwSimProjAgentTrace WHERE NOT ISNUMERIC(traceValue)
You can find the documentation of ISNUMERIC here.
If you look in BoL (books online) at the convert command, you see that a nvarchar conversion to float is an implicit conversion. This means that only "float"-able values can be converted into a float. So, every numeric value (that is within the float range) can be converted. A non-numeric value can not be converted, which is quite logical.
Probably you have some non numeric values in your column. You might see them when you run your query without the convert. Look for something like comma vs dot. In a test scenario a comma instead of a dot gave me some problems.
For an example of isnumeric, look at this sqlfiddle