我们有一个SQL在ColumnA上执行CAST功能(到FLOAT)。 SQL有一个过滤器,它最终将间接过滤掉ColumnA中具有非数值的那些行。但是,由于我认为由于在并行中运行部分SQL,我相信CAST甚至应用于被过滤掉的行,这会导致SQL失败“无法将值转换为float ... “
我知道如果我通过添加查询提示
来运行一个procOPTION (MAXDOP 1)
SQL按预期运行。我怀疑在1 proc上运行会强制应用过滤器以使用columnA中的非数值来清除行,以便其值的CASTING成功。我还发现使用查询提示
OPTION (FORCE ORDER)
解决了这个问题,我假设因为这也确保首先应用过滤器,并且我在一个柱面上运行的查询性能要好得多。
我倾向于使用第二个选项解决问题。如果我对这里发生的事情有任何误解,或者有人想阐述我的一般理解或提出建议,我将不胜感激。
我在
上运行Microsoft SQL Server 2008 R2(RTM) - 10.50.1720.0(X64)2010年6月12日 01:34:59版权所有(c)Microsoft Corporation Enterprise Edition Windows NT 5.2(Build 3790:Service Pack 2)上的(64位)
事后的想法:
似乎T-SQL有以下函数检查是否可以将字符串转换为特定数据类型,这样会很好。
IsFloat 则IsNumeric IsInteger 等
我真的很烦恼我在数据库中找到的各种数据列中定义为varchar(255)。我想解决方案是“不要那样做!”
答案 0 :(得分:4)
关于你的想法。
似乎T-SQL具有以下功能会很好 检查字符串是否可以转换为特定的数据类型。
SQL Server 2012确实为此需求引入了TRY_CONVERT
。因此,以下查询将返回NULL
而不是错误。
SELECT TRY_CONVERT ( FLOAT, 'Fish')
即使是串行计划也无法保证在WHERE
评估之前会发生SELECT
子句。正如从SQL Server 2005开始的in this blog post所解释的,这种情况比以前的版本更容易发生。 Behavior Changes to Database Engine Features in SQL Server 2005具体说明如下。
SQL Server 2005有时会在查询中更早地评估表达式 当它们在SQL Server 2000中进行评估时。此行为提供了 以下重要的好处:
- 将计算列上的索引与查询中与计算列表达式相同的表达式进行匹配的能力。
- 防止表达结果的冗余计算。
有关此行为的更多讨论,请参阅Craig Freedman撰写的另一篇精彩博文Conversion and Arithmetic Errors。
在2012年之前的版本和TRY_CONVERT
上,您需要将CAST AS FLOAT
包装在CASE
语句中。 e.g。
SELECT CASE WHEN ISNUMERIC(Col)=1 THEN CAST(Col AS FLOAT) END AS Col
FROM Table
WHERE ISNUMERIC(Col)=1
这仍然不能绝对保证可以防止您收到错误,因为ISNUMERIC
本身只会检查该值是否会转换为其中一种数值数据类型,而不是专门用于浮动An example of an input that would fail is '.'
CASE
主要是在线图书短信(some exceptions are discussed here)
您还可以在连接项SQL Server should not raise illogical errors和good explanation of a similar issue中通过SQLKiwi
找到有关此问题的其他讨论/投诉答案 1 :(得分:1)
我相信你是对的。 CONVERT()函数在谓词“间接过滤”行之前应用。
为了避免异常,你是对的,一种方法是尝试对执行计划中的操作顺序进行一些控制。如果声明提示对您有用,那么您可以使用它。 (就个人而言,提示是我的最后手段。)
请注意,SQL Server确实具有IsNumeric函数。
IsNumeric函数有点不合适,因为有些值会“传递”IsNumeric测试但会在转换为数值数据类型时引发异常。
就个人而言,我倾向于采用这种方法:
select convert(float,case when isnumeric( t.foo )=1 then t.foo else null end)
而非声明级提示。
或者,我会指定应该“过滤”不应转换的值的其他谓词。
select convert(float,case when t.fi in ('fo','fum') then t.foo else null end)