简体   繁体   English

将Nvarchar转换为Int失败的SQL Server 2008

[英]Cast Nvarchar to Int Failing SQL Server 2008

I have some rows I'm trying to cast to integer to get the last number in sequence. 我有一些行要转换为整数,以获取序列中的最后一个数字。

This is my original query. 这是我的原始查询。

SELECT
  MAX(CAST(REPLACE(ItemName, 'CA', '') AS INT)) + 1
FROM InventoryItem ii
JOIN InventoryItemDepartment iid
  ON ii.ItemCode = iid.ItemCode
WHERE iid.DepartmentCode = 'Filters'
AND ItemName LIKE 'CA4%____'
AND CAST(REPLACE(ItemName, 'CA', '') AS INT) < 41000

However I get an error message: Error (1,1): Conversion failed when converting the nvarchar value '41020-S' to data type int. 但是,我收到一条错误消息: Error (1,1): Conversion failed when converting the nvarchar value '41020-S' to data type int.

Obviously, I'm aware what this message means. 显然,我知道此消息的含义。 However I'm confused why its throwing the error, as the WHERE clauses I've specified are for the sole purpose of excluding records which might fail the cast. 但是我很困惑为什么它抛出错误,因为我指定的WHERE子句仅用于排除可能导致转换失败的记录。

If I modify the query to just select the original values, without any replacing or casting... 如果我修改查询以仅选择原始值,而不进行任何替换或强制转换...

SELECT
  ItemName
FROM InventoryItem ii
JOIN InventoryItemDepartment iid
  ON ii.ItemCode = iid.ItemCode
WHERE iid.DepartmentCode = 'Filters'
AND ItemName LIKE 'CA4%____'

This returns some data like so: 这将返回一些数据,如下所示:

CA40000
CA40001
CA40002
CA40003
CA40004
CA40005
.... etc

The troublesome value '41020-S' (Originally 'CA41020-S' after the Replace) isn't included in the second debug result set, as I'd expected. 正如我期望的那样,麻烦的值“ 41020-S”(最初是“替换”之后的“ CA41020-S”)未包含在第二个调试结果集中。

Can anyone help me explain this weird behaviour, and how I might possibly overcome it? 谁能帮助我解释这种奇怪的行为,以及我可能如何克服它?

There's no guarantee on the evaluation order of individual predicates in the WHERE clause * . WHERE子句*中 不能保证单个谓词的评估顺序。 (Nor does SQL Server guarantee not to evaluate expressions in the SELECT clause for values that should be filtered by the WHERE clause). (SQL Server也不保证不对SELECT子句中的表达式求值,而该表达式应由WHERE子句过滤)。

The strongest way to guarantee that your filters take effect is, unfortunately, to split your query into two separate queries - your first query performing the required filtering and putting its results into a temp table/table variable and your second query building from that and performing data conversions. 不幸的是,确保过滤器生效的最有效方法是将查询拆分为两个单独的查询-第一个查询执行所需的过滤并将其结果放入临时表/表变量中,第二个查询以此为基础进行构建并执行数据转换。 1 1个

The slightly weaker way that almost always works, except sometimes with aggregates it can be a bit funny 2 is to use CASE expressions instead: 几乎总是可以使用的稍微较弱的方法,除非有时使用聚合,这可能有点有趣 2是使用CASE表达式代替:

SELECT
  MAX(CASE WHEN iid.DepartmentCode = 'Filters'
AND ItemName LIKE 'CA4%____'
THEN CAST(REPLACE(ItemName, 'CA', '') AS INT)
ELSE 60000 END) + 1
FROM InventoryItem ii
JOIN InventoryItemDepartment iid
  ON ii.ItemCode = iid.ItemCode
WHERE CASE WHEN iid.DepartmentCode = 'Filters'
AND ItemName LIKE 'CA4%____'
THEN CAST(REPLACE(ItemName, 'CA', '') AS INT)
ELSE 60000 END < 41000

1 This goes against the usual advice to build a single large query and let the optimizer find the best way of evaluating your query. 1这与建立单个大型查询并让优化器找到评估查询的最佳方法的通常建议背道而驰。 Unfortunately, the optimizer often gets this one wrong and there's no indication that Microsoft plan to fix this since it's been a known issue for more than a decade + . 不幸的是,优化往往得到此一子错,而且也没有迹象表明,因为它是一个微软的计划来解决这个已知问题超过十年 +。

Note that any answer purporting to fix this just by rearranging the query (such as placing parts into subqueries) or adding additional guard clauses may superficially appear to fix the problem by accidentally forcing the optimizer to choose a different plan. 请注意,任何试图通过重新安排查询(例如将零件放入子查询中)或添加其他保护子句来解决此问题的答案,表面看来都是通过无意间迫使优化器选择了不同的计划来解决的。 But you've got no guarantee over if or when the optimizer will return to using a plan that does generate the error message again. 但是,您无法保证优化程序是否或何时返回使用再次生成错误消息的计划。

2 CASE : "In some situations, an expression is evaluated before a CASE statement receives the results of the expression as its input. Errors in evaluating these expressions are possible. Aggregate expressions that appear in WHEN arguments to a CASE statement are evaluated first, then provided to the CASE statement." 2 CASE :“在某些情况下,在CASE语句接收表达式的结果作为输入之前先对表达式求值。评估这些表达式时可能会出错。首先对出现在CASE语句的WHEN参数中的聚合表达式进行求值,然后再求值提供给CASE语句。”

* Unlike some other programming languages, SQL offers no guarantee such as left-to-right evaluation, nor any way to influence whether or not it exhibits any short-circuiting behaviour. *与某些其他编程语言不同,SQL不提供从左到右的评估之类的保证,也不提供任何方法来影响它是否表现出任何短路行为。

+ This issue was originally reported on User Voice. +此问题最初是在“用户语音”上报告的。 Unfortunately, in it's migration to the Azure feedback forum a lot of detail has been compressed into the single Microsoft "response" which makes it difficult to read, and "oh dear" also lost the substantial votes it had previously received on User Voise. 不幸的是,在迁移到Azure反馈论坛时,很多细节被压缩到单个Microsoft“响应”中,这使得它很难阅读,“哦,亲爱的”也失去了以前在User Voise上获得的大量票数。

Try this query, change the condition in where clause to check only four characters after CA4 尝试此查询,更改where子句中的条件以仅检查CA4之后的四个字符

SELECT
  MAX(CAST(REPLACE(ItemName, 'CA', '') AS INT)) + 1
FROM InventoryItem ii
JOIN InventoryItemDepartment iid
  ON ii.ItemCode = iid.ItemCode
WHERE iid.DepartmentCode = 'Filters'
AND ItemName LIKE 'CA4____'
AND CAST(REPLACE(ItemName, 'CA', '') AS INT) < 41000

Still above query may fail if execution plan chose to evaluate the condition CAST(REPLACE(ItemName, 'CA', '') AS INT) < 41000 first. 如果执行计划选择首先评估条件CAST(REPLACE(ItemName, 'CA', '') AS INT) < 41000则上述查询仍然可能失败。 To be fail safe you can use below query. 为了安全起见,您可以使用以下查询。

SELECT
  MAX(CAST(REPLACE(ItemName, 'CA', '') AS INT)) + 1
FROM 
(   SELECT ItemName
    FROM InventoryItem ii
    JOIN InventoryItemDepartment iid
      ON ii.ItemCode = iid.ItemCode
    WHERE iid.DepartmentCode = 'Filters'
    AND ItemName LIKE 'CA4____'
) AS SubQ
WHERE CAST(REPLACE(ItemName, 'CA', '') AS INT) < 41000

There are two learning points here: 这里有两个学习要点:

  1. Avoid using CAST and CONVERT in WHERE expressions wherever possible - they fragilize the query and reduce performance due to performing conversions on rows which will be excluded. 避免使用CASTCONVERTWHERE表达式尽可能-他们fragilize查询并降低性能,因为在其将被排除在外行执行转换。

  2. MAX() also works with string values. MAX()也适用于字符串值。

Assuming the values from which you want to find the maximum are in the range CA40000 to CA40999 and the data are well formed, apart from the occasional suffix like the 41020-S which broke your query, you could use : 假设要从中找到最大值的值在CA40000到CA40999的范围内,并且数据格式正确,除了偶尔出现的后缀(如41020-S)打乱了您的查询,您可以使用:

SELECT CAST(MAX(SUBSTRING(ItemName, 3, 5)) AS INT) + 1 FROM InventoryItem ii INNER JOIN InventoryItemDepartment iid ON ii.ItemCode = iid.ItemCode WHERE iid.DepartmentCode = 'Filters' AND ItemName LIKE 'CA40___%'

In a very large table, MAX(LEFT(ItemName, 7)) might be faster, because it can directly use an index on ItemName, but this makes the query a little more complicated. 在非常大的表中, MAX(LEFT(ItemName, 7))可能会更快,因为它可以直接使用ItemName上的索引,但这会使查询更加复杂。

If the data could have non-numeric values after CA40, one can use range matching with LIKE to avoid errors : LIKE 'CA40[0-9][0-9][0-9]%' 如果数据在CA40之后可能具有非数字值,则可以将范围匹配与LIKE配合使用以避免错误: LIKE 'CA40[0-9][0-9][0-9]%'

Use this 用这个

MAX(CAST(REPLACE(REPLACE(name, 'CA', ''),'-S','') AS INT)) + 1

In place of 代替

MAX(CAST(REPLACE(ItemName, 'CA', '') AS INT)) + 1

And for 而对于

CAST(REPLACE(REPLACE(name, 'CA', ''),'-S','') AS INT)

in place 到位

CAST(REPLACE(ItemName, 'CA', '') AS INT)

You can use ISNUMERIC if you want to remove any rows with unexpected values, such as having an unknown character in it. 如果要删除任何带有意外值(例如,其中包含未知字符)的行,则可以使用ISNUMERIC

SELECT
  MAX(CAST(REPLACE(ItemName, 'CA', '') AS INT)) + 1
FROM InventoryItem ii
JOIN InventoryItemDepartment iid
  ON ii.ItemCode = iid.ItemCode
WHERE iid.DepartmentCode = 'Filters'
AND ItemName LIKE 'CA4%____'
AND ISNUMERIC(CAST(REPLACE(ItemName, 'CA', '')) = 1 AND CAST(REPLACE(ItemName, 'CA', '') AS INT) < 41000.

Note: ISNUMERIC is not perfect. 注意: ISNUMERIC并不完美。 It will consider some characters as numeric too. 它将某些字符也视为数字。 You can read it here . 您可以在这里阅读。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM