我想在我的数据库中取一个列的平均值。该列为AMOUNT
,并存储为NVARCHAR(300),null
。
当我尝试将其转换为数值时,我收到以下错误:
Msg 8114,Level 16,State 5,Line 1
时出错
将数据类型NVARCHAR转换为NUMBER
这就是我现在所拥有的。
SELECT AVG(CAST(Reimbursement AS DECIMAL(18,2)) AS Amount
FROM Database
WHERE ISNUMERIC(Reimbursement) = 1
AND Reimbursement IS NOT NULL
答案 0 :(得分:9)
你会认为你的代码会起作用。但是,SQL Server不保证WHERE
子句在SELECT
转换发生之前过滤数据库。在我看来这是一个错误。在微软看来,这是一个优化功能。
因此,您的WHERE
无法保证正常运作。即使使用CTE也无法解决问题。
SQL Server 2012 +中提供了最佳解决方案TRY_CONVERT()
:
SELECT AVG(TRY_CONVERT(DECIMAL(18,2), Reimbursement)) AS Amount
FROM Database
WHERE ISNUMERIC(Reimbursement) = 1 AND Reimbursement IS NOT NULL;
在早期版本中,您可以使用CASE
。 CASE
确实保证子句的顺序排序,因此:
SELECT AVG(CASE WHEN ISNUMERIC(Reimbursement) = 1 AND Reimbursement IS NOT NULL
THEN CONVERT(DECIMAL(18,2), Reimbursement))
END)
FROM Database;
由于AVG()
会忽略NULL
个值,因此WHERE
不是必需的,但如果您愿意,可以将其包括在内。
最后,您可以使用计算列简化代码:
alter database add Reimbursement_Value as
(CASE WHEN ISNUMERIC(Reimbursement) = 1 AND Reimbursement IS NOT NULL
THEN CONVERT(DECIMAL(18,2), Reimbursement))
END);
然后你可以把代码写成:
select avg(Reimbursement_Value)
from database
where Reimbursement_Value is not null;
答案 1 :(得分:2)
Quote from MSDN ...
对于某些非数字字符,例如加号(+),减号( - )和有效货币符号(如美元符号($)),ISNUMERIC返回1。有关货币符号的完整列表,请参阅money和smallmoney
select isnumeric('+')---1
select isnumeric('$')---1
所以尽量添加以避免非数字数字弄乱您的输出..
WHERE Reimbursement NOT LIKE '%[^0-9]%'
如果你在SQLServer 2012上,你可以尝试使用TRY_Convert,它为转换失败输出null。
SELECT AVG(try_convert( DECIMAL(18,2),Reimbursement))
from
table
答案 2 :(得分:0)
我猜测,因为它是Nvarchar,你会在那里找到一些带有' $','。'或a(,)的值。我会运行一个查询:
SELECT Amount
FROM database
WHERE Amount LIKE '%$%' OR
Amount LIKE '%.%' OR
Amount LIKE '%,%'
看看你得到了什么,我猜你会得到一些行返回,然后更新这些行并重新尝试。
目前,您的查询会提取所有不是全数字的数字,这也是它失败的原因。而是尝试运行:
SELECT AVG(CAST(Reimbursement AS DECIMAL(18,2)) AS Amount
FROM Database
--Changed ISNUMERIC() = to 0 for true so it will only pull numeric numbers.
WHERE ISNUMERIC(Reimbursement) = 0 and Reimbursement IS NOT NULL