我有一个带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?
答案 0 :(得分:8)
如果您使用的是SQL Server 2012,则可以使用以下两种新方法:
两种方法都是等价的。如果转换成功,它们返回一个值转换为指定的数据类型;否则,返回null。唯一的区别是CONVERT是SQL Server特定的,CAST是ANSI。使用CAST将使您的代码更具可移植性(虽然不确定是否有任何其他数据库提供程序实现TRY_CAST)
答案 1 :(得分:7)
SQL Server不保证布尔运算符短路,请参阅On SQL Server boolean operator short-circuit。所以使用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
但真正的问题是,如果你有数百万行而你必须像这样搜索它们,你总是会端对端扫描,因为表达式不具备SARG能力(无论我们如何重写它)。这里真正的问题是数据纯度,应该在适当的级别解决数据填充的问题。另一件需要考虑的事情是,是否可以使用此表达式创建一个持久的计算列,并在其上创建一个消除NULL的过滤索引(即非数字)。这会加速一些事情。
答案 2 :(得分:4)
ISNUMERIC将接受空字符串和值,如1.23或5E-04,因此可能不可靠。
而且您不知道将对哪些订单进行评估,因此它仍然可能失败(SQL是声明性的,而不是程序性的,因此WHERE子句可能不会从左到右进行评估)
所以:
类似的东西:
SELECT
*
FROM
(
SELECT TOP 2000000000 *
FROM MyTable
WHERE MyColumn NOT LIKE '%[^0-9]%' --double negative rejects anything except 0-9
ORDER BY MyColumn
) foo
WHERE
CAST(MyColumn as bigint) = 123456789012 --applied after number check
编辑:失败的快速示例。
CREATE TABLE #foo (bigintstring varchar(100))
INSERT #foo (bigintstring )VALUES ('1.23')
INSERT #foo (bigintstring )VALUES ('1 23')
INSERT #foo (bigintstring )VALUES ('123')
SELECT * FROM #foo
WHERE
ISNUMERIC(bigintstring) = 1
AND
CAST(bigintstring AS bigint) = 123
答案 3 :(得分:1)
SELECT *
FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as float) = 123456789012
答案 4 :(得分:0)
ISNUMERIC()函数应该可以满足您的需求。
SELECT * FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as bigint) = 123456789012
并添加像托马斯建议的案例陈述:
SELECT * FROM MyTable
WHERE CASE(ISNUMERIC(MyRow)
WHEN 1 THEN CAST(MyRow as bigint)
ELSE NULL
END = 123456789012
答案 5 :(得分:0)
SELECT *
FROM MyTable
WHERE (ISNUMERIC(MyColumn) = 1) AND (CAST(MyColumn as bigint) = 123456789012)
此外,您可以使用CASE语句来获取空值。
SELECT
CASE
WHEN (ISNUMERIC(MyColumn) = 1) THEN CAST(MyColumn as bigint)
ELSE NULL
END AS 'MyColumnAsBigInt'
FROM tableName
如果您需要额外的过滤,对于无法转换为bigint的数字,您可以使用以下代替ISNUMERIC:
PATINDEX('%[^ 0-9]%',MyColumn))= 0
如果您需要十进制值而不是整数,请转换为浮点值并将正则表达式更改为'%[^ 0-9。]%'