View Alias上的WHERE导致错误

时间:2012-11-01 17:09:24

标签: sql-server tsql

我有这样的观点:

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。

为什么呢?由于位置必须采用1-2-100-0800-A格式,因此我看不出转换错误的原因。 CAST有可能在WHERE有机会过滤结果之前失败吗?如果是这样,为什么第一个查询有效?

编辑 - 工作环境

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

这是在SELECT for ProcessCode中:

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, 

3 个答案:

答案 0 :(得分: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]-_')

请参阅此Connect item

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从表中进行单个检索,同时采用{​​{1}}和location两个表达式。

答案 1 :(得分: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

这是小提琴&gt; 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]-_')
)

答案 2 :(得分:0)

演员在那之前失败了,是的。事实上,演员在SELECT之前就失败了,如果我没有弄错的话,就在它看着桌子并确定位置栏下的子串的形状之后。

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

将您的陈述分解为较小的陈述。我先看看SUBSTRING的结果。

希望有所帮助。