SQL无效转换返回null而不是抛出错误

Bry*_*ner 7 t-sql invalid-characters

我有一个带varchar列的表,我想找到与某个数字匹配的值.因此,我们可以说该列包含以下条目(现实生活中除了数百万行):

123456789012
2345678
3456
23 45
713?2
00123456789012
Run Code Online (Sandbox Code Playgroud)

所以我决定我想要所有数字上的行123456789012写一个看起来像这样的语句:

SELECT * FROM MyTable WHERE CAST(MyColumn as bigint) = 123456789012
Run Code Online (Sandbox Code Playgroud)

它应该返回第一行和最后一行,但整个查询会爆炸,因为它无法将"23 45"和"713?2"转换为bigint.

是否有另一种方法可以进行转换,为无法转换的值返回NULL?

Mos*_*aou 8

如果您使用的是SQL Server 2012,则可以使用以下两种新方法:

两种方法都是等价的.如果转换成功,它们返回一个值转换为指定的数据类型; 否则,返回null.唯一的区别是CONVERT是SQL Server特定的,CAST是ANSI.使用CAST将使您的代码更具可移植性(虽然不确定是否有任何其他数据库提供程序实现TRY_CAST)


Rem*_*anu 7

SQL Server不保证布尔运算符短路,请参阅On SQL Server布尔运算符短路.所以所有使用ISNUMERIC(...) AND CAST(...)的解决方案都存在根本性的缺陷(它们可能会起作用,但是嘿可以随意地依赖于生成的计划而失败).正如托马斯建议的那样,更好的解决方案是使用CASE : CASE ISNUMERIC(...) WHEN 1 THEN CAST(...) ELSE NULL END. 但是,正如gbn指出的那样,ISNUMERIC在识别"数字"意味着什么以及许多人希望它返回0的情况下,它是非常挑剔的,它返回1.因此将CASE与LIKE混合:

CASE WHEN MyRow NOT LIKE '%[^0-9]%' THEN CAST(MyRow as bigint) ELSE NULL END
Run Code Online (Sandbox Code Playgroud)

但真正的问题是,如果你有数百万行,你必须像这样搜索它们,你总是会端到端扫描,因为表达式不具备SARG能力(无论我们如何重写它).这里真正的问题是数据纯度,应该在适当的级别解决,其中填充数据.另一件需要考虑的事情是,是否可以使用此表达式创建一个持久的计算列,并在其上创建一个消除NULL的过滤索引(即非数字).这会加速一些事情.