casting to Integer is not working properly? - sql

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

Related

How can varchar containing integer work in calculations

How come string can contain integer. Even if I assume string storing numeric values as string, but even i can use in it calculation and getting the result as well. Just to try I wrote 5 in inverted commas and still calculation works fine. Not sure how?
declare #x varchar(20)
declare #y int
select #x='5'
select #y=6
select #x+#y
SQL Server -- and all other databases -- convert values among types when the need arises.
In this case, you have + which can be either string concatenation or number addition. Because one argument is an integer, it is interpreted as addition, and SQL Server attempts to convert the string to a number.
If the string cannot be converted, then you will get an error.
I would advise you to do your best to avoid such implicit conversions. Use the correct type when defining values. If you need to store other types in a string, use cast()/convert() . . . or better yet, try_cast()/try_convert():
try_convert(int, #x) + #y
A varchar can contain any character from the collations codepage you are using. For the purposes of this answer, I'm going to assume you're using something like the collation SQL_Latin1_General_CP1_CI_AS (which doesn't have any "international" characters, like Kanji, Hiragana, etc).
You first declare the variable #x as a varchar(20) and put the varchar value '5' in it. This is not an int, it's a varchar. This is an important distinction as a varchar and a numerical data type (like an int) behave very differently. For example '10' has a lower value than '2', where as the opposite is true for 10 and 2. (This is one reason why using the correct data type is always important.)
Then the second variable you have is #y, which is an int and has the value 6.
Then you have your expression SELECT #x+#y;. This has 2 parts to it. Firstly, as you have 2 datatypes, Data Type Precedence comes into play. int has a higher precedence than a varchar, and so #x is implicitly converted to an int. Then the expression is calculated, uses + as an addition operator (not a concatenation operator). Therefore the expression is effectively derived like this:
#x + #y = '5' + 6 = CONVERT(int,'5') + 6 = 5 + 6 = 11
SQL Server uses the following precedence order for data types:
user-defined data types (highest)
sql_variant
xml
datetimeoffset
datetime2
datetime
smalldatetime
date
time
float
real
decimal
money
smallmoney
bigint
int
smallint
tinyint
bit
ntext
text
image
timestamp
uniqueidentifier
nvarchar (including nvarchar(max) )
nchar
varchar (including varchar(max) )
char
varbinary (including varbinary(max) )
binary (lowest)

Using Logical Operators inside NULLIF()

If, then how can we use logical operators inside the NULLIF() funtion in SQL. Suppose i have a variable #a with value 5. I want to return NULL value if
#a>3. (which it is)
DECLARE #a INT
SET #a = 5;
SELECT NUllIF(#a,>3) AS Logical
-- Error: Incorrect syntax near '>'.
SELECT NUllIF(#a,'>3') AS Logical
-- Error: Conversion failed when converting the varchar value '>200' to data type int.
DECLARE #a INT
SET #a = 5;
SELECT CASE WHEN ISNULL(#a,0)>3 THEN NULL ELSE #a END AS output
Was that what it was about?
Use CASE
SELECT CASE WHEN #a<=3 THEN #a END AS Logical
Since you are using SQL Server 2016, you can use IIF function.
DECLARE #a AS INT = 5;
SELECT IIF(#a > 3, NULL, #a) AS Logical

Data type error when passing dynamic column names into SQL Server

I've run into an issue while executing a stored procedure from VBA: I want to pass in a column name as a string for the parameter, and then use a case statement to select the actual column name in the data.
This query works fine when the column name (#FACTOR) i'm passing through is an integer, but not when it's a varchar. I get a conversion error:
Error converting data type nvarchar to float.
Here's my code:
WITH T0 AS (
SELECT DISTINCT
CASE #FACTOR
WHEN 'DRVREC' THEN DRIVINGRECORD --OK
WHEN 'POAGE' THEN POAGE
WHEN 'ANNUALKM' THEN AMC_VH_ANNL_KM
WHEN 'DAILYKM' THEN AMC_VH_KM_TO_WRK
WHEN 'RATETERR' THEN AMC_VH_RATE_TERR --OK
WHEN 'BROKERNAME' THEN MASTERBROKER_NAME
WHEN 'DRVCLASS' THEN DRIVINGCLASS -- OK
WHEN 'VEHAGE' THEN VEH_AGE -- OK
WHEN 'YEARSLIC' THEN YRSLICENSE
WHEN 'COVERAGECODE' THEN COVERAGECODE
ELSE NULL END AS FACTOR FROM DBO.Automation_Data
),
...
...
Or perhaps the example below is more concise:
DECLARE #FACTOR varchar(50)
SELECT #FACTOR = 'NOT_A_VARCHAR'
SELECT CASE #FACTOR
WHEN 'A_VARCHAR' THEN COLUMNNAME1
WHEN 'NOT_A_VARCHAR' THEN COLUMNNAME2
ELSE NULL END AS FACTOR FROM dbo.myTable
^ This would work, but if #FACTOR = 'A_VARCHAR' then i get the error.
Thanks in advance!
UPDATE **********************************:
It appears to be an issue with the case statement itself?
When I only have the varchar option in my case statement, the query runs. When I un-comment the second part of the case statement I get the error.
DECLARE #FACTOR varchar(50)
SELECT #FACTOR = 'A_VARCHAR'
SELECT CASE #FACTOR
WHEN 'A_VARCHAR' THEN COLUMNNAME1
--WHEN 'NOT_A_VARCHAR' THEN COLUMNNAME2 ELSE NULL
END AS FACTOR FROM dbo.myTable
When you are selecting from multiple columns as a single column like you are doing, SQL returns the result as the highest precedence type. Same goes with coalesce etc. when a single result is to be returned from multiple data types.
If you try the code below for example, 3rd select will return the error you're getting, as it tries to convert abc to int (higher precedence). If you set #V to '123', error will go away, as the convert from '123' to int/float works. When you check the 'BaseType' of the result, you can see it shows the highest precedence data type of the mixed types.
DECLARE #F int = 1 --if you use float here error message will show ...'abc' to data type float.
DECLARE #V varchar(5) = 'abc'
DECLARE #O varchar = '1'
SELECT CASE WHEN #O = '1' THEN #F ELSE #V END --no error
SELECT SQL_VARIANT_PROPERTY((SELECT CASE WHEN #O = '1' THEN #F ELSE #V END), 'BaseType') --int/float
SET #O = '2'
SELECT CASE WHEN #O = '1' THEN #F ELSE #V END --error: Conversion failed when converting the varchar value 'abc' to data type int.
When you converted all your selects to nvarchar, nvarchar became the highest precedence data type, so it worked. But if you know some of your columns are float and some of them nvarchar, you only need to convert float columns to nvarchar. So this will work as well:
SET #O = '2'
SELECT CASE WHEN #O = '1' THEN CONVERT(NVARCHAR(5), #F) ELSE #V END
See SQL Data Type Precedence

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

Truncate and cast into varchar

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.