Why is arithmetic + preferred over textual one? - sql-server

If I run
select '1' + '1'
the result is 11, since I have added a text to another one.
If I run
select 1 + '1'
the result is 2. I assume the arithmetic operator is chosen over the concatenator because of the type of the first operand. If my reasoning was valid, then the result of
select '1' + 1
would be 11. But instead, it is 2. So, it seems that the operator + is tried to be used as an arithmetic operator and if neither of the operands is arithmetic, then goes on to the next operator. If that is true, that would explain why did I get the error of
Conversion failed when converting the varchar value 'customer_' to data type
int.
instead of customer_<somenumber> when I ran a select and had 'customer_' + <somenumber>.
Long story short: I think I observed that arithmetic + is preferred over its meaning of concatenation at SQL Server. Am I right? If so, is there an official reason of this behavior?

What you're running into is a matter of data type precedence. SQL Server looks to character data types after numerics. So regardless of the ordering of your operands (1 + '1' vs '1' + 1), it's attempting to convert your types to numerics, and succeeding.
The same happens with your second attempt - it's trying to convert the string customer_ to an integer because you're using an arithmetic operator along with an integer.

Yes, the precedence is arithmetic first when compared to concatenation.
https://msdn.microsoft.com/en-us/library/ms190276.aspx
Your error, as you know, is because the it won't implicitly attempt to convert INT to VARCHAR

Related

I'm having problems trying to correct a conversion issue in sql server 2016

I have a case statement where I'm trying to SUM multiple column int values and then format the summed value to '00000015700+' as an example but getting a conversion error in SQL Server 2016.
Here is the error:
Conversion failed when converting the varchar value '00000015700+' to
data type int.
Here is my code :
CASE
WHEN x.Code = 'WRITPREM' AND x.[Description] = 'NEW POLICY' THEN RIGHT('00000000000' + CAST(REPLACE((sum(x.totalPolicy_BIN) + sum(x.totalPolicy_COL) + sum(x.totalPolicy_OTC) + sum(x.totalPolicy_PDM) + sum(x.totalPolicy_MED) +sum(x.totalPolicy_PIP) + sum(x.totalPolicy_REN) + sum(x.totalPolicy_TOW) + sum(x.totalPolicy_UBI) + sum(x.totalPolicy_UPD)),'.','') as varchar(12)) + '+',12)END as TEST
Any help/direction would be appreciated. Thanks.
One of the fields that you are sum()ing contains the value with a "+" sign. As a result, SQL Server cannot convert the value to an integer in order to sum(). It seems you are applying the function on a non-numeric column(s). So you have unexpected data in at least one of those columns. Try eliminating some of the sum() columns to figure out which column contains the offending data. Ideally, you'd have the correct data types on each column and not run into this issue.
You have reversed the REPLACE and CAST commands. You should CAST the value to string, then REPLACE the decimal point and add your plus sign to the end, then take the RIGHT(..., 12) of that value.

floor() returning different results

In the following code, floor() (in the 3rd param of the iif()) does not return 100 as expected; it returns 100.00. I am just testing the output and the logic, hence the hard coded values.
select iif(floor(100.000) < cast(round(100.000, 2) as decimal(5, 2)), cast(round(100.000, 2) as decimal(5, 2)), floor(100.000))
However this indeed returns 100 as expected. I am not sure why:
select floor(100.000)
Further, back to the iif() function: If I first cast the value inside the floor function to a float, it indeed returns 100. I have tried casting it to an integer and a decimal as well and floor() still returns 100.00. This really has thrown me. I was under the impression that floor will only return anything after the decimal if it is of type money.
From the docs on IIF:
Returns the data type with the highest precedence from the types in true_value and false_value
And the rules on data type precedence:
When an operator combines two expressions of different data types, the rules for data type precedence specify that the data type with the lower precedence is converted to the data type with the higher precedence
Since the true_value part of your expression returns a decimal, the entire expression will do the same.
To use a simpler example:
SELECT IIF(0 > 0,
CAST(1 AS DECIMAL(5, 2)),
2)
This will cause the 2 to be cast to a decimal.

Casting binary to bit

Based on the recent question.
Can someone point me to explanation of the following?
If I cast binary(4) constant 0x80000000 to int, take the resulting value and cast it to bit type, the result is 1.
select cast(0x80000000 as int) --> -2147483648
select cast(-2147483648 as bit) --> 1
But if I cast 0x80000000 to bit type directly the result is 0.
select cast(0x80000000 as bit) --> 0
I hoped to get 1 in this case as well, thinkning that probably this expression equivalent to
select cast(cast(0x80000000 as binary(1)) as bit)
but this is not the case. Instead, it seems that the highest byte of the binary constant is taken and converted to bit. So, effectively it is something like
select cast(cast(right(0x80000000, 1) as binary(1)) as bit)
I'm clear with first binary -> int -> bit part. What I'm not clear with is the second binary -> bit part. I was not able to find this behavior explained in the documentation, where only
Converting to bit promotes any nonzero value to 1.
is stated.
binary is not a number, it's a string of bytes. When you cast binary to another type, a conversion is performed. When binary is longer than the target data-type, it is truncated from the left. When it's shorter than the target, it is padded with zeroes from the left. The exception is when casting to another string type (e.g. varchar or another binary) - there it's padding and truncation from the right, which may be a bit confusing at first :)
So what happens here?
select cast(cast(0x0F as binary(1)) as bit) -- 1 - 0x0F is nonzero
select cast(cast(0x01 as binary(1)) as bit) -- 1 - 0x01 is nonzero
select cast(cast(0x01 as binary(2)) as bit) -- 0 - truncated to 0x00, which is zero
select cast(cast(0x0100 as binary(2)) as bit) -- 0 - truncated to 0x00
select cast(cast(0x0001 as binary(2)) as bit) -- 1 - truncated to 0x01, nonzero
As the documentation says:
When data is converted from a string data type (char, varchar, nchar, nvarchar, binary, varbinary, text, ntext, or image) to a binary or varbinary data type of unequal length, SQL Server pads or truncates the data on the right. When other data types are converted to binary or varbinary, the data is padded or truncated on the left. Padding is achieved by using hexadecimal zeros.
Which is something you can use, because:
select cast(0x0100 as binary(1)) -- 0x01
So if you need non-zero on the whole value, you basically need to convert to an integer data type, if possible. If you want the rightmost byte, use cast as bit, and if you want the leftmost, use cast as binary(1). Any other can be reached by using the string manipulation functions (binary is a string, just not a string of characters). binary doesn't allow you to do something like 0x01000 = 0 - that includes an implicit conversion to int (in this case), so the usual rules apply - 0x0100000000 = 0 is true.
Also note that there are no guarantees that conversions from binary are consistent between SQL server versions - they're not really managed.
Yes, in general when converting from an arbitrary length binary or varbinary value to a fixed size type, it's the rightmost bits or bytes that are converted:
select
CAST(CAST(0x0102030405060708 as bigint) as varbinary(8)),
CAST(CAST(0x0102030405060708 as int) as varbinary(8)),
CAST(CAST(0x0102030405060708 as smallint) as varbinary(8)),
CAST(CAST(0x0102030405060708 as tinyint) as varbinary(8))
Produces:
------------------ ------------------ ------------------ ------------------
0x0102030405060708 0x05060708 0x0708 0x08
I can't actually find anywhere in the documentation that specifically states this, but there again, the documentation does basically state that conversions between binary and other types is not guaranteed to follow any specific conventions:
Converting any value of any type to a binary value of large enough size and then back to the type, will always result in the same value if both conversions are taking place on the same version of SQL Server. The binary representation of a value might change from version to version of SQL Server.
So, the above shown conversions were the "expected" results running on SQL Server 2012, on my machine, but others may get different results.

Why I got an int datatype when using conditional case?

Consider the following Sql snippet:
declare #param as int
set #param=1
select
case when #param=1 then '987' else '' end as Value1,
case when #param=2 then 987 else '' end as Value2,
case when #param<>2 then '' else 987 end as Value3
I would expect to get 1 row with 3 fields: The string 987 and 2 empty strings but I get the following result:
Can you explain me please why I got a 0 value? I know it is because the column is interpreted as integer datatype but not sure the reason why.
At the moment of writing the question I found the answer:
This behaviour is due to the Data Type Precedence
When an operator combines two expressions of different data types, the rules for data type precedence specify that the data type with the lower precedence is converted to the data type with the higher precedence. If the conversion is not a supported implicit conversion, an error is returned. When both operand expressions have the same data type, the result of the operation has that data type.
for more information
https://msdn.microsoft.com/en-us/library/ms190309.aspx

How do I do decimal arithmetic on two varchars and return result to an aliased column?

I have two fields of type varchar that contain numeric values or blank strings, the latter of which I have filtered out to avoid Divide by Zero errors.
I am attempting to determine the percentage value that num2 represents in relation to num1, i.e. (Num_2 * 1 / Num_1). Relatively simple math.
The problem I am having is that I cannot seem to do the math and then cast it to a decimal value. I keep receiving Arithmetic overflow error converting int to data type numeric errors.
Can someone help me out with the casting issue?
You didn't interpret the error correctly.
It is not about casting the result of your math to float, it is about implicit type casting before the equation is evaluated.
You have in your table some values that cannot be converted to numeric, because they are not valid numbers or numbers out of range. It is enough that one row contains invalid data to make fail the whole query.
perhaps you're looking for something similar to this?
declare #table table (
[numerator] [sysname]
, [denominator] [sysname]);
insert into #table
([numerator],[denominator])
values (N'1',N'2'),
(N'9999999999',N'88888888888');
select case
when isnumeric([numerator]) = 1
and isnumeric ([denominator]) = 1
then
cast([numerator] as [float]) / [denominator]
else
null
end
from #table;
Is this what you're looking for?
select cast('25.5' as decimal(15, 8)) / cast('100.0' as decimal(15, 8))
The example above will return this:
0.25500000000000000000000
In this case, I'm converting the operand types before they get used in the division.
Remember to replace the literals in my query by your field names.
you said that can be number or blank string.
son try something like this:
SELECT
(CASE WHEN NUM_2 = '' THEN 0 ELSE CAST(NUM_2 AS NUMERIC(15,4)) END)
/
(CASE WHEN NUM_1 = '' THEN 1 ELSE CAST(NUM_1 AS NUMERIC(15,4)) END)
you test if string is blank. if it is, you use 0 (or 1, to avoid division by zero)

Resources