我有一个包含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,我仍然会得到同样的错误。)
答案 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