A strange operation problem in SQL Server: -100/-100*10 = 0 - sql

If you execute SELECT -100/-100*10 the result is 0.
If you execute SELECT (-100/-100)*10 the result is 10.
If you execute SELECT -100/(-100*10) the result is 0.
If you execute SELECT 100/100*10 the result is 10.
BOL states:
When two operators in an expression have the same operator precedence level, they are evaluated left to right based on their position in the expression.
And
Level Operators
1 ~ (Bitwise NOT)
2 * (Multiplication), / (Division), % (Modulus)
3 + (Positive), - (Negative), + (Addition), + (Concatenation), - (Subtraction), & (Bitwise AND), ^ (Bitwise Exclusive OR), | (Bitwise OR)
Is BOL wrong, or am I missing something? It seems the - is throwing the (expected) precedence off.

According to the precedence table, this is the expected behavior. The operator with higher precedence (/ and *) is evaluated before operator with lower precedence (unary -). So this:
-100 / -100 * 10
is evaluated as:
-(100 / -(100 * 10))
Note that this behavior is different from most programming languages where unary negation has higher precedence than multiplication and division e.g. VB, JavaScript.

BOL is correct. - has lower precedence than *, so
-A * B
is parsed as
-(A * B)
Multiplication being what it is, you don't typically notice this, except when mixing in the two other binary operators with equal precedence: / and % (and % is rarely used in compound expressions like this). So
C / -A * B
Is parsed as
C / -(A * B)
explaining the results. This is counter-intuitive because in most other languages, unary minus has higher precedence than * and /, but not in T-SQL, and this is documented correctly.
A nice (?) way to illustrate it:
SELECT -1073741824 * 2
produces an arithmetic overflow, because -(1073741824 * 2) produces 2147483648 as an intermediate, which does not fit in an INT, but
SELECT (-1073741824) * 2
produces the expected result -2147483648, which does.

Notice in the documentation that (perhaps counter-intuitively) the order of precedence for - (Negative) is third.
So you effectively get:
-(100/-(100*10)) = 0
If you place them into variables you won't see this happening, as there is no unary operation that occurs after the multiplication.
So here A and B are the same, whereas C, D, E show the result you are seeing (with E having the complete bracketing)
DECLARE #i1 int, #i2 int, #i3 int;
SELECT #i1 = -100,
#i2 = -100,
#i3 = 10;
SELECT #i1/#i2*#i3 [A],
-100/(-100)*10 [B],
-100/-100*10 [C],
-100/-(100*10) [D],
-(100/-(100*10)) [E];
A - 10
B - 10
C - 0
D - 0
E - 0

Related

SQL order of operation does not make sense with simple mathematical calculation

I have a simple calculation to do in a stored procedure. Depending on the order I put the variables I get a different result
If you copy/past this into SQL Query Analyzer you can easily reproduce the issue where I get a different result. The result I was looking for was the second calculation (57364.32)
DECLARE #mnyDocTotal MONEY;
DECLARE #mnyUSDTotal MONEY
DECLARE #mnyDetailLine MONEY
SET #mnyDocTotal = 78000
SET #mnyUSDTotal = 86046.48
SET #mnyDetailLine = 52000
PRINT 'Result: ' + CAST(ROUND(#mnyDetailLine / #mnyDocTotal * #mnyUSDTotal,2) as char(20))
PRINT 'Result: ' + CAST(ROUND(#mnyDetailLine * #mnyUSDTotal / #mnyDocTotal,2) as char(20))
--Result: 57358.58
--Result: 57364.32
I believe that / and * are on the same level and operate from left to right in this case.
If you run the numbers with a calculator, you will always get 57364.32.
This caused me about 2 hours of effort to figure this out. In all my years I've never had this issue occur. Why is the result different?
This article does a pretty good job explaining why you should not use money.
They are not numeric values. They are stored as integers. And they have rounding problems. So:
(a * b) / c
can product a different result due to rounding from:
(a / c) * b
This is actually true of integers in general, as this simple example illustrates:
select (2 * 4 / 3), (2 / 3 * 4)
If you use numeric, you won't have a problem. Here is a db<>fiddle.

SQL assign variable with subquery

I have a question for following 2 SQL:
declare #i1 bit, #b1 bit
declare #i2 bit, #b2 bit
declare #t table (Seq int)
insert into #t values (1)
-- verify data
select case when (select count(1) from #t n2 where 1 = 2) > 0 then 1 else 0 end
-- result 0
select #i1 = 1, #b1 = case when #i1 = 1 or ((select count(1) from #t n2 where 1 = 2) > 0) then 1 else 0 end from #t n where n.Seq = 1
select #i1, #b1
-- result 1, 0
select #i2 = 1, #b2 = case when #i2 = 1 or (0 > 0) then 1 else 0 end from #t n where n.Seq = 1
select #i2, #b2
-- result 1, 1
SQL Fiddle Here
Before the execute, I thought the case part should be null = 1 or (0 > 0), and it will return 0.
But now, I wondering why the 2nd SQL will return 1
Just to extend #Giorgi's answer:
See this execution plan:
Since #i2 is evaluated first (#i2=1), case when #i2 = 1 or anything returns 1.
See also this msdn entry: https://msdn.microsoft.com/en-us/library/ms187953.aspx and Caution section
If there are multiple assignment clauses in a single SELECT statement,
SQL Server does not guarantee the order of evaluation of the
expressions. Note that effects are only visible if there are
references among the assignments.
It's all related to internal optimization.
I will post this as an answer as it is quite large text from Training Kit (70-461):
WHERE propertytype = 'INT' AND CAST(propertyval AS INT) > 10
Some assume that unless precedence rules dictate otherwise, predicates
will be evaluated from left to right, and that short circuiting will
take place when possible. In other words, if the first predicate
propertytype = 'INT' evaluates to false, SQL Server won’t evaluate the
second predicate CAST(propertyval AS INT) > 10 because the result is
already known. Based on this assumption, the expectation is that the
query should never fail trying to convert something that isn’t
convertible.
The reality, though, is different. SQL Server does
internally support a short-circuit concept; however, due to the
all-at-once concept in the language, it is not necessarily going to
evaluate the expressions in left-to-right order. It could decide,
based on cost-related reasons, to start with the second expression,
and then if the second expression evaluates to true, to evaluate the
first expression as well. This means that if there are rows in the
table where propertytype is different than 'INT', and in those rows
propertyval isn’t convertible to INT, the query can fail due to a
conversion error.
Just to extend both answers.
From Dirty Secrets of the CASE Expression:
CASE will not always short circuit
The official documentation implies that the entire expression will short-circuit, meaning it will evaluate the expression from left-to-right, and stop evaluating when it hits a match:
The CASE statement evaluates its conditions sequentially and stops with the
first condition whose condition is satisfied.
And MS Connect:
CASE / COALESCE won't always evaluate in textual order
Aggregates Don't Follow the Semantics Of CASE
CASE Transact-SQL
The CASE statement evaluates its conditions sequentially and stops with the first condition whose condition is satisfied. In some situations, an expression is evaluated before a CASE statement receives the results of the expression as its input. Errors in evaluating these expressions are possible.

SQL decimals inaccurate for floating point value [duplicate]

Debugging some finance-related SQL code found a strange issue with numeric(24,8) mathematics precision.
Running the following query on your MSSQL you would get A + B * C expression result to be 0.123457
SELECT A,
B,
C,
A + B * C
FROM
(
SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A,
CAST(0 AS NUMERIC(24,8)) AS B,
CAST(500 AS NUMERIC(24,8)) AS C
) T
So we have lost 2 significant symbols. Trying to get this fixed in different ways i got that conversion of the intermediate multiplication result (which is Zero!) to numeric (24,8) would work fine.
And finally a have a solution. But still I hace a question - why MSSQL behaves in this way and which type conversions actually occured in my sample?
Just as addition of the float type is inaccurate, multiplication of the decimal types can be inaccurate (or cause inaccuracy) if you exceed the precision. See Data Type Conversion and decimal and numeric.
Since you multiplied NUMERIC(24,8) and NUMERIC(24,8), and SQL Server will only check the type not the content, it probably will try to save the potential 16 non-decimal digits (24 - 8) when it can't save all 48 digits of precision (max is 38). Combine two of them, you get 32 non-decimal digits, which leaves you with only 6 decimal digits (38 - 32).
Thus the original query
SELECT A, B, C, A + B * C
FROM ( SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A,
CAST(0 AS NUMERIC(24,8)) AS B,
CAST(500 AS NUMERIC(24,8)) AS C ) T
reduces to
SELECT A, B, C, A + D
FROM ( SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A,
CAST(0 AS NUMERIC(24,8)) AS B,
CAST(500 AS NUMERIC(24,8)) AS C,
CAST(0 AS NUMERIC(38,6)) AS D ) T
Again, between NUMERIC(24,8) and NUMERIC(38,6), SQL Server will try to save the potential 32 digits of non-decimals, so A + D reduces to
SELECT CAST(0.12345678 AS NUMERIC(38,6))
which gives you 0.123457 after rounding.
Following the logic pointed out by eed3si9n and what you said in your question it seems that the best approach when doing mathematics operations is to extract them into a function and additionally to specify precision after each operation,
It this case the function could look something like:
create function dbo.myMath(#a as numeric(24,8), #b as numeric(24,8), #c as numeric(24,8))
returns numeric(24,8)
as
begin
declare #d as numeric(24,8)
set #d = #b* #c
return #a + #d
end
Despite what it says on Precision, Scale, and Length (Transact-SQL). I believe it is also applying a minimum 'scale' (number of decimal places) of 6 to the resulting NUMERIC type for multiplication the same as it does for division etc.

SQL Server case statement altering value accuracy

We have a fairly complicated SQL Server 2008 r2 sp2 query with this as one of the lines :-
SUM((t.Quantity * contract.ValueOfOnePoint) * ((
CASE contract.Style
WHEN 3
THEN 1 / (1.0 + ((100.0 - Val) / 100.0 * 90.0 / 365.0))
WHEN 2
THEN 1000 * (6.0 * (1.0 - (POWER((1.0 / (1.0 + ((100.0 - Val) / 200.0))), 20.0))) / ((100.0 - Val) / 200.0) + (100.0 * (POWER((1.0 / (1.0 + ((100.0 - Val) / 200.0))), 20.0))))
END
) - (
CASE contract.Style
WHEN 3
THEN 1.0 / (1.0 + ((100.0 - t.Price) / 100.0 * 90.0 / 365.0))
WHEN 2
THEN 1000 * (6.0 * (1.0 - (POWER((1.0 / (1.0 + ((100.0 - t.Price) / 200.0))), 20.0))) / ((100.00 - t.Price) / 200.00) + (100.0 * (POWER((1.0 / (1.0 + ((100.0 - t.Price) / 200.0))), 20.0))))
END
)
)) AS NativeAmount
I am testing this on a single row which has a style of 3 so only the first line in the case statement should have any affect yet leaving the "WHEN 2" clause in it reduces the accuracy of the formula.
Eg. if I remove both WHEN 2 conditions I get an answer such 123.45678 but with the WHEN 2 line left in I get 123.46. It seems to be rounding for some reason even though the second WHEN should never be in-play.
Any thoughts would be really appreciated - going mad!
Thanks.
James.
You need to combine two things. The return type of the case statement is the same for all the then and else clauses. This is a quote from the documentation:
[The case statement] 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).
So, the SQL Engine does care about all the clauses in the query (which is the answer to your question).
I don't fully understand what is happening in this case. When you call the power() function, the compiler has to decide on the precision of the numeric value, based on the constants and column types. Based on this SQL Fiddle, it chooses a precision of 38 and a scale of 1. However, simple arithmetic on the values produces a precision of 36 and a scale of 23. I'm not sure why, in the end, this results in rounding the value to two decimal places. Perhaps the logic for assigning types for the into clause doesn't quite match the logic for typing of expressions.

Execute a WHERE clause before another one

I have the following statement
SELECT * FROM foo
WHERE LEN(bar) = 4 AND CONVERT(Int,bar) >= 5000
The values in bar with a length of exactly 4 characters are integers. The other values are not integers and therefore it throws an conversion exception, when trying to convert one of them to an integer.
I thought it's enough to put the LEN(bar) before the CONVERT(Int,bar) >= 5000. But it's not.
How can I kind of prioritize a specific where clause? In my example I obviously want to select all values with a length of 4, before converting and comparing them.
6 answers and 5 of them don't work (for SQL Server)...
SELECT *
FROM foo
WHERE CASE WHEN LEN(bar) = 4 THEN
CASE WHEN CONVERT(Int,bar) >= 5000 THEN 1 ELSE 0 END
END = 1;
The WHERE/INNER JOIN conditions can be executed in any order that the query optimizer determines is best. There is no short-circuit boolean evaluation.
Specifically for your question, since you KNOW that the data with 4-characters is a number, then you can do a direct lexicographical (text) comparison (yes it works):
SELECT *
FROM foo
WHERE LEN(bar) = 4 AND bar > '5000';
try this
SELECT bar FROM
(
SELECT CASE
WHEN LEN(bar) = 4 THEN CAST( bar as int)
ELSE CAST(-1 as int) END bar
FROM Foo
) Foo
WHERE bar>5000