CTE返回错误

时间:2016-12-12 21:13:20

标签: sql-server-2008 tsql

我写了一个CTE来从数据集中删除非数值,然后计算一个范围内的数值。

WITH    dtr
      AS ( SELECT   resultlevel r
           FROM     dbo.Result
           WHERE    DrugID = 'AMP'
                    AND ISNUMERIC(ResultLevel) = 1
                    AND AuditStamp > '1/1/2016'
                    AND DeleteFlag = 0
         )
SELECT  COUNT(*)
FROM    dtr
WHERE   CONVERT(INT, r) BETWEEN 50 AND 75

这会在SMS中返回错误

Msg 245, Level 16, State 1, Line 2
Conversion failed when converting the varchar value 'PND            ' to data type int.

如果没有' dtr'这个错误是完全可能的。在CTE中查询。

当我重写这个时,而不是CTR,而是一个TEMP表,它可以工作。

SELECT  resultlevel r
INTO    #d
FROM    dbo.Result
WHERE   DrugID = 'AMP'
        AND ISNUMERIC(ResultLevel) = 1
        AND AuditStamp > '1/1/2016'
        AND DeleteFlag = 0

SELECT  COUNT(*)
FROM    #d
WHERE   CONVERT(INT, r) BETWEEN 50 AND 75

所以我的问题是为什么?我一直认为CTE就像创建TEMP表一样。

测试数据

if object_id('tempdb..#temp') is not null drop table #temp

create table #temp (result char(5)) 
insert into #temp (result) values 
('1'),('A'),('>2'),('PEN ') ,('@3'),('-2'),('-33') 


;with isnum AS ( 
SELECT result 
FROM #temp 
WHERE ISNUMERIC(result) = 1)

--Selecting from the CTE yields 1, -2, and -33 all of which can be converted to INT
--Running the query with the where clause causes the conversion error
SELECT 
    result, 
    ISNUMERIC(result)
FROM isnum
--WHERE CONVERT(INT,result) > 1

1 个答案:

答案 0 :(得分:3)

SQL Server中有Logical Processing Order of the SELECT statement,它确定在后续步骤中一个步骤中定义的对象何时可用于子句:

  1. FROM
  2. ON
  3. JOIN
  4. WHERE
  5. GROUP BY
  6. WITH CUBE或WITH ROLLUP
  7. HAVING
  8. 选择
  9. DISTINCT
  10. ORDER BY
  11. TOP
  12. 这是您的查询将被处理的方式,并且您的查询看起来非常好。但有时,SQL Server决定不遵循此顺序以优化您的查询。

    在您的情况下,在应用SQL Server过滤之前,convert可能只是简单地将您的查询转换为另一个并执行where isnumeric函数。

    如果我们使您的查询更复杂(但仍然给出相同的结果),SQL Server这次正确执行代码:

    ;with isnum AS ( 
        SELECT result
        FROM #temp 
        WHERE ISNUMERIC(result) = 1
        GROUP BY result
        HAVING MAX(result) = result
    )
    SELECT 
        result, 
        ISNUMERIC(result)
    FROM isnum
    WHERE CONVERT(INT,result) > 1;
    

    在你的情况下(这就是我在这种情况下所做的事情,当不同类型存储在一列中时),你可以简单地使用TRY_CONVERT函数:

    ;with isnum AS ( 
    SELECT result 
    FROM #temp 
    WHERE ISNUMERIC(result) = 1)
    
    SELECT 
        result, 
        ISNUMERIC(result)
    FROM isnum
    WHERE TRY_CONVERT(INT, result) > 1