我有一个带varchar列的表,我想找到与某个数字匹配的值.因此,我们可以说该列包含以下条目(现实生活中除了数百万行):
123456789012 2345678 3456 23 45 713?2 00123456789012
所以我决定我想要所有数字上的行123456789012写一个看起来像这样的语句:
SELECT * FROM MyTable WHERE CAST(MyColumn as bigint) = 123456789012
它应该返回第一行和最后一行,但整个查询会爆炸,因为它无法将“23 45”和“713?2”转换为bigint.
是否有另一种方法来进行转换,为无法转换的值返回NULL?
解决方法
sql Server不保证布尔运算符短路,请参见
On SQL Server boolean operator short-circuit.所以使用ISNUMERIC(…)和CAST(…)的所有解决方案都存在根本缺陷(它们可能有效,但是嘿可以随意依赖生成的计划失败).一个更好的解决方案是使用CASE,正如托马斯建议的那样:CASE ISNUMERIC(…)1,那么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
但真正的问题是,如果你有数百万行,你必须像这样搜索它们,你总是会端对端扫描,因为表达式不具备SARG能力(无论我们如何重写它).这里真正的问题是数据纯度,应该在适当的级别解决数据填充的问题.另一件需要考虑的事情是,是否可以使用此表达式创建一个持久的计算列,并在其上创建一个消除NULL的过滤索引(即非数字).这会加速一些事情.