SQL Server Float 数据类型超出范围

SQL Server Float Datatype out of range

提问人:JoeLeBaron 提问时间:7/2/2020 更新时间:7/3/2020 访问量:2559

问:

我有一个查询,我通过扩展事件会话捕获,其浮点数据类型如下所示:

@variable = 120700.8000000000000000000000000000000000000000000000

如果我尝试在 SSMS 中运行相同的查询,则会出现错误:数字“120700.80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

就像你要跑步一样

DECLARE @variable float = 120700.8000000000000000000000000000000000000000000000

跟踪运行时查询成功。事件是rpc_completed的,它有持续时间、cpu、行数等......

不确定这是否相关,但有问题的查询是执行sp_executeSQL。跟踪捕获的完整查询如下所示:

exec sp_executeSQL N'SELECT
    col1,
    col2
FROM table
WHERE col3 > @variable', N'@variable float', 
@variable = 120700.8000000000000000000000000000000000000000000000

所以我的问题是,为什么跟踪中的代码能够无错误地执行,但是当我将相同的代码复制/粘贴到 SSMS 时,它会引发错误。如果我使用相同的代码并修剪掉一堆零,它就会起作用。

我在 SQL Azure DB 上运行。我已经在兼容性SQL2016、SQL2019和 Prem SQL2019 上进行了复制。

SQL Server 精确度 SQLDataTypes

评论

2赞 Alex 7/2/2020
文本首先隐式转换为十进制,因此出现错误。尝试(注意末尾的 E0) 来源:stackoverflow.com/questions/3024306/...120700.8000000000000000000000000000000000000000000000120700.8000000000000000000000000000000000000000000000E0
0赞 Alex 7/2/2020
@VladimirBaranov - 完成;无法抗拒赞成:)

答:

3赞 Alex 7/2/2020 #1

字符串文字首先隐式转换为十进制,因此会出现错误。120700.8000000000000000000000000000000000000000000000

尝试

DECLARE @variable float = 120700.8000000000000000000000000000000000000000000000E0

源:TSQL - 创建文本浮点值

3赞 Vladimir Baranov 7/2/2020 #2

为什么跟踪中的代码能够无错误地执行,但是当我 将相同的代码复制/粘贴到 SSMS,它会引发错误。

将此查询放入 SSMS 时

exec sp_executeSQL N'SELECT
    col1,
    col2
FROM table
WHERE col3 > @variable', N'@variable float', 
@variable = 120700.8000000000000000000000000000000000000000000000

文本被解释为数字/十进制类型。引擎尝试将其转换为数字/十进制类型的值,但失败,因为数字的最大精度为 38 位。120700.8000000000000000000000000000000000000000000000

如果将此查询放在 SSMS 中,它应该可以正常工作

exec sp_executeSQL N'SELECT
    col1,
    col2
FROM table
WHERE col3 > @variable', N'@variable float', 
@variable = 120700.8000000000000000000000000000000000000000000000E0

我在最后添加了。E0

若要使文字成为文字,我们需要使用科学记数法,如常量 (Transact-SQL) 中所述float


为什么跟踪中的代码能够执行而不会出错

我不能肯定地说,但我怀疑您在跟踪中看到的代码与应用程序发送到 SQL Server 的代码并不完全相同。它可以以这样一种方式发送,即参数类型和值实际上是 ,而不是具有所有这些零的文本字符串。float

我认为,应用程序可能会进行RPC调用,将具有适当类型的适当参数传递给函数调用。因此,当应用程序成功调用时,从未发生过从文本字符串到或类型的转换。numericfloat

评论

0赞 Alex 7/2/2020
RE “RPC 调用以正确的类型传递正确的参数” - 我也这么认为。它看起来像一个参数化查询。唯一让我感到困惑的是数字中的尾随 0。这只是一个格式上的奇怪之处吗?
0赞 Vladimir Baranov 7/2/2020
@Alex,它很容易成为跟踪呈现/显示/格式化其数据的奇怪方式。我在扩展事件方面没有太多经验。
1赞 JoeLeBaron 7/3/2020
这让我走上了正确的轨道。我将语句从跟踪中取出并在 C# 中执行它,只需将整个命令放入 Command.Text 属性中即可。我需要作为存储过程执行并显式声明参数。我将在下面添加一个答案,以准确显示我的意思。
2赞 JoeLeBaron 7/3/2020 #3

关于我为证明上述公认的答案所做的工作的详细信息。

我正在使用 C# 在另一个数据库上重播跟踪语句,如下所示(这将抛出超出范围的错误):

cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "exec sp_executesql @stmt=N'SELECT @f',
    @params=N'@f float',@f=120700.800000000002910383045673370361328125";
cmd.ExecuteNonQuery();

我需要做的是提取这些部分并使用如下参数构建一个存储过程:

cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "sp_executesql";

SqlParameter p1 = new SqlParameter();
p1.DbType = System.Data.DbType.String;
p1.ParameterName = "@stmt";
p1.Value = "SELECT @f";

SqlParameter p2 = new SqlParameter();
p2.DbType = System.Data.DbType.String;
p2.ParameterName = "@params";
p2.Value = "@f float";

SqlParameter p3 = new SqlParameter();
p3.DbType = System.Data.DbType.Double;
p3.Value = 120700.8000000000000000000000000000000000000000000000;
p3.ParameterName = "@f";

cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
cmd.Parameters.Add(p3);

cmd.ExecuteNonQuery();

当我运行它时,它在跟踪中显示为:

exec sp_executesql @stmt=N'SELECT @f',@params=N'@f float',@f=120700.800000000002910383045673370361328125

所以问题是我不能只是从rpc_completed事件中获取声明。我需要解析它并显式重建存储过程。

评论

0赞 Alex 7/3/2020
仅供参考:SQL Server Profiler 允许您重播事件:learn.microsoft.com/en-us/sql/tools/sql-server-profiler/...
1赞 JoeLeBaron 7/15/2020
遗憾的是,Profiler 不适用于 Azure SQL DB。