1
SET COUNTDECIMALPLACES =  
    CASE Charindex('.', NEWNUMBER)
        WHEN 0 THEN 0
        ELSE
          LEN(
            CAST(
                 CAST(
                    REVERSE( NEWNUMBER ) AS float
                     ) AS bigint
                ) 
           )
    END   

如果数字是 222.9375,它会错误地说 COUNTDECIMALPLACES = 3。但是,如果数字是 17.5548,它会正确地说 COUNTDECIMALPLACES = 4。有人知道为什么吗?

编辑 1:回答我正在使用 SQL Server 2008 (MS) 的评论中的问题。看起来 Gordon Linoff 遇到了和我一样的错误。此外,NEWNUMBER 的数据类型是浮点数。鉴于我有浮点数,我的最终目标是计算小数点右侧的小数位数。

4

1 回答 1

2

你有 3 代表 222.9375 那是因为REVERSE接受一个字符串表达式。通过传入一个浮点数,SQL Server 必须进行从浮点数到 varchar 的隐式转换。

根据在线图书:

转换和转换 (Transact-SQL)

float 和 real 样式
当表达式为 float 或 real 时,style 可以是下表中显示的值之一。其他值被处理为 0。


0(默认)

输出
最多 6 位。适当时以科学记数法使用。

可以使用以下示例将上述定义可视化:

declare @a float = 222.9375, @b float = 12.34564, @c float = 1234564.123456
select convert(varchar, @a, 0) -- Returns 222.938 -- Notice only 6 digits are displayed
    , convert(varchar, @b, 0) -- Returns 12.3456 -- Notice only 6 digits are displayed
    , convert(varchar, @c, 0) -- Returns 1.23456e+006 -- Notice only 6 digits are displayed

如果您继续使用浮点数作为您的数据类型,那么即使您float转换varchar并执行字符串操作,您也不能期望得到您正在寻找的确切数字。

您甚至不能简单地float转换decimal,因为您不知道要分配给小数点的小数位数(比例)。

Alternatively, if you start with decimal as the data type, then you automatically know how many decimal places a number has because you had to declare it.

For example

declare @number decimal(19, 4) = 222.9375
select @number -- 222.9375

Let's take it one step further. To work out the scale (number of decimal places) after some sort of calculation, you can employ the function below to get the answer:

declare @newnumber decimal(19, 4) = 222.9375    

select @newnumber * @newnumber -- Returns 49701.1289063
    , sql_variant_property(@newnumber * @newnumber, 'scale') -- Returns 7

However, you will most likely have no control over the data type used, one way I can think of is by combining the usage of both decimal and varchar to achieve what you want.

declare @newnumber float = 222.9375

-- The first 6 columns are just the working steps, solution is in the last column.
select 
    @newnumber as 'original'
    , cast(@newnumber as decimal(38, 18)) as 'decimal' -- Assuming all your numbers can fit in decimal(38, 18).
    , cast(cast(@newnumber as decimal(38, 18)) as varchar(max)) as 'varchar'
    , reverse(cast(cast(@newnumber as decimal(38, 18)) as varchar(max))) as 'reverse'
    , cast(reverse(cast(cast(@newnumber as decimal(38, 18)) as varchar(max))) as decimal(38, 0)) as 'decimal'
    , len(cast(reverse(cast(cast(@newnumber as decimal(38, 18)) as varchar(max))) as decimal(38, 0))) as 'len'

    , case charindex('.', @newnumber)
        when 0 then 0
        else len(cast(reverse(cast(cast(@newnumber as decimal(38, 18)) as varchar(max))) as decimal(38, 0)))
    end as 'All In One Step'
于 2013-05-09T04:53:15.223 回答