5

我有这样的看法:

SELECT location, CAST(SUBSTRING(location, 9, 4) AS int) AS ProcessCode
FROM   dbo.asset
WHERE  (status NOT IN ('INACTIVE', 'NOT READY', 'LIMITEDUSE'))
AND (location LIKE '[1-6]-[12]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-_')

如果我这样称呼它,它会起作用:

SELECT * FROM FooView

但是,如果我添加 WHERE 子句:

SELECT * FROM FooView WHERE ProcessCode > 0

我收到此错误:

将 varchar 值“-01-”转换为数据类型 int 时转换失败。

为什么?由于 location 必须是 format 1-2-100-0800-A,我看不出怎么会出现转换错误。是否有可能在有机会过滤结果CAST之前失败?WHERE如果是这样,那么为什么第一个查询有效?

编辑 - 解决方法

我刚刚让一位同事建议了一个好的解决方法。它有效,但它仍然没有解释为什么最初的问题。

这是在 ProcessCode 的 SELECT 中:

CASE WHEN location LIKE '[1-6]-[12]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-_'
THEN CAST(SUBSTRING(location, 9, 4) AS int) ELSE 0 END AS ProcessCode, 
4

3 回答 3

4

改变你的看法

SELECT location,
       CASE WHEN SUBSTRING(location, 9, 4) > ''
             AND SUBSTRING(location, 9, 4) NOT LIKE '%[^0-9]%' THEN
                 CAST(SUBSTRING(location, 9, 4) AS int) END AS ProcessCode
  FROM dbo.asset
 WHERE (status NOT IN ('INACTIVE', 'NOT READY', 'LIMITEDUSE'))
   AND (location LIKE '[1-6]-[12]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-_')

看到这个连接项目

SQL Server 可以按照它决定优化的顺序自由地评估 WHERE/SELECT 子句。除非具体化为 INDEXED VIEW 的视图被扩展为外部查询,因此您的 WHERE 子句实际上被精简到视图中,即

SELECT * FROM FooView WHERE ProcessCode > 0
 -- is really seen as
SELECT location, CAST(SUBSTRING(location, 9, 4) AS int) AS ProcessCode
FROM   dbo.asset
WHERE  (status NOT IN ('INACTIVE', 'NOT READY', 'LIMITEDUSE'))
AND (location LIKE '[1-6]-[12]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-_')
AND CAST(SUBSTRING(location, 9, 4) AS int) > 0 ---- << Expanded inline

因为该表达式同时用于 SELECT 和 WHERE 子句,所以似乎 SQL Server 已决定在原始检索中首先解析 SELECT 子句中的表达式。通过使用 Ctrl-L 查看查询执行计划可以很容易地看到这一点。您将看到 SQL Server 从表中进行一次检索,同时采用 2 个表达式,beinglocationCAST(SUBSTRING(location, 9, 4) AS int)

于 2012-11-01T18:29:07.667 回答
1

这适用于 Sql Server 2008,其他一些时髦的事情正在发生......

create view myview
AS
SELECT  CAST(SUBSTRING('1-2-100-0800-A', 9, 4) AS int) as ProcessCode
Where '1-2-100-0800-A' LIKE '[1-6]-[12]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-_'

GO

SELECT * FROM myview WHERE ProcessCode > 0

这是小提琴> http://sqlfiddle.com/#!3/3bcfd/2

编辑可能是下面建议的执行顺序,用 in 试试(用 ids 优化)

SELECT location, CAST(SUBSTRING(location, 9, 4) AS int) AS ProcessCode
FROM   dbo.asset 
Where id in(
 select id
 from dbo.asset 
 WHERE  (status NOT IN ('INACTIVE', 'NOT READY', 'LIMITEDUSE'))
 AND (location LIKE '[1-6]-[12]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-_')
)
于 2012-11-01T18:13:05.653 回答
0

演员在哪里之前失败了,是的。实际上,如果我没记错的话,演员表在 SELECT 本身之前就失败了,就在它查看表格并确定位置列下的子字符串的形状之后。

您似乎对返回的子字符串是什么进行了错误计算,因为如果您说格式确实是 1-2-100-0800-A,那么 SUBSTRING(str,9, 4) 应该返回 0800,但它正在返回'-1-',它不是一个 INT。

将你的陈述分解成更小的陈述。我会先查看 SUBSTRING 的结果。

希望有帮助。

于 2012-11-01T17:22:56.333 回答