-- Transact SQL: case when 1=1 then 0.5 else ceiling(sh) end /* returns 1 (!) why? */
declare @T table (h decimal(2,1))
insert @T (h) values (1.0)
select
case when 1=1 then 0.5 else ceiling(sh) end /* returns 1 (!) why? */
from @T T1
join (select sum(h) as sh from @T )T2 on 1 = 1
-
3it is converting to int – Hogan Nov 30 '18 at 15:32
-
@hogan why? I always thought the `CASE` took the first case type for the whole case? – Juan Carlos Oropeza Nov 30 '18 at 15:36
-
2Because ceiling(sh) returns int -- in a case where both sides of the case are different it will "upgrade" to int. see @JuanCarlosOropeza code in his answer – Hogan Nov 30 '18 at 15:38
-
2Actually, it returns decimal(38,0) – Andrey Nikolov Nov 30 '18 at 15:38
-
2@Hogan it doesn't return int and int doesn't have higher datatype precedence than decimal – Martin Smith Nov 30 '18 at 15:38
-
1yep... decimal not int. that is more correct. – Hogan Nov 30 '18 at 15:39
-
`select sum(h) as sh from @T` returns `numeric(38,1)` so not sure why the 1 precision gets lost – Martin Smith Nov 30 '18 at 15:40
-
because it is not two sides of a case @MartinSmith. case handling was always a pain. – Hogan Nov 30 '18 at 15:41
-
2oh that's the `CEILING` of course that makes it `numeric(38,0)` – Martin Smith Nov 30 '18 at 15:46
-
@Hogan Yes I wrote that answer :) ... I know how to fix the problem. But I mean I though the CASE statement go for the first case to get the type, Like when you use `UNION`. So what is the logic to choose the datatype for the `CASE` – Juan Carlos Oropeza Nov 30 '18 at 15:48
-
2https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-type-precedence-transact-sql?view=sql-server-2017 – Martin Smith Nov 30 '18 at 15:48
3 Answers
The answer is nothing to do with the int
datatype
- The literal
0.5
has a dataype ofdecimal(1,1)
- Applying
CEILING
on adecimal(p,s)
returns a result of typedecimal(p,0)
- Applying
SUM
on adecimal(p,s)
returns a result of typedecimal(38,s)
- With a mixed
CASE
expression that can returndecimal(p1,s1)
ordecimal(p2,s2)
the result will use the same rules as whenUNION
-ing these data types and have precision (*
) ofmax(s1, s2) + max(p1-s1, p2-s2)
and scale ofmax(s1, s2)
*
The result precision and scale have an absolute maximum of 38. When a result precision is greater than 38, it is reduced to 38, and the corresponding scale is reduced to try to prevent the integral part of a result from being truncated. (source)
So your column h
has datatype of decimal(2,1)
, the datatype when SUM
is applied is decimal(38,1)
, the datatype of CEILING
applied to that is decimal(38,0)
. Then you use that in a CASE
expression with decimal(1,1)
max(s1, s2) + max(p1-s1, p2-s2)
max( 0, 1) + max( 38, 0) = 1 + 38 = 39
And
max(s1, s2) = max(0, 1) = 1
So the desired result datatype would be decimal(39,1)
. This is larger than 38 so you get the scale reduction described above and end up with decimal(38,0)
- 0.5
is rounded to 1
when cast to that datatype.
If you would rather keep the precision of the final result you can use
case when 1=1 then 0.5 else CAST(ceiling(sh) AS decimal(38,1)) end
There is a miniscule additional risk of overflow with this but to be hit by it the sum would need to add up to one of the following values
9999999999999999999999999999999999999.5
9999999999999999999999999999999999999.6
9999999999999999999999999999999999999.7
9999999999999999999999999999999999999.8
9999999999999999999999999999999999999.9
such that the SUM
itself fits into 38,1
but CEILING
doesn't.

- 438,706
- 87
- 741
- 845
More explanation:
select CEILING(170.00/6.00),CEILING(170/6),170.00/6.00,170/6
Result:
29 28 28.333333 28

- 185
- 1
- 1
- 10
As explain in the documentation
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.
In your case is converted to int
because has the bigger precedence.
declare @T table (h decimal(2,1))
insert @T (h) values (1.0)
select
case when 1=1 then 0.5 else ceiling(sh)*1.0 end -- << convert to float
from @T T1
join (select sum(h) as sh from @T )T2 on 1 = 1

- 47,252
- 12
- 78
- 118
-
1
-
Not sure, but it might have to do something with the `float`. A proper solution would not leave the `decimal` domain. – GSerg Nov 30 '18 at 15:52
-
@GSerg So maybe use cast on both statement to make sure has the same type? Just wondering how you can know what type is 0.5 converted in this case? I cant tell in my demo – Juan Carlos Oropeza Nov 30 '18 at 16:03
-
1No, it is converted to `decimal(38,0)` because [this is what `ceiling` returns](https://stackoverflow.com/questions/53560374/transact-sql-case-when-1-1-then-0-5-else-ceilingsh-end-returns-1-why#comment93987029_53560374). You are supposed to cast only the `ceiling` part back to a `decimal` with a decimal position. – GSerg Nov 30 '18 at 16:03
-
@GSerg According to [documentation](https://learn.microsoft.com/en-us/sql/t-sql/functions/ceiling-transact-sql?view=sql-server-2017) ceiling `Return values have the same type as numeric_expression.` how you know is `decimal(38,0)` ? – Juan Carlos Oropeza Nov 30 '18 at 16:05
-
4I know because Martin Smith [said so](https://stackoverflow.com/questions/53560374/transact-sql-case-when-1-1-then-0-5-else-ceilingsh-end-returns-1-why#comment93987029_53560374) and because `exec sp_describe_first_result_set N'select ceiling(sum(h)) from (values (cast(0.5 as decimal(2,1)))) T1(h)';` confirms it. Ceiling keeps the base type (numeric) but changes the precision. First `sum()` bumps it up to `(38,1)`, then `ceiling` rounds it down to `(38,0)`. – GSerg Nov 30 '18 at 16:07
-
@GSerg ok, didnt knew about `exec sp_describe_first_result_set` still not sure why ceiling remove the precision when the documentation said will keep the same data type. Or why people keep downvoting :/ – Juan Carlos Oropeza Nov 30 '18 at 16:12
-
2After replacing ceiling(sh) by ceiling(h), the result is ok, 0.5. The question is, why ceiling(sh) works not equally to ceiling(h) – Petr Kral Nov 30 '18 at 16:16
-
Ok, let me dig a litle big. But you ask why return 1 and I already answer that isnt? – Juan Carlos Oropeza Nov 30 '18 at 16:22
-
-
3@PetrKral Because `ceiling(h)` gives `decimal(2,0)`, and between `(2,0)` and `(2,1)` the resulting type is `(3,1)`. On contrary, `sum()` pushes the size up to the maximum `(38)`, there cannot be `(39,1)`, so it ends up being `(38,0)`. – GSerg Nov 30 '18 at 16:48