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 / 问题 / 319890
Accepted
CaM
CaM
Asked: 2022-11-19 07:04:26 +0800 CST2022-11-19 07:04:26 +0800 CST 2022-11-19 07:04:26 +0800 CST

SQL 模函数给出了错误的值?

  • 772

在 Javascript、Excel、Python 和科学计算器中,模运算

-34086.1576962834 % 360.0      <-- python
=MOD(-34086.1576962834, 360.0) <-- Excel function

给出一个值113.84230371659942。(或者非常接近的东西,取决于小数位数和四舍五入。)

但是在 T-SQL 中,

Select -34086.1576962834 % 360.0

返回值-246.1576962834。

我试过使用

Select cast(-34086.1576962834 as numeric(30,15)) % cast(360.0 as numeric(30,15))

正如我所想,这可能是浮点数与固定十进制数学不匹配的一个例子,但这并不能解决问题。

为什么 SQL Server 不以与计算器/预期结果一致的方式执行此操作?更重要的是,有没有办法让 T-SQL 像我的计算器一样进行算术运算?

我主要使用 SQL Server 2019,但我已经确认 SQL Server 2012 和 2014 也给出相同的 -246... 结果。

Oracle 函数mod(x,y)和 pythonmath.fmod(x,y)给出相同的 -246.nnn 结果。

sql-server
  • 2 2 个回答
  • 646 Views

2 个回答

  • Voted
  1. Best Answer
    bbaird
    2022-11-19T07:26:42+08:002022-11-19T07:26:42+08:00

    根据维基百科,SQL 标准实现了%运算符的截断版本,对于数字a = nq + r,其中q是整数,将余数r定义为:

    r = a - n [ a / n ] 其中 [] 是整数部分函数(整数部分)1

    因此,将示例代入方程式可得

    r = -34086.1576962834 - 360.0[-34086.1576962834/360.0] 
    
      = -34086.1576962834 - 360.0[-94.68377138]
    
      = -34086.1576962834 - 360.0(-94)
    
      = -246.1576962834
    

    因此,根据 SQL 标准,该%函数按预期工作。请参阅以类似方式实现标准的Postgres和MySQL 。

    如果您希望在被除数为负时通过使用底数除法而不是整数部分的方法获得相同的结果,则可以将除数添加到结果中,如 [ x ] + 1 = FLOOR( x ) 当x为负数时。

    r + n = a - n[a/n] + n
    
          = a - n([a/n] + 1) 
    
          = a - n(FLOOR(a/n))
    

    或者,如本SO 答案所示,无论股息是正数还是负数 2(n + a % n) % n),都将转换为底线除法。


    这是 SQL 标准必须说的:(感谢ypercubeᵀᴹ和这篇博文):

    <modulus expression> ::=
      MOD <left paren>
          <numeric value expression dividend>
          <comma>
          <numeric value expression divisor>
          <right paren>
    ...
    If <modulus expression> is specified,
    then the declared type of each
    <numeric value expression>
    shall be exact numeric with scale 0 (zero).
    The declared type of the result is the
    declared type of the immediately contained
    <numeric value expression divisor>.
    ...
    9)If <modulus expression> is specified,
      then let N be the value of the immediately
      contained <numeric value expression
      dividend> and let M be the value of the
      immediately contained <numeric value
      expression divisor>.
      Case:
        a)If at least one of N and M is the null
          value, then the result is the null value.
        b)If M is zero,
          then an exception condition is raised:
          data exception—division by zero.
        c)Otherwise, the result is the unique exact
          numeric value R with scale 0 (zero) such
          that all of the following are true:
          i)R has the same sign as N.
          ii)The absolute value of R is less than
             the absolute value of M.
          iii)N = M * K + R for some exact numeric 
              value K with scale 0 (zero).
    

    因此,您会注意到很多“零刻度”,但这并不相关,就好像我们从N、M和R中删除了该限制一样,计算没有改变。移动我们有:

    R = N - M * K与 || R || < || 男|| 和 SIGN( R ) = SIGN( N )。

    这不是math.stackexchange.com,所以我不会做正式证明,但这两个限制意味着K在这种情况下是 [ N / M ] 而不是 FLOOR( N / M )。

    使用您的示例,我们有:

    R = -34086.1576962834 - 360.0*(-94)
      = -246.1576962834
    

    1这与FLOOR应用于负值时的函数不同。

    2证明作为练习留给读者,因为作者已经 10 多年没有接触过数学教科书了。

    • 13
  2. AMtwo
    2022-11-19T07:34:09+08:002022-11-19T07:34:09+08:00

    这似乎是 SQL Server 如何使用负数计算模数的问题。

    你会注意到,如果你拿出你的科学计算器(我使用的是 Windows 内置的calc.exe科学模式),并计算正商和负商,你将得到 113.nnn 和 246。 nnn 号码: 计算器

    如果我们分解模数数学并“从长远来看”,那么我们可以看到 SQL Server 在做什么:

    DECLARE @dividend numeric(30,15) = -34086.1576962834;
    DECLARE @divisor numeric(30,15)  = 360.0;
    
    SELECT QuotientFloor          = FLOOR(@dividend / @divisor),
           QuotientCeiling        = CEILING(@dividend / @divisor),
           Remainder              = @dividend % @divisor,
           DividendInput          = @dividend,
           DivisorInput           = @divisor,
           QuotientFloorPlusMod   = ( FLOOR(@dividend / @divisor) * @divisor ) + (@dividend % @divisor),
           QuotientCeilingPlusMod = ( CEILING(@dividend / @divisor) * @divisor ) + (@dividend % @divisor);
    

    恕我直言,这是一个错误

    SQL Server 总是做 aFLOOR来计算商,而不是总是向零四舍五入。在我看来,这是一个错误,因为数学规则告诉我们应该向零舍入,而不是向下舍入。

    维基百科关于“模运算”的文章表明,对“模”的含义有不同的解释,尤其是在转向实数时——尽管自然数对模有更普遍理解的定义。

    根据上面链接的维基百科文章,ISO SQL 标准要求使用“截断除法”公式:

    许多实现使用截断除法,其中商定义为 截断除法商公式 其中 [] 是整数部分函数(向零舍入),即截断为零有效数字。因此,根据等式 (1),余数与被除数具有相同的符号: 截断除法 Mod 公式

    请注意,这需要向零舍入。这是大多数人所期望的行为,因为在四舍五入时,许多人觉得向零四舍五入是“自然的”。

    相反,SQL Server 使用“floored division”方法:

    Donald Knuth 提倡底除法,其商定义为 底除商公式 其中 ⌊⌋ 是底函数(向下舍入)。因此,根据等式(1),余数与除数具有相同的符号: 地板分裂模式公式

    请注意,这需要向下舍入。

    微软可能不会改变行为

    SQL Server 长期以来一直存在这种行为,我不希望 SQL Server 更改其实现以使用 ISO SQL 标准为%和调用的“截断除法”公式mod()。现在更改此功能将导致依赖现有行为的多年代码被破坏。

    相反,我希望微软能够清楚地记录现有功能,以明确他们使用哪种方法计算模数。如果我们幸运的话,他们可能会引入一种新的语法,也可以使用其他方法执行取模,这样人们就可以根据需要选择合适的行为。

    • 2

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

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

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

Sidebar

Stats

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

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

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

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

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

    • 4 个回答
  • Marko Smith

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

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • 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
    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

热门标签

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