转换失败。来自Person.Address的SELECT * WHERE ISNUMERIC(PostalCode)= 1 AND PostalCode< 7000

时间:2015-03-12 14:18:33

标签: sql sql-server tsql

是Microsoft SQL Server。

在AdventureWorks 2012 PostalCode表的这一列Person.Address中,有数字和字符串值。

我希望获得包含行WHERE PostalCode < 7000

的表格

这不能按预期工作:

USE [AdventureWorks2012]

SELECT *
FROM Person.Address
WHERE ISNUMERIC(PostalCode) = 1 
  AND PostalCode < 7000

因为我收到了这个错误:

  

转换nvarchar值时转换失败&#39; K4B 1T7&#39;数据类型int。

我可以通过创建这样的临时表来实现它:

/* creating of temp table */
USE AdventureWorks2012

SELECT * 
INTO temp2
FROM Person.Address
WHERE ISNUMERIC(PostalCode) = 1

/* get data from temp table */
SELECT * 
FROM temp2
WHERE PostalCode < 7000

但这是一种糟糕的方式,导致低生产率和不必要的温度表。

使用行WHERE PostalCode < 7000获取表的更好方法是什么,但数据不仅包含数值?

6 个答案:

答案 0 :(得分:5)

如果您使用的是SQL Server 2012或更高版本,则应使用try_convert而不是isnumeric。 Isnumeric有一些有趣的问题,即使对于无法转换为数字的字符串,它也会返回1。所以这样的事情应该有效:

SELECT *
FROM Person.Address
WHERE try_convert(int, PostalCode) < 7000

如果无法转换字符串,则try_convert返回null。

MSDN:https://msdn.microsoft.com/en-us/library/hh230993.aspx

答案 1 :(得分:3)

正在返回错误,因为正在评估的条件不是短路 - 即使邮政编码是非数字的,也会评估条件PostalCode<7000

相反,请尝试:

SELECT *
from Person.Address
WHERE CASE WHEN PostalCode NOT LIKE '%[^0-9]%'
           THEN CAST(PostalCode AS NUMERIC)
           ELSE CAST(NULL AS NUMERIC) 
      END <7000

(更新以下评论)

答案 2 :(得分:1)

该文字来自70-461培训套件 (Exam 70-461: Querying Microsoft SQL Server 2012):

  

回想第1章所有出现在同一个表达式中的表达式   逻辑查询处理阶段 - 例如,WHERE阶段 -   在同一时间点进行概念性评估。例如,   考虑以下过滤谓词。

WHERE propertytype = 'INT' AND CAST(propertyval AS INT) > 10 
  

假设要查询的表   拥有不同的财产价值。 propertytype列表示   属性的类型(INT,DATE等)和   propertyval列保存字符串中的值。什么时候   propertytype为'INT',propertyval中的值可转换为INT;   否则,不一定。

     

有些人认为除非优先规则   否则,谓词将从左到右进行评估,   并且在可能的情况下将发生短路。其他   如果第一个谓词propertytype ='INT'的计算结果为false,   SQL Server不会评估第二个谓词CAST(propertyval AS   INT)&gt;因为结果已经知道了。基于此   假设,期望是查询永远不会失败   转换不可转换的东西。

     

但事实是,现实是   不同。 SQL Server内部支持短路概念;   然而,由于语言中的一次性概念,它并非如此   必须以从左到右的顺序评估表达式。   它可以根据成本相关的原因决定从开始   第二个表达式,然后如果第二个表达式求值为   是的,也可以评估第一个表达式。这意味着如果   表中有行与propertytype不同的行   'INT',在那些行中,propertyval不能转换为INT,即   由于转换错误,查询可能会失败。

答案 3 :(得分:0)

这样做的唯一安全方法是首先获取您感兴趣的字段的ID,然后在不同的语句中加入它们,否则查询规划器可以决定首先进行数字比较。当我们升级到以前没有发生的SQL Server 2008时,我开始遇到很多这样的问题。

但您可以进行转化:

USE [AdventureWorks2012]
SELECT *
from Person.Address
WHERE ISNUMERIC(PostalCode) =1 AND CAST(CAST(PostalCode AS INT) AS VARCHAR)<'7000'

我已经完成了铸件试图避免任何可能是数字的数据,但左边有0填充可能会搞砸订单。

请注意,这不会是最好的,并且不会使用PostalCode上的索引。

答案 4 :(得分:0)

在丹麦,邮政编码总是有相同的长度,所以我会使用这个脚本来避免奇怪的问题*与isnumeric和转换问题。 它将检查postalCode是否有4位数并比较字符串值。

SELECT * 
FROM temp2
WHERE
  PostalCode < '7000' and
  PostalCode like '[0-9][0-9][0-9][0-9]'

* isnumeric

的奇怪问题示例
SELECT isnumeric('£1.1')
SELECT isnumeric('-')

两者都返回1

答案 5 :(得分:-1)

您可以使用子查询来执行此操作:

    select * from ( 
     SELECT * from Person.Address WHERE ISNUMERIC(PostalCode) =1 ) t 
     where PostalCode<7000