是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
获取表的更好方法是什么,但数据不仅包含数值?
答案 0 :(得分:5)
如果您使用的是SQL Server 2012或更高版本,则应使用try_convert而不是isnumeric。 Isnumeric有一些有趣的问题,即使对于无法转换为数字的字符串,它也会返回1。所以这样的事情应该有效:
SELECT *
FROM Person.Address
WHERE try_convert(int, PostalCode) < 7000
如果无法转换字符串,则try_convert返回null。
答案 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