Error when summing converted column - sql

I have a table in SQL Server that contains 2 columns: Question_asked, Response
The Response column is of the datatype varchar(500) and holds the users response to the question that is held in the column Question_asked
I have created a view V_age_attr with the following SQL statement:
select
question_asked, cast(Response as integer)
from
main_table
where
question_asked = 'What is your age'
If I run a simple SQL query the results return as expected. But in some circumstances like when using a sum/group by query. I get an error msg (actual msg I get):
Conversion failed when converting the varchar value '2011-08-13 00:00:00' to data type int
In the table, main_table, one of the questions is the start_date, so the column Response does hold date values in the table, but not in the view I created.
Again, I can run a select query and check all the values for my cast(Response as integer) and they all are integer values.
My main question is: can anyone tell me if this is known/expected behavior in SQL Server 2008 R2, and/or does anyone else have another way to get a subset of values from this type of table other than creating a view?
Thank you

Alas, filtering by isnumeric doesn't necessarily work. SQL is a descriptive language, not a procedural language. So, the operations do not necessary work in the order specified.
In this case, SQL Server will attempt to do the conversion when reading the data and then apply the filter. Too late. You have an error.
The case statement is the only statement that guarantees order of evaluation, at least when aggregation functions are not involved. The following should fix the conversion problem:
select question_asked,
(case when isnumeric(Response) = 1 and
Response not like '%.%'
then cast(Response as int)
end) as Response
from main_table
where question_asked = 'What is your age'
This version also checks for a decimal point in Response. ISNUMERIC will return 1 when there is a decimal point, but that will fail the conversion as well.

Given the bug that Martin has highlighted, I would actually use the original condition in the CASE statement, which will apply to any numeric type, not limited to Gordon's int interpretation. Better than ISNUMERIC, but TRY_CONVERT would be better from SQL Server 2012 onwards. You would hope SQL Server can re-use the condition resolution in the WHERE clause to assist in the SELECT, or vice-versa.
select
question_asked, case when question_asked = 'What is your age'
then cast(Response as integer)
end
from
main_table
where
question_asked = 'What is your age'

Filter using ISNUMERIC - there may be non-integral data as responses which are messing up the aggregate functions.
SELECT
question_asked,
CASE(response AS INT)
FROM
main_table
WHERE
question_asked = 'What is your age'
AND ISNUMERIC(response) = 1
Edit: if that still doesn't work, you may need to do some more aggressive type-checking on your response column. Check out the responses here:
How to get if a string is a number in T-SQL

Related

SQL Server subquery behaviour

I have a case where I want to check to see if an integer value is found in a column of a table that is varchar, but is a mix of values that can be integers some are just strings. My first thought was to use a subquery to just select the rows with numeric-esque values. The setup looks like:
CREATE TABLE #tmp (
EmployeeID varchar(50) NOT NULL
)
INSERT INTO #tmp VALUES ('aa1234')
INSERT INTO #tmp VALUES ('1234')
INSERT INTO #tmp VALUES ('5678')
DECLARE #eid int
SET #eid = 5678
SELECT *
FROM (
SELECT EmployeeID
FROM #tmp
WHERE IsNumeric(EmployeeID) = 1) AS UED
WHERE UED.EmployeeID = #eid
DROP TABLE #tmp
However, this fails, with: "Conversion failed when converting the varchar value 'aa1234' to data type int.".
I don't understand why it is still trying to compare #eid to 'aa1234' when I've selected only the rows '1234' and '5678' in the subquery.
(I realize I can just cast #eid to varchar but I'm curious about SQL Server's behaviour in this case)
You can't easily control the order things will happen when SQL Server looks at the query you wrote and then determines the optimal execution plan. It won't always produce a plan that follows the same logic you typed, in the same order.
In this case, in order to find the rows you're looking for, SQL Server has to perform two filters:
identify only the rows that match your variable
identify only the rows that are numeric
It can do this in either order, so this is also valid:
identify only the rows that are numeric
identify only the rows that match your variable
If you look at the properties of this execution plan, you see that the predicate for the match to your variable is listed first (which still doesn't guarantee order of operation), but in any case, due to data type precedence, it has to try to convert the column data to the type of the variable:
Subqueries, CTEs, or writing the query a different way - especially in simple cases like this - are unlikely to change the order SQL Server uses to perform those operations.
You can force evaluation order in most scenarios by using a CASE expression (you also don't need the subquery):
SELECT EmployeeID
FROM #tmp
WHERE EmployeeID = CASE IsNumeric(EmployeeID) WHEN 1 THEN #eid END;
In modern versions of SQL Server (you forgot to tell us which version you use), you can also use TRY_CONVERT() instead:
SELECT EmployeeID
FROM #tmp
WHERE TRY_CONVERT(int, EmployeeID) = #eid;
This is essentially shorthand for the CASE expression, but with the added bonus that it allows you to specify an explicit type, which is one of the downsides of ISNUMERIC(). All ISNUMERIC() tells you is if the value can be converted to any numeric type. The string '1e2' passes the ISNUMERIC() check, because it can be converted to float, but try converting that to an int...
For completeness, the best solution - if there is an index on EmployeeID - is to just use a variable that matches the column data type, as you suggested.
But even better would be to use a data type that prevents junk data like 'aa1234' from getting into the table in the first place.

ASP.Net VB Database connection issue [duplicate]

email belongs to table booking and its defined as type "Text" in our Microsoft sql server
SELECT email,
COUNT(email) AS NumOccurrences
FROM Booking
GROUP BY email
HAVING ( COUNT(email) > 1 )
after running the above query(trying to find duplicates emails in the booking)
I got the error message like this:
The text, ntext, and image data
types cannot be compared or sorted, except when using IS NULL or LIKE
operator.
I am using Microsoft Sql
since you are using SQL Server, why not change the data type to VARCHAR(100)?
To work around this error without changing the datatype, the TEXT or NTEXT column needs to be converted to VARCHAR or NVARCHAR when used in either the ORDER BY clause or the GROUP BY clause of a SELECT statement. eg, which is alittle bit messy
SELECT CAST(email AS NVARCHAR(100)) email,
COUNT(CAST(email AS NVARCHAR(100))) AS NumOccurrences
FROM Booking
GROUP BY CAST(email AS NVARCHAR(100))
HAVING COUNT(CAST(email AS NVARCHAR(100))) > 1
SQL Server Error Messages - Msg 306
I had an issue with the accepted answer because of an issue with my ORM. This answer is only a workaround for when the accepted answer won't work, and is only valid if you only have the column in group by because it is required so you can include the column in the select.
It casts the column inside of aggregate function, so that it is not required in the group by, and the error will not occur and the returned data should still be correct.
MIN(CAST(email AS NVARCHAR(4000))) as email
Ideally you'd find a way to avoid this hack, but this may be useful in some circumstances.

SQL Server DBLlink to oracle returns numbers as string

I have an Oracle database containing my data and an SQL Server database getting the data from Oracle through DBLink.
Problem is - all numbers from the Oracle tables are accepted at the SQL Server as nvarchar. As a result, when i try to filter the query in the SQL Server with some_number_field = 0 i get:
"Conversion failed when converting the nvarchar value '3.141' to data type int."
This also happens if i try to select "some_number_field * 1" or similar expressions.
Any idea ?
Today I ran into the same kind of problem. It seems that Oracle field with datatype NUMBER are shown as nvarchar where querying through a linked server. However, NUMBER(x,y) not.
E.g. colB is the NUMBER field from an Oracle View (or table)
Try this:
SELECT colA, CAST(colB AS DECIMAL(23,2)) colB
FROM OPENQUERY(LINKED_SERVER_NAME, 'select * from myView')
Note: the DECIMAL(xx,y) values depends of course on your data. Also, remember, if your NUMBER column is a repetitive fraction (eg. 33.33333333 etc), you need to place a round() on the oracle side otherwise the CAST will throw an error.

UNION causes "Conversion failed when converting the varchar value to int"

I tried to search for previous articles related to this, but I can't find one specific to my situation. And because I'm brand new to StackOverflow, I can't post pictures so I'll try to describe it.
I have two datasets. One is 34 rows, 1 column of all NULLs. The other 13 rows, 1 column of varchars.
When I try to UNION ALL these two together, i get the following error:
Conversion failed when converting the varchar value to data type int.
I don't understand why I'm getting this error. I've UNIONed many NULL columns and varchar columns before, among many other types and I don't get this conversion error.
Can anyone offer suggestions why this error occurs?
The error occurs because you have corresponding columns in the two of the subqueries where the type of one is an integer and the type of the other is a character. Then, the character value has -- in at least one row -- a value that cannot be automatically converted to an integer.
This is easy to replicate:
select t.*
from (select 'A' as col union all
select 1
) t;
Here is the corresponding SQL Fiddle.
SQL Server uses pretty sophisticated type precedence rules for determining the destination type in a union. In practice, though, it is best to avoid using implicit type conversions. Instead, explicitly cast the columns to the type you intend.
EDIT:
The situation with NULL values is complicated. By itself, the NULL value has no type. So, the following works fine:
select NULL as col
union all
select 'A';
If you type the NULL, then the query will fail:
select cast(NULL as int) as col
union all
select 'A';
Also, if you put SQL Server in a position where it has to assign a type, then SQL Server will make the NULL an integer. Every column in a table or result set needs a type, so this will also fail:
select (select NULL) as col
union all
select 'A';
Perhaps your queries are doing something like this.
I have also encountered this error when I accidentally had the fields out of sequence in the 2 SELECT queries that I was unioning. Adjusting the fields' sequence fixed the problem.

The text, ntext, and image data > types cannot be compared or sorted, except when using IS NULL or LIKE > operator

email belongs to table booking and its defined as type "Text" in our Microsoft sql server
SELECT email,
COUNT(email) AS NumOccurrences
FROM Booking
GROUP BY email
HAVING ( COUNT(email) > 1 )
after running the above query(trying to find duplicates emails in the booking)
I got the error message like this:
The text, ntext, and image data
types cannot be compared or sorted, except when using IS NULL or LIKE
operator.
I am using Microsoft Sql
since you are using SQL Server, why not change the data type to VARCHAR(100)?
To work around this error without changing the datatype, the TEXT or NTEXT column needs to be converted to VARCHAR or NVARCHAR when used in either the ORDER BY clause or the GROUP BY clause of a SELECT statement. eg, which is alittle bit messy
SELECT CAST(email AS NVARCHAR(100)) email,
COUNT(CAST(email AS NVARCHAR(100))) AS NumOccurrences
FROM Booking
GROUP BY CAST(email AS NVARCHAR(100))
HAVING COUNT(CAST(email AS NVARCHAR(100))) > 1
SQL Server Error Messages - Msg 306
I had an issue with the accepted answer because of an issue with my ORM. This answer is only a workaround for when the accepted answer won't work, and is only valid if you only have the column in group by because it is required so you can include the column in the select.
It casts the column inside of aggregate function, so that it is not required in the group by, and the error will not occur and the returned data should still be correct.
MIN(CAST(email AS NVARCHAR(4000))) as email
Ideally you'd find a way to avoid this hack, but this may be useful in some circumstances.