Newbie - Errors when using CAST() and CONVERT() - sql-server

I am very new to programming. I am learning SQL and am using SS2008 as a starting point. Firstly,thank you for your help! It makes the process of learning for someone like myself a lot easier.
I have read other threads but their code seems a lot more complicated.
I am going to keep this as simple as possible: I am querying a database wanting to perform a sum function on a particular column.
At present, the meta data of the column is of type varchar. I understand, to be able to use the sum function, the column in question needs to be of int.
To convert the column accordingly, I thought I could use either the cast/convert functions but incur errors when doing so:
/**
CAST:Charge_Pct FROM VARCHAR -> INT
**/
SELECT CAST(Charge_Pct AS NUMERIC(7,7))
FROM [Main].[Share_Class_Charges]
WHERE CHARGE_PCT <> 'None'
-- Arithmetic overflow error converting varchar to data type numeric.
/**
CONVERT: Charge_Pct FROM VARCHAR -> INT
**/
SELECT CONVERT(NUMERIC(7,7),Charge_Pct)
FROM [Main].[Share_Class_Charges]
WHERE CHARGE_PCT <> 'None'
-- Error converting data type varchar to numeric.
I am confused by where I am going wrong and what the error messages are saying. Please could someone help me by explaining what the error messages means and what needs to be done to correct the code?
Many thanks,
Al.

The NUMERIC(7,7) type describes a number with 0 digits before the decimal and 7 after. This means that if you try to cast a VARCHAR as small as 10.12 you will get an overflow error.
Try this query instead:
SELECT CAST(Charge_Pct AS NUMERIC(5,2))
FROM [Main].[Share_Class_Charges]
WHERE CHARGE_PCT <> 'None'
This will attempt to convert the Charge_Pct column (which I assume holds percentages) into a NUMERIC type consisting of 5 total numbers with 2 coming after the decimal place.

Related

Unable to fix "may affect "CardinalityEstimate" in query plan choice" conversion warning (int to varchar)

Hi My task for a project is to remove this warning "Type conversion in expression (CONVERT(varchar(10),[Expr1017],0)) may affect "CardinalityEstimate" in query plan choice". I've been searching for a day and a half how to fix it and most other questions arent exactly what i need (i've tried their solutions but still have it). I can't just ignore it like some answers suggest bc the whole point of my task is to remove it.
CAST(product.Schedule AS VARCHAR(10)) AS [CSACode],
the schedule column is NOT computed and is generally either null or a single digit int.
I've tried reducing/increasing the VARCHAR size, using variables to pass around values and then convert that int var to the varchar (another site said issues arise sometime when converting a column to another type), using convert instead of cast, and just rework ton of code so it can return an int save it back in my MVC (it was too much work to do so, my boss said thats not the fix they want, and still resulted in other issues), but nothing seems to remove this warning beyond removing the cast entirely.
Removing this cast doesn't affect the row count at all it just removes the casted column CSACode in my select. I've been googling this CardinalityEstimate and row estimate seems to be all there is to it. It has little to no impact itself from what I can tell .
enter image description here
These 2 are the closest to questions I've found that are similar to my issue but not helpful.
How to avoid Implicit Type Conversion
Concatenation of INT columns warning: Type conversion in expression causes CardinalityEstimate warnings in execution plan
I am getting the same error with this minimal example:
SELECT CAST(product.Schedule AS VARCHAR(10)) AS [CSACode]
FROM (SELECT 123 AS Schedule UNION ALL SELECT NULL) AS product
The warning disappears when I change CAST(product.Schedule AS VARCHAR(10)) to FORMAT(product.Schedule, 'N0'):
SELECT FORMAT(product.Schedule, 'N0') AS [CSACode]
FROM (SELECT 123 AS Schedule UNION ALL SELECT NULL) AS product

Error converting data type nvarchar to real

I need to subtract two nvarchar values and store the result in another column. I understand that first I need to convert nvarchar to numeric values, but I always get this error
Error converting data type nvarchar to real
Just to mention in varchar values are stored only numeric values but I can't seem to get this conversion right.
This looks like a SQL Server error. If so, use try_convert():
select try_convert(real, string_col)
This returns NULL instead of an error if there are conversion issues.
You can find the offending values using:
select string_col
from t
where try_convert(real, string_col) is null and
string_col is not null;
use a case statement. It should be something like (t-sql pseudo code inside a SELECT)
case when IsNumeric(column1) AND IsNumeric(column2)
then cast(column1 as decimal) - cast(column2 as decimal)
else NULL end as column3
This is a classical example of why you shouldn't mix types (nvarchar and decimal in this case), there are all sorts of shananigans - like ('abc' - 5) or (NULL - NULL) or ('five' - '3 '). If this works, it might be a good idea to look at the "NULL" results, as this might give you valuable insights as to what kind of garbage is being collected in your system.
You said that...
Just to mention in varchar values are stored only numeric values but I
can't seem to get this conversion right.
If that is absolutely true, then there's a very high probability that there are 1 or 2 trailing control characters (Tab, Carriage Return, Line Feed, whatever) in the items causing the errors.
Try converting the NVARCHAR values to the MONEY datatype first. It won't work for leading control characters but it will for trailing control characters. Obvioussly your original original value doesn't haven more than 4 decimal places, of course.

what is the default type when a plain number is hard coded in a mssql statement?

I have some code that
is doing
SELECT * FROM table where col = 123
col is varchar(64)
when col was containing values less than 1456324852868
it was working fine till at some point someone added value 635912311949815157 in the table which of course caused this error
Arithmetic overflow error converting varchar to data type numeric.
This is because in order to compare two values they need to be in compatible types. So for some reason values less than 1456324852868 were converted fine but 635912311949815157 couldn't be converted.
My question is when there is hardcoded sql integer values (like 123 above) what data type they are and why it was working for varchar value 1456324852868 which is above int range but when varchar value 635912311949815157 was added things started breaking?
I did some experimentation and
SELECT * FROM table where col = convert(int,123) gives a different error (Arithmetic overflow error converting expression to data type int.) so
the answer is not int.
btw I know that putting quotes around the number will fix this new bug but I would like to know what is happening with the data types. I know it is a terrible practice but the code is there and I didn't write it.
I am using MS-SQL Server 2012
Thanks!
The value 123 is stored as an integer, you can verify this in a similar statement using sp_describe_first_result_set
EXEC sp_describe_first_result_set N'SELECT 123'
When evaluating your query, col will be implicitly converted to an integer, however, it will depend on the chosen execution plan which records will be converted and therefore you may have gotten away with 1456324852868.
I had to debug a similar issue in our live environment. The query in question had been running without problems for years, until new data was added to one of the tables and the chosen execution plan changed. This resulted in failure of the query, because the new execution plan resulted in evaluation of a VARCHAR value that couldn't be converted to an integer.
For starters one should not be inserting hard-coded integer values into varchar (or any kind of character) columns. DOING SO IS A BAD PRACTICE! All literal values inserted into a character datatype should be enclosed in single quotes.
That said the following code answers your main question:
use tempdb
go
select
20 as 'tiny',
1000 as 'small',
33000 as 'medium',
100000 as 'large',
3000000000 as 'huge'
into #tempvalues
go
exec sp_help 'tempdb.dbo.#tempvalues_________________________________________________________________________________________________________000000000003'
go
You will have to do this in tempdb and lookup the appropriate name for the sp_help
The result is that all integer literals are implicitly cast as int unless they are too big in which case they are cast as numeric

sql server 2000 instance specific setting to cause varchar to int error

I am having an inconsistent issue being produced on one of my servers whereby I have the following
Select *
from SomeVarcharTable v
join SomeIntTable i on i.MyInt=v.MyVarchar
Where v.Id = SomeID
The "MyVarChar" column is surprisingly of type varchar, and "MyInt" is of type int.
Whats curious is that when I run this on my development instance of Sql 2000, I receive no error.
When I run this on my Live instance of Sql 2000 I receive the following error:
Syntax error converting the varchar value '1.4' to a column of data type int.
Does anyone know of any server settings which might cause this. I have gone so far as to do a restore of my production database to my development system and the error doesn't occur.
I wondered whether it was something to do with the sql query engine, but this was working without an issue a few days ago.
Also, I am aware that this is not best practice, and that I could fix the above query by this:
Select *
from SomeVarcharTable v
join SomeIntTable i on cast(i.MyInt as varchar)=v.MyVarchar
Where v.Id = SomeID
I am more interested at this stage in wondering why the issue has been caused, so I can educate others in my company
Thanks in advance
Mark.
This is a data issue. The live environment contains data in the varchar column that is not a valid integer, whereas presumably your dev box doesn't and when the join is being done, it is attempting to convert the VARCHAR to an INTEGER to match on the MyInt column. If MyVarchar contains any value that is not a valid integer, then you will get that error.
The issue is you are storing values in VARCHAR that you are treating in your query as being a valid INTEGER. The fact you are storing them in a VARCHAR instead of an INTEGER column, which of course would be the ideal, indicates in your system that they are not intended to always be valid integers and that's a problem.
So, you either need to:
1) change MyVarchar to be an integer column and only allow valid ints to be stored
2) CAST MyInt to a VARCHAR in the join as you are doing (not great for performance)
3) don't try to join when MyVarchar is not a valid integer (equally not great for performance)
e.g.
Select *
from SomeVarcharTable v
join SomeIntTable i on i.MyInt=v.MyVarchar
Where v.Id = SomeID
AND v.MyVarchar NOT LIKE '%[^0-9]%' -- ignore rows where MyVarchar is not a valid int
4) create a new INTEGER column, alongside MyVarchar, and store the MyVarchar values in there that are valid integers, and use that to join instead. You'd be having some duplicate data, but performance-wise you'd at least be able to index and join on matching types without any workarounds.
Update:
Check out this reference on data type precedence.
Note INTEGER has a higher precedence than VARCHAR, hence is why VARCHARs are converted to INTEGERs.
I believe, when you are trying to compare a left hand INT value with the right side VARCHAR, the sql tries to implicitly convert the VARCHAR to an INT value and then compare. If it cannot implicitly convert varchar to INT, then it throws the above error which you are encountering.
integer data type has a higher precedence over a varchar data type. Since the integer data type has a higher precedence, the varchar data type is implicitly converted by SQL Server to an integer data type, and not the other way around.

Why am I getting 'Error converting data type varchar to numeric' on a floating point number in Perl?

We are inserting values into a SQL Server 2005 database column of type NUMERIC(19,5) from Perl. As long as the absolute values are .0001 or greater, it is working. However, when the values go to the 5th decimal place, Perl starts storing them in exponential format (-9e-05 instead of -0.00009), and then we get the error "Error converting data type varchar to numeric" from SQL Server. How do we prevent that and cause it to properly insert small numeric values?
We are using perl v5.8.5, DBI 1.56, DBD::Sybase 1.07, and SQL Server 2005.
The code is roughly the following, but I have removed the extraneous fields:
$invoice->{IL_UNITPRICE} = 0.09; # Actually comes from another database
$invoice->{IL_PRICEUNITCONV} = 0.001; # Actually comes from another database
$unitprice = $invoice->{IL_UNITPRICE} * -1 * $invoice->{IL_PRICEUNITCONV};
#$unitprice equals -9e-05 at this point.
$sth = $dbh->prepare('INSERT INTO foo ( bar ) VALUES ( ? )');
$sth->execute($unitprice);
The above line fails with error: DBD::Sybase::st execute failed: Server message number=8114 severity=16 state=5 line=2 server=baz text=Error converting data type varchar to numeric.
Try
$sth->execute(sprintf("%.6f",$unitprice));
To expand a bit -- When you pass a native float/double through the DBi interface it gets converted to a string, which, if you don't specify otherwise, uses Perl's default formatting conventions (imposed by the C compiler). The default is 6 digits precision, and if the number has more than that it's rendered in scientific notation.
To get fixed-point format you have to ask for it explicitly, using sprintf.
Actually I think using Math::Bigint and its associated Math::BigFloat would also solve this problem. Whenever I use floats or numbers of a sufficiently large size I use these modules so I don't have to throw sprintf's all over the place except when I really want to control the formatting.

Resources