Why the output is changed when using case statement? - sql

Can anyone explain the Result?
DECLARE #dec AS VARCHAR(5)
SET #dec = 'Yes'
DECLARE #val DECIMAL(15, 2);
SET #val = - 34152542256.86;
SELECT #val as c1
,CAST(#val AS BIGINT) as c2
SELECT #val as c1
,CASE #dec
WHEN 'Yes'
THEN CAST(#val AS BIGINT)
ELSE #val
END as c2
Results:
of first select statement:
c1 c2
-34152542256.86 -34152542256
of second select statement:
c1 c2
-34152542256.86 -34152542256.00

Source
You have an implicit conversion in your CASE statement. Basically, c2 must have a data-type, but SQL Server doesn't know whether it's DECIMAL(15,2) or BIGINT, as both types are returned from the various branches through the CASE statement. The rules of data-type precedence kick in and c2 ends up being converted to a DECIMAL(15,2).

As a try and I think this could work in this case, try sql_variant data-type like this:
DECLARE #val sql_variant;

In the first select statement the result for c2 can only be BIGINT.
In the second statement the result can be BIGINT or DECIMAL(15,2), belongs to which branch of case statement is entered. Since you can't have mixed types in one column sql server automatically converts c2 to DECIMAL(15,2)

BIGINT is an integer value (it does not allow scale), try using DECIMAL or NUMERIC.
And don't give up reading documentation.

Related

Only assign a variable if it is a numeric value

Is it possible to do something like this?
declare #ShouldBeNumber as varchar(10)
set #ShouldBeNumber = 1
declare #Number as varchar(10)
select #Number = case when ISNUMERIC(#ShouldBeNumber) then #Number = #ShouldBeNumber else #Number = '' end
i.e. only assign the variable if it is numeric. I have spent the last hour Googling this and have not found anything.
For example, I have looked here: How to check if a variable has a value in a SQL Server 2008 stored procedure
I think you just want:
select #Number = (case when ISNUMERIC(#ShouldBeNumber) = 1
then #ShouldBeNumber else #Number
end)
I assume isnumeric() does what you want, although it might have some unwanted behavior depending on your needs (accepting negative numbers, decimals, and exponential notation for instance). The else clause is a no-op; it just assigns the existing value back to #Number.
You can also use the try_cast() function as shown below.
DECLARE #ShouldBeNumber AS VARCHAR(10)
SET #ShouldBeNumber = 'A'
DECLARE #number AS VARCHAR(10)
Select #number = isnull(cast(try_cast(#ShouldBeNumber as float) as varchar(10)),'')
SELECT #number
Live db<>fiddle demo.

SQL Server 2014: How to cast a string value to int only if the value can be casted?

I try to cast a string value in a field of a table to int, but only in the case that the value stores a value that can be casted to int (in the other case the original value must be returned). Example:
DECLARE #ErrorCode nvarchar(1024)
SET #ErrorCode = 'a10'
SELECT IIF(TRY_CAST(#ErrorCode AS int) IS NULL, #ErrorCode, CAST(#ErrorCode AS int))
I've seen similar codes on StackOverflow.
My problem is that the SQL Server (2014) does not seem to short-circuit, and executes the cast always, even if TRY_CAST(#ErrorCode AS int) results in NULL. The result of the code above is the error "Conversion failed when converting the nvarchar value 'a10' to data type int."
See this sample on rextester.com
I also tried other variants with the same result:
SELECT CASE WHEN TRY_CAST(#ErrorCode AS int) IS NULL THEN #ErrorCode ELSE (SELECT CAST(#ErrorCode AS int)) END
SELECT CASE TRY_CAST(#ErrorCode AS int) WHEN 1 THEN CAST(#ErrorCode AS int) ELSE #ErrorCode END
How can I achieve my goal (avoid the cast in case the value in #ErrorCode cannot be casted)?
The simple solution would be to use COALSECE:
DECLARE #ErrorCode nvarchar(1024)
SET #ErrorCode = 'a10'
SELECT COALSECE(CAST(TRY_CAST(#ErrorCode AS int) as nvarchar(1024)), #ErrorCode)
However, I don't see the point of casting to int and then back to nvarchar.
To validate NULL you should IS NULL
DECLARE #ErrorCode NVARCHAR(1024)
SET #ErrorCode = 'a10'
SELECT IIF(TRY_CAST(#ErrorCode AS int) IS NULL, #ErrorCode, CAST(CAST(#ErrorCode AS int) AS VARCHAR(50)))
You need to convert the INT again to VARCHAR again to avoid the implicit conversion. IIF returns the data type with the highest precedence from the types in true_value and false_value.
The problem I see is trying to hold 2 different types in a single column. I'm not sure how you plan to use this information, but you could split the values in to numeric and text columns based on the type. You can do an ISNUMERIC() check and CAST to INT if it's true, otherwise leave it as text, like so:
CREATE TABLE #ErrorCodes ( ErrorCode NVARCHAR(10) )
INSERT INTO #ErrorCodes
( ErrorCode )
VALUES ( '123' ),
( 'a10' ),
( 'bbb' ),
( '456' )
SELECT ErrorCode AS OriginalVal ,
CASE WHEN ISNUMERIC(ErrorCode) = 1 THEN CAST(ErrorCode AS INT)
ELSE NULL
END AS NumericVal ,
CASE WHEN ISNUMERIC(ErrorCode) = 0 THEN ErrorCode
ELSE NULL
END AS NonNumericVal
FROM #ErrorCodes
DROP TABLE #ErrorCodes
Produces:
OriginalVal NumericVal NonNumericVal
=====================================
123 123 NULL
a10 NULL a10
bbb NULL bbb
456 456 NULL

SQL Different column type comparison error

Hi I have a stored procedure which suppose to compare between 2 columns on different tables
Users.ID => int
Trans.ID => nvarchar
Sometimes the value in Trans.ID is not numeric at all, and sometimes it's null, which causes an error while trying to compare
is there a way to try to parse Trans.ID into a number and in case it doesn't succeed to return 0??
I tried NullIf() but it doesn't work when the value inside is not numeric.
Assuming Trans.ID is a varchar(20) field, you can convert the Users.ID field to a varchar, use COALESCE to handle NULL values, and compare as follows:
WHERE CAST(Users.ID AS varchar(20)) = COALESCE(Trans.ID, '')
If using sql server you can do this
CASE WHEN ISNUMERIC(Trans.ID) = 0 then null
else cast(Trans.ID as int) end = Users.ID
you can do something like:
select * from users u
inner join trans t on u.userid = (CASE WHEN ISNUMERIC(t.id) = 1 THEN CONVERT(int, t.id) ELSE 0 END)
hope this helps.
If it is just equality comparison (which I think it is to make sense), I would convert Users.ID to NVARCHAR and then compare with Trans.ID.
IsNumeric() is not always right, e.g. ' - ' and '.' both return 1 for IsNumeric, but will fail your query.
This function (Adapted from here)
create function dbo.GetNumeric(#x varchar(10)) returns float as
begin
return
case
when #x is null or #x = '' then null -- blanks
when #x like '%[^0-9e.+-]%' then null -- non valid char found
when #x like 'e%' or #x like '%e%[e.]%' then null -- e cannot be first, and cannot be followed by e/.
when #x like '%e%_%[+-]%' then null -- nothing must come between e and +/-
when #x='.' or #x like '%.%.%' then null -- no more than one decimal, and not the decimal alone
when #x like '%[^e][+-]%' then null -- no more than one of either +/-, and it must be at the start
when #x like '%[+-]%[+-]%' and not #x like '%[+-]%e[+-]%' then null
else convert(float,#x)
end
end
Your query (involving inequality)
where users.id >= case when IsNull(dbo.GetNumeric(trans.id),0)
And if trans.id does not involve decimal points
where user.id >= case when trans.id not like '%[^0-9]%' and trans.id >''
then trans.id end
Of course, if it's a simple equality, just cast the int to a varchar
where right(users.id,10) = trans.id

why sql convert a number to '*' charachter

i run this query in SQL Server 2008
declare #a varchar(1)
select #a = 22
select #a
it's return this
*
why this query make this result ?
You are converting a 2 character number into a 1 character field.
It won't fit.
SQL is indicating the data is missing. Otherwise, it would display just a 2 and you wouldn't know if this was the full value or not.
insufficient space was detected -
declare #a varchar(2)
select #a = 242
select #a
this will also do that
Use following code:
declare #a varchar(2)
select #a = 22
select #a

IsNull() on bigint's min value?

Why does the following expression in SQL Server return -9223372036854775808 and not 123?
I am calling this from a stored proc where I can't pass null parameters
declare #t bigint;
set #t = -9223372036854775808; --min value for bigint / long
select ISNULL(#t, 123)
Because:
IF #t IS NOT NULL
PRINT #t
ELSE
PRINT 123
Being negative doesn't mean the value is NULL. NULL is the lack of any value at all.
Because #t is not null.
What made you think that the most negative value for a bigint would be interpreted as null?
The ISNULL(#t, 123) function returns 123 if #t is NULL, otherwise it returns #t. You may want to do something like this.
NULLIF(#t, -9223372036854775808)
This will return NULL if #t equals -9223372036854775808. NULLIF returns the first expression (#t) if the two expressions are not equal.
To achieve what I think you want to achieve, try this:
declare #t bigint;
set #t = -9223372036854775808; --min value for bigint / long
select ISNULL(NULLIF(#t, -9223372036854775808) , 123)
or this:
declare #t bigint;
set #t = -9223372036854775808; --min value for bigint / long
select case #t when -9223372036854775808 then 123 else #t end
You seem to be assuming that -9223372036854775808 IS NULL which is incorrect. ISNULL(#t, 123) would only return NULL if #t IS NULL but it's not null since it has the value of -9223372036854775808 which is non-NULL.
ISNULL returns the first non-null value, they are both non-null (have value) so it returns the first one.
#t is not null because you assigned it a value. If you want ISNULL() to return 123, remove the assignment.
declare #t bigint;
select ISNULL(#t, 123)