在存储过程中运行时 CAST 抛出错误,但作为原始查询运行时则不会

Chr*_*ice 8 sql-server stored-procedures sql-server-2012

这里正在发生一些非常奇怪的事情。

我有一个看起来像这样的查询。

SELECT CAST(FT.DOP AS SMALLINT) FROM TRACKING_DATA WHERE date > @mydate and identifier = 0000000000
Run Code Online (Sandbox Code Playgroud)

当作为原始查询运行时,它返回数据就好了。

当我把它放在一个改变 where 子句的存储过程中时,它会抛出这个错误。

Msg 244, Level 16, State 2, Procedure myprocedure, Line 107 [Batch Start Line 2]
The conversion of the varchar value '58629' overflowed an INT2 column. Use a larger integer column.
Run Code Online (Sandbox Code Playgroud)

所以这就是奇怪的地方。我用这样的查询遍历了那个 where 子句的所有可能数据。

SELECT DISTINCT DOP FROM TRACKING_DATA where identifier = 000000000000
Run Code Online (Sandbox Code Playgroud)

SELECT DISTINCT CAST(DOP AS smallint) FROM TRACKING_DATA where identifier = 000000000000
Run Code Online (Sandbox Code Playgroud)

这就是我得到的回报。

17
12
9
19
8
14
6
16
11
13
7
10
0
18
5
15
4
Run Code Online (Sandbox Code Playgroud)

它没有任何地方对于SMALLINT. 所以我想,好吧,也许它是一个不可打印的 ASCII 字符。但我找不到任何。

我现在有点困惑。它将 find 作为原始查询运行,作为过程展开,所有可能的数据都基于 where 有效。我唯一的怀疑是查询计划在过滤方面做了一些奇怪的事情,或者在作为过程运行时可能以不同的验证运行。

Jos*_*ell 13

你最好不要依赖索引来避免这些错误,而是编写查询来防止这种情况。由于您使用的是 SQL Server 2012,一种选择是使用 TRY_CAST:

SELECT TRY_CAST(FT.DOP AS SMALLINT) 
FROM TRACKING_DATA 
WHERE date > @mydate and identifier = 0000000000;
Run Code Online (Sandbox Code Playgroud)

这将导致NULL为无法从 varchar 转换为 smallint 的值被选中。但是只要您知道没有任何类似的结果,或者您的应用程序可以处理这些NULL结果,您就应该很好。


Chr*_*ice 12

所以这是它一直决定使用的该死的查询计划。

它爆炸的值存在于表中(它不应该存在,但这是另一个问题),但它与一个完全不同的标识符相关联。

有问题的查询正在一个clustered seek索引上运行 a ,该索引涵盖date但不包括identifierthis 导致它扫描整个表,由于多种原因,这是如此,如此,如此错误。

为该where子句添加了一个适当的索引,该过程现在很高兴地嗡嗡作响,因为它在日期之后过滤标识符。我希望我能在统计之前/之后获得,但我很确定它现在也运行得更快。

  • @LaughingVergil - 这不能保证有效。`CAST` 仍然可以在过滤器之前被推送,最可靠的方法是使用 `TRY_CONVERT`,因此它是否针对会导致转换失败但后来被消除的行运行并不重要 (2认同)