【问题标题】:How do I count decimal places in SQL?如何计算 SQL 中的小数位数?
【发布时间】:2013-01-20 19:53:47
【问题描述】:

我有一个 X 列,其中充满了浮点数,小数位从 0(无小数)到 6(最大值)不等。我可以指望没有超过 6 位小数的浮点数。鉴于此,我如何创建一个新列,以便它告诉我小数点后有多少位?

我看到一些线程建议我使用 CAST 将浮点数转换为字符串,然后解析字符串以计算小数点后字符串的长度。这是最好的方法吗?

【问题讨论】:

  • @Leniel,注意你的标签。你刚刚重新创建了float,我在过去的 48 小时内再次使用了它。 :p
  • @Charles 对不起,Charles...下次会睁大眼睛! :)

标签: sql sql-server-2008 floating-point decimal scale


【解决方案1】:

这个问题涉及常规 SQL,但我需要 SQLite 的解决方案。 SQLite 既没有 log10 函数,也没有内置的反向字符串函数,所以这里的大多数答案都不起作用。我的解决方案类似于Art's answer,事实上,类似于 phan 在问题正文中描述的内容。它的工作原理是将浮点值(在 SQLite 中,一个“REAL”值)转换为文本,然后计算小数点后的字符。

对于名为“Table”的表中名为“Column”的列,以下查询将生成每行小数位数的计数:

select
length(
    substr(
        cast(Column as text),
        instr(cast(Column as text), '.')+1
    )
) as "Column-precision" from "Table";

代码会将列转换为文本,然后获取文本中句点 (.) 的索引,并获取从该点到文本末尾的子字符串。然后,它计算结果的长度。

如果您不希望它运行整个表,请记住limit 100

这不是一个完美的解决方案;例如,它认为 "10.0" 有 1 个小数位,即使它只是一个 0。但是,这实际上是我需要的,所以这不是我关心的问题。

希望这对某人有用:)

【讨论】:

    【解决方案2】:

    我注意到 Kshitij Manvelikar 的回答有一个错误。如果没有小数位,则不返回 0,而是返回数字中的字符总数。

    所以改进它:

    Case When (SomeNumber = Cast(SomeNumber As Integer)) Then 0 Else LEN(PARSENAME(Cast(SomeNumber as float),1)) End
    

    【讨论】:

      【解决方案3】:

      我发现的另一种方法是

      SELECT 1.110000 , LEN(PARSENAME(Cast(1.110000 as float),1)) AS Count_AFTER_DECIMAL

      【讨论】:

        【解决方案4】:

        Oracle 的解决方案,但您明白了。 trunc() 删除 Oracle 中的小数部分。

        select *
        from your_table
        where (your_field*1000000 - trunc(your_field*1000000)) <> 0;
        

        查询思路:乘以 1 000 000 后是否还有小数。

        【讨论】:

          【解决方案5】:

          我之前回答过这个问题,但我可以从 cmets 看出它有点不清楚。随着时间的推移,我找到了更好的表达方式。

          将 pi 视为

          (a) 3.141592653590
          

          这将 pi 显示为 11 位小数。然而这被四舍五入到小数点后 12 位,因为 pi,到 14 位是

          (b) 3.1415926535897932
          

          计算机或数据库以二进制形式存储值。对于单精度浮点数,pi 将存储为

          (c) 3.141592739105224609375
          

          这实际上是四舍五入到单精度可以存储的最接近的值,就像我们在 (a) 中四舍五入一样。单精度可以存储的下一个最小数字是

          (d) 3.141592502593994140625
          

          因此,当您尝试计算小数位数时,您会尝试找出小数位数,然后所有剩余的小数将为零。但是,由于数字可能需要四舍五入才能存储,它并不代表正确的值。

          数字在进行数学运算时也会引入舍入误差,包括在输入数字时从十进制转换为二进制,在显示值时从二进制转换为十进制。

          您无法可靠地找到数据库中数字的小数位数,因为它被近似为四舍五入以存储在有限的存储空间中。实际值之间的差异,甚至数据库中的精确二进制值将四舍五入以十进制表示。四舍五入时总是会丢失更多的小数位,所以你不知道零之后什么时候不会有更多的非零数字。

          【讨论】:

            【解决方案6】:

            你可以这样使用:

            declare @v sql_variant
            
            set @v=0.1242311
            
            select SQL_VARIANT_PROPERTY(@v, 'Scale') as Scale
            

            这将返回7


            我尝试使上述查询与float 列一起工作,但无法按预期工作。它仅适用于 sql_variant 列,您可以在此处看到:http://sqlfiddle.com/#!6/5c62c/2

            所以,我继续寻找另一种方法并在此 answer 的基础上,我得到了这个:

            SELECT value,
            LEN(
                CAST(
                     CAST(
                          REVERSE(
                                  CONVERT(VARCHAR(50), value, 128)
                                 ) AS float
                         ) AS bigint
                    )
               ) as Decimals
            FROM Numbers
            

            这是一个用于测试的 SQL Fiddle:http://sqlfiddle.com/#!6/23d4f/29


            为了解决这个小怪癖,这里有一个修改版本,可以处理浮点值没有小数部分的情况:

            SELECT value,
                   Decimals = CASE Charindex('.', value)
                                WHEN 0 THEN 0
                                ELSE
                       Len (
                        Cast(
                         Cast(
                          Reverse(CONVERT(VARCHAR(50), value, 128)) AS FLOAT
                             ) AS BIGINT
                            )
                           )
                                END
            FROM   numbers
            

            这是随附的 SQL Fiddle:http://sqlfiddle.com/#!6/10d54/11

            【讨论】:

            • 很酷的把戏,虽然我认为你的意思是Scale,而不是Precision
            • 你能在一列浮点数上使用它吗?
            • @TimLehner Afaik 没有直接的 SQL 查询,set 将变量类型设置为具有 7 位小数的数字(即存储值所需的最小值),Scale 将返回 datatype 的精度,而不是值。当您从表格列中选择时,您将获得列 type 的比例,而不是实际数字的比例。
            • 我刚刚发现了一个小怪癖。如果我的问题中提到了零小数,那么值 1、12、123 和 1234 将产生 1、2、3、4 作为结果,而实际上结果应该是 0。基本上,反转和转换为 bigint如果问题中提到的小数位数为 0,则砍掉数字不起作用。我对吗?谢谢。
            • 我在使用 CHARINDEX 方法时得到了一些误报。进行RIGHT(LTRIM(STR(MyColumn, 50, 10)), 10) = '0000000000' 检查似乎效果更好。
            【解决方案7】:

            这是另一个 Oracle 示例。正如我总是在非 Oracle 用户开始对我大喊大叫和投票等之前警告他们...... SUBSTRING 和 INSTRING 是 ANSI SQL 标准函数,可以在任何 SQL 中使用。 Dual 表可以用任何其他表替换或创建。这是我从以下位置复制双表代码的 SQL SERVER 博客的链接:http://blog.sqlauthority.com/2010/07/20/sql-server-select-from-dual-dual-equivalent/

            CREATE TABLE DUAL
            (
             DUMMY VARCHAR(1)
            )
            GO
            INSERT INTO DUAL (DUMMY)
            VALUES ('X')
            GO
            

            此查询返回点或小数位后的长度。 如果需要,可以将 str 转换为_number(str)。您还可以在点小数位之前获取字符串的长度 - 将代码更改为 LENGTH(SUBSTR(str, 1, dot_pos))-1 并删除 INSTR 部分中的 +1:

            SELECT str, LENGTH(SUBSTR(str, dot_pos)) str_length_after_dot FROM
            (
             SELECT '000.000789' as str
                  , INSTR('000.000789', '.')+1 dot_pos 
               FROM dual
            )
            /
            
            SQL>
            
            STR           STR_LENGTH_AFTER_DOT
            ----------------------------------
            000.000789    6
            

            你已经有了关于选角等的答案和例子……

            【讨论】:

              【解决方案8】:

              这个帖子也用了 CAST,不过我发现这个答案很有趣:

              http://www.sqlservercentral.com/Forums/Topic314390-8-1.aspx

              DECLARE @Places INT
               SELECT TOP 1000000 @Places = FLOOR(LOG10(REVERSE(ABS(SomeNumber)+1)))+1
                 FROM dbo.BigTest
              

              在 ORACLE 中:

              SELECT FLOOR(LOG(10,REVERSE(CAST(ABS(.56544)+1 as varchar(50))))) + 1 from DUAL
              

              【讨论】:

              • 有趣的想法,但遗憾的是,由于 TSQL 中的默认舍入(浮点到 varchar 削减 6 位),手动转换可能会解决这个问题,但会使查询更加复杂。
              • 非常适合在我的数据集中查找具有高精度的小数
              【解决方案9】:

              浮点数只是代表一个实数。实数的小数位数没有意义。特别是实数3可以有六位小数,3.000000,只是所有的小数位都是零。

              您可能有一个显示转换未在十进制中显示最右边的零值。

              另外请注意,最多有 6 位小数的原因是第七位不精确,因此显示转换不会承诺第七位小数位值。

              还要注意浮点数以二进制形式存储,它们实际上在二进制点的右侧有二进制位置。十进制显示是浮点存储中二进制有理数的近似值,而浮点数又是实数的近似值。

              所以重点是,浮点值有多少个小数位实际上是没有意义的。如果您转换为字符串(例如使用 CAST),您可以计算小数位。这确实是您尝试做的最好的方法。

              【讨论】:

              • 如果你转换成字符串,你不能真正找到小数位数。转换将四舍五入或截断小数位。
              • 我想出了一个更好的解释方式。在 ruby​​ 控制台中,键入“1.1 - 1.0”会得到 0.10000000000000009。那么小数点应该是多少位呢? @ARS 是对的,通常转换为字符串函数会截断小数位数以不超过可以存储的精度。因此,也许 to 字符串例程将 0.10000000000000009 截断为 0.1000000,然后修剪尾随零以给您 0.1,这可能更接近您想要的。但是,谁能说这个值有多少位小数。
              • 所以总而言之,有一些数值,比如 1/3,有无限小数位和无限位二进制位,由于存储有限,它们是近似的。十进制和二进制之间的转换也引入了精度问题,如 0.1。字符串函数四舍五入以截断超出有限存储能够真正表示的精度。在这些情况下,唯一合理的做法是转换为字符串,并将其结果作为小数位数。
              猜你喜欢
              • 2021-03-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-11-14
              • 1970-01-01
              • 2011-01-20
              • 2015-04-12
              相关资源
              最近更新 更多