AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 18997
Accepted
Nick Chammas
Nick Chammas
Asked: 2012-06-09 11:47:32 +0800 CST2012-06-09 11:47:32 +0800 CST 2012-06-09 11:47:32 +0800 CST

为什么 10^37 / 1 会抛出算术溢出错误?

  • 772

继续我最近玩大数字的趋势,我最近将我遇到的一个错误归结为以下代码:

DECLARE @big_number DECIMAL(38,0) = '1' + REPLICATE(0, 37);

PRINT @big_number + 1;
PRINT @big_number - 1;
PRINT @big_number * 1;
PRINT @big_number / 1;

我得到的这段代码的输出是:

10000000000000000000000000000000000001
9999999999999999999999999999999999999
10000000000000000000000000000000000000
Msg 8115, Level 16, State 2, Line 6
Arithmetic overflow error converting expression to data type numeric.

什么?

为什么前 3 个操作有效但最后一个无效?如果@big_number明明可以存储 的输出,怎么会出现算术溢出错误@big_number / 1呢?

sql-server sql-server-2008
  • 1 1 个回答
  • 1392 Views

1 个回答

  • Voted
  1. Best Answer
    Nick Chammas
    2012-06-09T11:47:32+08:002012-06-09T11:47:32+08:00

    在算术运算的上下文中理解精度和比例

    让我们分解一下,仔细看看除法算术运算符的细节。这是 MSDN 对除法运算符的结果类型的描述:

    结果类型

    返回具有更高优先级的参数的数据类型。有关详细信息,请参阅数据类型优先顺序 (Transact-SQL)。

    如果整数被除数除以整数除数,则结果是一个整数,结果的任何小数部分都被截断了。

    我们知道那@big_number是一个DECIMAL. SQL Server 转换1成什么数据类型?它将它转换为INT. 我们可以通过以下方式确认这一点SQL_VARIANT_PROPERTY():

    SELECT
          SQL_VARIANT_PROPERTY(1, 'BaseType')   AS [BaseType]  -- int
        , SQL_VARIANT_PROPERTY(1, 'Precision')  AS [Precision] -- 10
        , SQL_VARIANT_PROPERTY(1, 'Scale')      AS [Scale]     -- 0
    ;
    

    对于踢球,我们还可以1将原始代码块中的 替换为显式键入的值,DECLARE @one INT = 1;并确认我们得到相同的结果。

    所以我们有一个DECIMAL和一个INT。由于DECIMAL具有比更高的数据类型优先级INT,我们知道除法的输出将被转换为DECIMAL。

    那么问题出在哪里呢?

    问题在于输出中的比例DECIMAL。以下是有关 SQL Server 如何确定算术运算结果的精度和小数位数的规则表:

    Operation                              Result precision                       Result scale *
    -------------------------------------------------------------------------------------------------
    e1 + e2                                max(s1, s2) + max(p1-s1, p2-s2) + 1    max(s1, s2)
    e1 - e2                                max(s1, s2) + max(p1-s1, p2-s2) + 1    max(s1, s2)
    e1 * e2                                p1 + p2 + 1                            s1 + s2
    e1 / e2                                p1 - s1 + s2 + max(6, s1 + p2 + 1)     max(6, s1 + p2 + 1)
    e1 { UNION | EXCEPT | INTERSECT } e2   max(s1, s2) + max(p1-s1, p2-s2)        max(s1, s2)
    e1 % e2                                min(p1-s1, p2 -s2) + max( s1,s2 )      max(s1, s2)
    
    * The result precision and scale have an absolute maximum of 38. When a result 
      precision is greater than 38, the corresponding scale is reduced to prevent the 
      integral part of a result from being truncated.
    

    下面是我们在这个表中的变量:

    e1: @big_number, a DECIMAL(38, 0)
    -> p1: 38
    -> s1: 0
    
    e2: 1, an INT
    -> p2: 10
    -> s2: 0
    
    e1 / e2
    -> Result precision: p1 - s1 + s2 + max(6, s1 + p2 + 1) = 38 + max(6, 11) = 49
    -> Result scale:                    max(6, s1 + p2 + 1) =      max(6, 11) = 11
    

    根据上表中的星号注释, a可以具有的最大精度DECIMAL为 38 。所以我们的结果精度从 49 降低到 38,并且“相应的小数位数减少以防止结果的整数部分被截断”。从这个评论中不清楚比例是如何减少的,​​但我们知道这一点:

    根据表中的公式,将两个 s 相除后的最小可能小数位数DECIMAL是 6。

    因此,我们最终得到以下结果:

    e1 / e2
    -> Result precision: 49 -> reduced to 38
    -> Result scale:     11 -> reduced to 6  
    
    Note that 6 is the minimum possible scale it can be reduced to. 
    It may be between 6 and 11 inclusive.
    

    这如何解释算术溢出

    现在答案很明显了:

    我们部门的输出被转换为DECIMAL(38, 6),DECIMAL(38, 6)不能容纳 10 37。

    有了这个,我们可以通过确保结果适合来构建另一个成功的部门DECIMAL(38, 6):

    DECLARE @big_number    DECIMAL(38,0) = '1' + REPLICATE(0, 37);
    DECLARE @one_million   INT           = '1' + REPLICATE(0, 6);
    
    PRINT @big_number / @one_million;
    

    结果是:

    10000000000000000000000000000000.000000
    

    注意小数点后的 6 个零。我们可以确认结果的数据类型是DECIMAL(38, 6)通过使用SQL_VARIANT_PROPERTY()如上:

    DECLARE @big_number   DECIMAL(38,0) = '1' + REPLICATE(0, 37);
    DECLARE @one_million  INT           = '1' + REPLICATE(0, 6);
    
    SELECT
          SQL_VARIANT_PROPERTY(@big_number / @one_million, 'BaseType')  AS [BaseType]  -- decimal
        , SQL_VARIANT_PROPERTY(@big_number / @one_million, 'Precision') AS [Precision] -- 38
        , SQL_VARIANT_PROPERTY(@big_number / @one_million, 'Scale')     AS [Scale]     -- 6
    ;
    

    一个危险的解决方法

    那么我们如何绕过这个限制呢?

    好吧,这当然取决于您进行这些计算的目的。您可能会立即跳转到的一种解决方案是将您的数字转换为FLOAT用于计算,然后DECIMAL在完成后将它们转换回。

    这在某些情况下可能有效,但您应该仔细了解这些情况是什么。众所周知,将数字相互转换FLOAT是危险的,可能会给您带来意想不到的或不正确的结果。

    在我们的例子中,将 10 37与10 37 相互转换FLOAT得到的结果完全错误:

    DECLARE @big_number     DECIMAL(38,0)  = '1' + REPLICATE(0, 37);
    DECLARE @big_number_f   FLOAT          = CAST(@big_number AS FLOAT);
    
    SELECT
          @big_number                           AS big_number      -- 10^37
        , @big_number_f                         AS big_number_f    -- 10^37
        , CAST(@big_number_f AS DECIMAL(38, 0)) AS big_number_f_d  -- 9999999999999999.5 * 10^21
    ;
    

    你有它。小心分开,我的孩子们。

    • 18

相关问题

  • 死锁的主要原因是什么,可以预防吗?

  • 我在索引上放了多少“填充”?

  • 是否有开发人员遵循数据库更改的“最佳实践”类型流程?

  • 如何确定是否需要或需要索引

  • 从 SQL Server 2008 降级到 2005

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何查看 Oracle 中的数据库列表?

    • 8 个回答
  • Marko Smith

    mysql innodb_buffer_pool_size 应该有多大?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    从 .frm 和 .ibd 文件恢复表?

    • 10 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    如何选择每组的第一行?

    • 6 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta 使用 psql 列出数据库权限 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    bernd_k 什么时候应该使用唯一约束而不是唯一索引? 2011-01-05 02:32:27 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve