我有一个带有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不能保证布尔操作员短路,请参阅 在SQL Server布尔操作员短路上. 。因此所有解决方案都使用 ISNUMERIC(...) AND CAST(...) 从根本上有缺陷(它们可能起作用,但是嘿,嘿,以后可能会对生成计划的依赖失败)。一个更好的解决方案是使用案例,正如托马斯(Thomas)所建议的那样: CASE ISNUMERIC(...) WHEN 1 THEN CAST(...) ELSE NULL END. 。但是,正如GBN指出的那样, ISNUMERIC 众所周知,在确定“数字”的含义方面是挑剔的,许多人期望它返回0的情况下它会返回1.因此,将案例与类似的情况混合:

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

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

其他提示

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

两种方法都是等效的。如果演员阵容成功,他们将铸件的值归还给指定的数据类型;否则,返回null。唯一的区别是转换是特定于SQL Server,铸造为ANSI。使用铸件将使您的代码更加便携(尽管不确定是否有其他数据库提供商实施try_cast)

Isnumeric将接受空字符串和1.23或5E-04之类的值,因此可能不可靠。

而且您不知道将在哪些顺序中评估什么顺序,因此它仍然可能失败(SQL是声明性的,而不是程序性的,因此可能不会从左到右评估条款)

所以:

  • 您想接受该值的价值 只要 字符0-9
  • 您需要实现“数字”过滤器,因此在铸造之前已应用

就像是:

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
SELECT * 
    FROM MyTable 
    WHERE ISNUMERIC(MyRow) = 1
        AND CAST(MyRow as float) = 123456789012

ISNumeric()函数应为您提供所需的内容。

SELECT * FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as bigint) = 123456789012

并添加像托马斯(Thomas)建议的案例陈述:

SELECT * FROM MyTable
WHERE CASE(ISNUMERIC(MyRow)
        WHEN 1 THEN CAST(MyRow as bigint)
        ELSE NULL
      END = 123456789012

http://msdn.microsoft.com/en-us/library/ms186272.aspx

SELECT *
FROM MyTable
WHERE (ISNUMERIC(MyColumn) = 1) AND (CAST(MyColumn as bigint) = 123456789012)

此外,您可以使用案例语句以获取零值。

SELECT 
    CASE
        WHEN (ISNUMERIC(MyColumn) = 1) THEN CAST(MyColumn as bigint)
        ELSE NULL
    END AS 'MyColumnAsBigInt'
FROM tableName

如果您需要其他过滤,则对于无效施放到BigInt的数字,您可以使用以下内容而不是数字:

patindex('%[^0-9]%',mycolumn))= 0

如果您需要十进制值而不是整数,请铸造浮动,然后将正则拨号更改为'%[^0-9。]%'

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top