【问题标题】:REAL column holding values outside documented rangeREAL 列包含超出记录范围的值
【发布时间】:2012-07-02 15:54:24
【问题描述】:

根据MSDN,REAL 值的范围是 - 3.40E + 38 到 -1.18E - 38、0 和 1.18E - 38 到 3.40E + 38。但是,我有很多超出该范围的值在我的桌子上。

以下查询返回许多非常小的值,而没有非常大的值:

SELECT  MyColumn ,
        *
FROM    data.MyTable
WHERE   MyColumn <> 0
        AND ( MyColumn < CONVERT(REAL, 1.18E-38)
              OR MyColumn > CONVERT(REAL, 3.40E+38)
            )
        AND ( MyColumn < CONVERT(REAL, -3.40E+38)
              OR MyColumn > CONVERT(REAL, -1.18E-38)
            ) 

很容易显示这些值如何最终出现在表格中。我不能直接插入它们:

CREATE TABLE a(r REAL NULL);
GO
INSERT INTO a(r) VALUES(4.330473E-39);
GO
SELECT r FROM a
GO
DROP TABLE a;

----
0.0

但我可以将两列分开并得到和超出范围值:

CREATE TABLE a
  (
    r1 REAL NULL ,
    r2 REAL NULL ,
    r3 REAL NULL
  ) ;
GO
INSERT  INTO a
        ( r1, r2 )
VALUES  ( 4.330473E-38, 1000 ) ;
GO
UPDATE  a
SET     r3 = r1 / r2 ;
SELECT  r1 ,
        r2 ,
        r3
FROM    a

r1            r2            r3
------------- ------------- -------------
4.330473E-38  1000          4.330433E-41

所以我猜 MSDN 给出了错误的有效数据范围,对吗? 我错过了什么吗?

一些人认为这是一个错误。

这种行为的哪一部分是错误。是吗:

  1. MSDN 中记录并在 DBCC 中使用的常量错误,以及向下舍入的错误阈值。
  2. 更新可以保存错误的值

【问题讨论】:

  • 好像是个bug,你把它归档到你最喜欢的地方了吗?我想您可以添加自己的约束,例如 CHECK (r3 &gt; 3.40E-38) :-)
  • @AaronBertrand 我绝对可以添加一个约束,但我想了解为什么 SQL Server 的行为不像记录的那样。
  • dbcc checktable('a') with DATA_PURITY:列“r3”值超出数据类型“real”的范围。将列更新为合法值。举报...
  • 正如我所说,这似乎是一个错误。 :-)
  • 其实它看起来不像是一个错误。一旦检查并启用了 DATA_PURITY,就不会再发生这种情况了。

标签: sql-server sql-server-2008 sql-server-2008-r2 data-integrity


【解决方案1】:

Books Online 仅记录单精度和双精度浮点数的正常范围。 IEEE 754 规则还指定比最小非零正常值更接近零的浮点数,也称为denormalized, denormal, and subnormal 数字。从最后一个链接:

非正规数保证浮点数的加法和减法永远不会下溢;两个附近的浮点数 数字始终具有可表示的非零差异。没有 逐渐下溢,减法 a−b 可以下溢并产生零 即使值不相等。这反过来又会导致 当逐渐下溢时不会发生除以零错误 用过。

SQL Server 遵循发布示例中的single-precision floating point 计算规则。该错误可能是 DBCC 仅检查 正常 值,并在遇到存储的非正常值时抛出不正确的错误消息。

产生非正规单精度值的示例:

DECLARE
    @v1 real = 14e-39,
    @v2 real = 1e+07;

-- 1.4013e-045
SELECT @v1 / @v2;

显示存储的 float 非正常通过 DBCC 检查的示例:

CREATE TABLE dbo.b (v1 float PRIMARY KEY);
INSERT b VALUES (POWER(2e0, -1075));
SELECT v1 FROM b; -- 4.94065645841247E-324
DBCC CHECKTABLE(b) WITH DATA_PURITY; -- No errors or warnings
DROP TABLE dbo.b;

【讨论】:

  • 哪个部分是错误取决于 SQL Server 程序员的意图。从文档(和DBCC CHECKDB)看来,意图是只存储标准化数字。
  • 确实,这证明DBCC CHECKDB有bug。能够存储非正规浮点数可能是也可能不是,这取决于产品团队做出的设计决策。 +1
  • +1,但是如果DBCC有bug,那么MSDN也是错的,对吧?
  • @SQLkiwi 不完整而不是错误?也许。我不知道为什么这很重要。
【解决方案2】:

这是 SQL Server 中的一个错误。您发布的最后一个脚本是一个很好的复制品。在最后添加一行:

DBCC CHECKDB WITH data_purity

这失败了:

消息 2570,级别 16,状态 3,第 1 行页面 (1:313),对象 ID 中的插槽 0 357576312,索引ID 0,分区ID 1801439851932155904,分配单元ID 2017612634169999360(键入“行内数据”)。列“r3”值超出 数据类型“真实”的范围。将列更新为合法值。

这证明这是一个错误。我建议您向Microsoft Connect for SQL Server 提交错误。

【讨论】:

  • +1,但是:这种行为的哪一部分是错误。是吗: 1. MSDN 中记录并在 DBCC 中使用的错误常量,以及向下舍入的错误阈值。 2.更新可以保存错误的值
  • @AlexKuznetsov,没有。 2 是错误:sqlskills.com/BLOGS/PAUL/post/…(“在 2005 年之前的 SQL Server 版本中,可以将无效数据值导入数据库。这些无效值可能导致查询执行问题,甚至可能导致错误结果。在 2005 年,当进口“漏洞”已关闭”)。他们应该被关闭。这不是官方文档,但我猜团队会将其视为错误。
  • 谢谢!我将确保未通过 UPDATE/MERGE 命令保存未记录的值。
  • @SQLkiwi,你能详细说明一下吗?列 r3 超出了记录范围,这正是 DBCC CHECKDB 报告的内容。我认为这是令人信服的证据。 Paul Randals 的帖子似乎也支持这种观点。
猜你喜欢
  • 1970-01-01
  • 2022-11-26
  • 2018-07-18
  • 1970-01-01
  • 2021-05-06
  • 2016-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多