我有这样的观点:
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,
答案 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)
你似乎在计算返回的子字符串是错误的,因为如果你说格式确实是1-2-100-0800-A,那么SUBSTRING(str,9,4)应该返回0800,但它返回'-1-',这不是INT。
将您的陈述分解为较小的陈述。我先看看SUBSTRING的结果。
希望有所帮助。