转换为浮动忽略WHERE过滤器时出错

时间:2014-05-08 08:25:24

标签: sql sql-server tsql

我有一个包含varchar列的表,PaymentRef,包含各种文本和数字数据。此外,还有一个列,CurrencyAmount数据类型为float

PaymentRef               CurrencyAmount
----------               --------------
EUR 100,00               100
EUR 50,00                50
USD 25,00                25.2
Auth#: 98103             NULL
Auth#: 98104             NULL
Transferred from 2356    NULL
Transferred to 1356      NULL

现在,只要记录在“EUR ##,##”,“USD ##,##”等表格中包含PaymentRef,我就需要将货币代码后的数值与货币代码的值进行比较。 CurrencyAmount列。

以下查询可以正常运行:

SELECT CAST(REPLACE(SUBSTRING(PaymentRef, 5, 100), ',', '.') AS FLOAT) AS PaymentRefNumeric,
    CurrencyAmount
FROM MyTable
WHERE LEFT(PaymentRef, 3) IN ('EUR', 'USD', 'SEK')

但是,如果我尝试将转换值与CurrencyAmount列进行比较,如:

WITH CTE AS (
    SELECT CAST(REPLACE(SUBSTRING(PaymentRef, 5, 100), ',', '.') AS FLOAT) AS PaymentRefNumeric,
        CurrencyAmount
    FROM MyTable
    WHERE LEFT(PaymentRef, 3) IN ('EUR', 'USD', 'SEK')
)
SELECT * FROM CTE WHERE PaymentRefNumeric <> CurrencyAmount

我收到以下错误:

Msg 8114, Level 16, State 5, Line 2
Error converting data type varchar to float.

我知道我可以轻松地将第一个查询的结果放入临时表中,然后执行我的比较,但我想知道这个错误的原因是什么。在应用第一个WHERE过滤器之前,我认为它与查询优化器尝试将PaymentRef值CAST到FLOAT有关。有没有办法确保在投射值之前应用过滤器? (这就是我尝试使用CTE的原因,但即使没有CTE,我仍然会得到同样的错误。)

4 个答案:

答案 0 :(得分:4)

这是我的评论

中描述的解决方法
WITH CTE AS (
    SELECT case when LEFT(PaymentRef, 3) IN ('EUR', 'USD', 'SEK')
                then CAST(REPLACE(SUBSTRING(PaymentRef, 5, 100), ',', '.') AS FLOAT) 
                else null
                end AS PaymentRefNumeric,
        CurrencyAmount
    FROM #t WHERE LEFT(PaymentRef, 3) IN ('EUR', 'USD', 'SEK')
)
SELECT * FROM CTE WHERE PaymentRefNumeric <> CurrencyAmount

答案 1 :(得分:1)

你不能把CTE的where条件改为

WHERE CurrencyAmount IS NOT NULL

?您的示例数据看起来就可以了......查询可以使用已更改的条件......

您还可以将其他条件更改为

WHERE currencyamount is not null and PaymentRefNumeric <> CurrencyAmount

答案 2 :(得分:0)

使用IsNull函数:

WITH CTE AS (
    SELECT CAST(REPLACE(SUBSTRING(PaymentRef, 5, 100), ',', '.') AS FLOAT) AS PaymentRefNumeric,
        CurrencyAmount
    FROM MyTable
    WHERE LEFT(PaymentRef, 3) IN ('EUR', 'USD', 'SEK')
)
SELECT * FROM CTE WHERE PaymentRefNumeric <> IsNull(CurrencyAmount, -1) -- Check if is null so replce it

答案 3 :(得分:0)

使用ISNUMERIC检查转换是否成功,否则返回NULL。 这将涵盖所有基地。

对于SQL2012用户,可以使用TRYCAST功能。如果转换失败,则返回NULL。 只需将CASE表达式替换为:

SQL2012 TRY_CAST(REPLACE(SUBSTRING(PaymentRef, 5, 100), ',', '.'))

WITH CTE AS (
SELECT CASE WHEN 
         ISNUMERIC(REPLACE(SUBSTRING(PaymentRef, 5, 100), ',', '.'))=1
       THEN(REPLACE(SUBSTRING(PaymentRef, 5, 100), ',', '.') AS FLOAT) 
       ELSE NULL
       END PaymentRefNumeric
    ,CurrencyAmount
FROM MyTable
WHERE LEFT(PaymentRef, 3) IN ('EUR', 'USD', 'SEK')
)
SELECT * FROM CTE WHERE PaymentRefNumeric <> CurrencyAmount