简体   繁体   English

为什么我的SQL代码无法正确计算小数点后的小数位数?

[英]How come my SQL code does not correctly count the number of decimal places after the decimal?

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

If the number is 222.9375 it will incorrectly say that COUNTDECIMALPLACES = 3. But, if the number is 17.5548 it will correctly say that COUNTDECIMALPLACES = 4. Anybody know why? 如果数字是222.9375,则将错误地说COUNTDECIMALPLACES =3。但是,如果数字是17.5548,则将正确地说COUNTDECIMALPLACES =4。有人知道为什么吗?

EDIT 1: TO answer the questions in the comments I am using SQL Server 2008 (MS). 编辑1:要回答评论中的问题,我正在使用SQL Server 2008(MS)。 It looks like Gordon Linoff got the same error as I did. 看起来Gordon Linoff遇到了与我相同的错误。 Also, the data type of NEWNUMBER is float. 另外,NEWNUMBER的数据类型为float。 Given that I have floats, my ultimate goal is to count the number of decimal places to the right of the decimal. 鉴于我有浮点数,所以我的最终目标是计算小数点右边的小数位数。

You have got 3 for 222.9375 that's because REVERSE accepts a string expression. 您的222.9375中有3个,这是因为REVERSE接受字符串表达式。 By passing in a float, SQL Server has to do an implicit conversion from float to varchar. 通过传递浮点数,SQL Server必须执行从浮点数到varchar的隐式转换。

According to Books Online: 根据在线书籍:

CAST and CONVERT (Transact-SQL) CAST和CONVERT(Transact-SQL)

float and real Styles 浮动和真实风格
When expression is float or real, style can be one of the values shown in the following table. 当expression为float或real时,style可以是下表中显示的值之一。 Other values are processed as 0. 其他值被处理为0。

Value
0 (default) 0(默认)

Output 产量
A maximum of 6 digits. 最多6位数字。 Use in scientific notation, when appropriate. 适当时以科学计数法使用。

The above definition can be visualised using the examples below: 可以使用以下示例将上述定义可视化:

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

If you keep using float as your data type, then you cannot expect to get the exact number you are looking for, even if you cast float into varchar and carry out string manipulation. 如果您继续使用float作为数据类型,那么即使将float转换为varchar并执行字符串操作,也无法期望获得确切的编号。

You cannot even simply cast float into decimal as you don't know how many decimal places (scale) to assign to the decimal. 您甚至不能简单地将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. 但是,您很可能无法控制所使用的数据类型,我想到的一种方法是通过结合使用decimalvarchar来实现所需的数据。

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'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM