为什么这个sql会引起类型转换错误?

时间:2018-08-02 07:09:46

标签: sql sql-server

path

此sql将导致错误

  

将varchar值“ hehe”转换为数据时转换失败   输入int。

但是表 series: [{ data: [{ y: 29.9, id: 'min' }, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, { y: 216.4, id: 'max' }, 194.1, 95.6, 54.4] }, { name: 'Fake annotations points', // To make this series hidden and unaccessible for user. type: 'scatter', marker: { enabled: false }, showInLegend: false, // data: [{ x: 0, y: 29.9, id: '1' }, { x: 0, y: 216.4, id: '2' }, { x: 8, y: 216.4, id: '3' }, { x: 8, y: 29.9, id: '4' }] }], annotations: [{ shapes: [{ type: 'path', points: ['1', '2', '3', '4'], }] }] }); 的值不等于前面的表WITH tb_testl AS ( SELECT 1 AS id ,'hehe' AS value UNION ALL SELECT 1 AS id, '1' AS value UNION ALL SELECT 2 AS id, '2' AS value UNION ALL SELECT 2 AS id, '2' AS value ), tb_test2 AS ( SELECT CONVERT(INT , value) AS value FROM tb_testl WHERE id = 2 ) SELECT * FROM tb_test2 WHERE value = 2; 中的值'hehe'。而且我发现,如果我不附加语句tb_test2,则此sql可以很好地工作。我已经尝试过tb_test1函数,但没有用。

版本:mssql2008 R2

3 个答案:

答案 0 :(得分:2)

SQL Server不保证语句的处理顺序(下面有一个例外)。也就是说,不能保证WHERE过滤发生在SELECT之前。或先评估一个CTE。之所以认为这是优势,是因为它允许SQL Server重新安排处理以优化性能(尽管我认为您会看到一个错误)。

显然,问题出在代码的这一部分:

tb_test2 AS (
    SELECT CONVERT(INT, value) AS value
    FROM tb_testl
    WHERE id = 2 
)

(嗯,实际上是引用tb_test2的地方。)

发生的事情是SQL Server将CONVERT()推送到要读取值的位置,因此在WHERE子句处理之前 尝试了转换。因此,错误。

在SQL Server 2012+中,您可以使用TRY_CNVERT()轻松解决此问题:

tb_test2 AS (
    SELECT TRY_CONVERT(INT, value) AS value
    FROM tb_testl
    WHERE id = 2 
)

但是,这在SQL Server 2008中不起作用。您可以使用以下事实:CASE在处理顺序上确实有一些保证:

tb_test2 AS (
    SELECT (CASE WHEN value NOT LIKE '%[^0-9]%' THEN CONVERT(INT, value)
            END) AS value
    FROM tb_testl
    WHERE id = 2 
)

答案 1 :(得分:1)

这部分陈述引起的错误

), tb_test2 AS (
    SELECT CONVERT(INT , value) AS value FROM tb_testl WHERE id = 2

的类型为 varchar ,并且'hehe'值不能转换为整数

WITH tb_testl AS (
   SELECT 1 AS id ,'hehe' AS value 

更新:sql尝试在您的语句中将所有值转换为整数。避免将错误改写为

WITH tb_testl AS (
 SELECT 1 AS id ,'hehe' AS value 
 UNION ALL SELECT 1 AS id, '1' AS value 
 UNION ALL SELECT 2 AS id, '2' AS value 
 UNION ALL SELECT 2 AS id, '2' AS value
), tb_test2 AS (
 SELECT value AS value FROM tb_testl WHERE id = 2 
),
 tb_test3 AS (
 SELECT cast(value as int) AS value FROM tb_test2
)
SELECT * FROM tb_test3

答案 2 :(得分:1)

关于发生这种情况的原因:

有一个Logical Processing Order,它描述了子句的计算顺序。顺序是:

FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP

SET SHOWPLAN_ALL ON时也可以看到处理顺序。对于此查询,处理如下:

  1. 恒定扫描-这是FROM子句,它由硬编码值和常量组成。
  2. 过滤器-这是WHERE子句。看起来好像有两个where子句(WHERE id = 2WHERE value = 2)。 SQL Server对此有不同的看法,它只考虑一个WHERE子句:WHERE CONVERT(INT , value) = 2 AND id = 2
  3. 计算缩放器。这是select中的CONVERT函数。

因为两个WHERE子句同时执行,所以hehe的值不会从CONVERT范围中过滤出来。

有效地,查询简化为:

SELECT  CONVERT(INT, tb_testl.value) AS Cvalue
FROM    (
            SELECT  1 AS id
            ,       'hehe' AS value
            UNION ALL
            SELECT  1 AS id
            ,       '1' AS value
            UNION ALL
            SELECT  2 AS id
            ,       '2' AS value
            UNION ALL
            SELECT  2 AS id
            ,       '2' AS value
        ) tb_testl
WHERE   CONVERT(INT, tb_testl.value) = 2
    AND tb_testl.id = 2

应阐明错误发生的原因。

使用SQL时,您无法以与命令式语言(如C)相同的方式读取代码。SQL代码行不一定(实际上实际上根本就根本没有)按照写入时的顺序执行。在这种情况下,认为内部位置在之前在外部位置执行是错误的。