difference between varchar and int when doing max()? - sql

Is there a technical difference between these two, when table.column is a varchar or int? When would the results not be the same? I tried a few examples of digit values (e.g. 1, '1', etc.) and results are the same.
-- table.column is int:
select
MAX(table.column) as m
-- table.column is varchar:
select
MAX(CAST(table.column as int)) as m

Both of the result are same, because after casting both of the values are converted into int type.
If any string which is actually int type converted into int shows no difference over there. But if you have any string of any other type and you're converting that string into int type it gives error.
declare #str nvarchar(100)
set #str = 'sdsfd fdf fd dfsf'
select cast(#str as int)
Msg 245, Level 16, State 1, Line 3486
Conversion failed when converting the nvarchar value 'sdsfd fdf fd dfsf' to data type int.
For strings that can be converted to integers there is no difference in aggregate functions.
declare #dd table ( id varchar(max), id2 int)
insert into #dd ( id, id2 )
values ( '1', 1 )
, ( '99', 99 )
, ( '52', 52 )
select max(id2) as col, max(cast(id as int)) as col1 from #dd
Result
------------------
col col1
99 99
Thanks for #Zohar for reminding.
Although it is better to use Try_cast for your type conversion in SQL Server 2012 and above version.

Is there a technical difference between these two
If there's an index on column, the server can use it to cheaply compute the MAX for the "column is an int" version of your query. Not for the other.
When would the results not be the same?
There shouldn't be a difference in the result1 but as I say above, there may be a considerable difference in the amount of work the server has to do to compute the result. Even without the index, all of those conversions require additional code to run.
1Assuming all of the strings are convertible to ints.

Related

TSQL CTE error ''Types don't match between the anchor and the recursive part"

Would someone help me understand the details of the error below..? This is for SQL Server 2008.
I did fix it myself, and found many search hits which show the same fix, but none explain WHY this happens in a CTE.
Types don't match between the anchor and the recursive part in column "txt" of recursive query "CTE".
Here is an example where I resolved the issue with CAST, but why does it work?
WITH CTE(n, txt) AS
(
--SELECT 1, '1' --This does not work.
--SELECT 1, CAST('1' AS varchar) --This does not work.
--SELECT 1, CAST('1' AS varchar(1000)) --This does not work.
SELECT
1,
CAST('1' AS varchar(max)) --This works. Why?
UNION ALL
SELECT
n+1,
txt + ', ' + CAST(n+1 AS varchar) --Why is (max) NOT needed?
FROM
CTE
WHERE
n < 10
)
SELECT *
FROM CTE
I assume there are default variable types at play which I do not understand, such as:
what is the type for something like SELECT 'Hello world! ?
what is the type for the string concatenation operator SELECT 'A' + 'B' ?
what is the type for math such as SELECT n+1 ?
The info you want is all in the documentation:
When concatenating two char, varchar, binary, or varbinary expressions, the length of the resulting expression is the sum of the lengths of the two source expressions, up to 8,000 bytes.
snip ...
When comparing two expressions of the same data type but different lengths by using UNION, EXCEPT, or INTERSECT, the resulting length is the longer of the two expressions.
The precision and scale of the numeric data types besides decimal are fixed. When an arithmetic operator has two expressions of the same type, the result has the same data type with the precision and scale defined for that type.
However, a recursive CTE is not the same as a normal UNION ALL:
The data type of a column in the recursive member must be the same as the data type of the corresponding column in the anchor member.
So in answer to your questions:
'Hello world!' has the data type varchar(12) by default.
'A' + 'B' has the data type varchar(2) because that is the sum length of the two data types being summed (the actual value is not relevant).
n+1 is still an int
In a recursive CTE, the data type must match exactly, so '1' is a varchar(1). If you specify varchar without a length in a CAST then you get varchar(30), so txt + ', ' + CAST(n+1 AS varchar) is varchar(33).
When you cast the anchor part to varchar(max), that automatically means the recursive part will be varchar(max) also. You don't need to cast to max, you could also cast the recursive part directly to varchar(30) for example:
WITH CTE(n, txt) AS
(
--SELECT 1, '1' --This does not work.
SELECT 1, CAST('1' AS varchar(30)) --This does work.
--SELECT 1, CAST('1' AS varchar(1000)) --This does not work.
UNION ALL
SELECT
n+1,
CAST(CONCAT(txt, ', ', n+1) AS varchar(30))
FROM
CTE
WHERE
n < 10
)
SELECT *
FROM CTE
db<>fiddle
If you place the query into a string then you can get the result set data types like with the query :
DECLARE #query nvarchar(max) = 'SELECT * FROM table_name';
EXEC sp_describe_first_result_set #query, NULL, 0;

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.

SELECT TOP COALESCE and bigint

Ignore the practicality of the following sql query
DECLARE #limit BIGINT
SELECT TOP (COALESCE(#limit, 9223372036854775807))
*
FROM
sometable
It warns that
The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.
Why doesn't it work but the following works?
SELECT TOP 9223372036854775807
*
FROM
sometable
And COALESCE(#limit, 9223372036854775807) is indeed 9223372036854775807 when #limit is null?
I know that changing COALESCE to ISNULL works but I want to know the reason.
https://technet.microsoft.com/en-us/library/aa223927%28v=sql.80%29.aspx
Specifying bigint Constants
Whole number constants that are outside the range supported by the int
data type continue to be interpreted as numeric, with a scale of 0 and
a precision sufficient to hold the value specified. For example, the
constant 3000000000 is interpreted as numeric. These numeric constants
are implicitly convertible to bigint and can be assigned to bigint
columns and variables:
DECLARE #limit bigint
SELECT SQL_VARIANT_PROPERTY(COALESCE(#limit, 9223372036854775807),'BaseType')
SELECT SQL_VARIANT_PROPERTY(9223372036854775807, 'BaseType') BaseType
shows that 9223372036854775807 is numeric, so the return value of coalesce is numeric. Whereas
DECLARE #limit bigint
SELECT SQL_VARIANT_PROPERTY(ISNULL(#limit, 9223372036854775807),'BaseType')
gives bigint. Difference being ISNULL return value has the data type of the first expression, but COALESCE return value has the highest data type.
SELECT TOP (cast(COALESCE(#limit, 9223372036854775807) as bigint))
*
FROM
tbl
should work.
DECLARE
#x AS VARCHAR(3) = NULL,
#y AS VARCHAR(10) = '1234567890';
SELECT
COALESCE(#x, #y) AS COALESCExy, COALESCE(#y, #x)
AS COALESCEyx,
ISNULL(#x, #y) AS ISNULLxy, ISNULL(#y, #x)
AS ISNULLyx;
Output:
COALESCExy COALESCEyx ISNULLxy ISNULLyx
---------- ---------- -------- ----------
1234567890 1234567890 123 1234567890
Notice that with COALESCE, regardless of which input is specified first, the type of the output is VARCHAR(10)—the one with the higher precedence. However, with ISNULL, the type of the output is determined by the first input. So when the first input is of a VARCHAR(3) data type (the expression aliased as ISNULLxy), the output is VARCHAR(3). As a result, the returned value that originated in the input #y is truncated after three characters.That means isnull would not change the type, but coalesce would.
Turns out that 9223372036854775807 is a numeric instead of a bigint
From https://technet.microsoft.com/en-us/library/aa223927(v=sql.80).aspx
Whole number constants that are outside the range supported by the int data type continue to be interpreted as numeric, with a scale of 0 and a precision sufficient to hold the value specified
So we need to explicitly cast it to bigint
DECLARE #limit BIGINT
SELECT TOP (COALESCE(#limit, CAST(9223372036854775807 AS BIGINT)))
*
FROM
sometable

Varchar to Number in sql

i have written a query in which i am fetching an amount which is a number like '50,000','80,000'.
select Price_amount
from per_prices
As these values contain ',' these are considered to be varchar.Requirement is to to print these as 'number' with ','
that is how can '50,000' be considered as number and not varchar
If a value has anything other than numbers in it, it is not an integer it is string containing characters. in your case you have a string containing character 5, 0 and ,.
If this is what is stored in your database and this is what you want to display then go ahead you do not need to change it to Integer or anything else. But if you are doing some calculations on these values before displaying them, Yes then you need to change them to an Integer values. do the calculation. Change them back to the varchar datatype to show , between thousands and hundred thousands and display/select them.
Example
DECLARE #TABLE TABLE (ID INT, VALUE VARCHAR(100))
INSERT INTO #TABLE VALUES
(1, '100,000'),(2, '200,000'),(3, '300,000'),(4, '400,000'),
(1, '100,000'),(2, '200,000'),(3, '300,000'),(4, '400,000')
SELECT ID, SUM(
CAST(
REPLACE(VALUE, ',','') --<-- Replace , with empty string
AS INT) --<-- Cast as INT
) AS Total --<-- Now SUM up Integer values
FROM #TABLE
GROUP BY ID
SQL Fiddle
you could combine the Replace and cast function
SELECT CAST(REPLACE(Price_amount, ',', '') AS int) AS Price_Number FROM per_prices
for more information visit 'replace', 'cast'
SQLFiddle

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