use*_*840 21 sql-server varchar numeric
我有一张桌子:
Account_Code | Desc
503100 | account xxx
503103 | account xxx
503104 | account xxx
503102A | account xxx
503110B | account xxx
Run Code Online (Sandbox Code Playgroud)
哪里Account_Code
是varchar
.
当我在下面创建查询时:
Select
cast(account_code as numeric(20,0)) as account_code,
descr
from account
where isnumeric(account_code) = 1
Run Code Online (Sandbox Code Playgroud)
它通过返回account_code
列中具有有效数值的所有记录运行良好.
但是当我尝试添加另一个select时,嵌套到先前的sql:
select account_code,descr
from
(
Select cast(account_code as numeric(20, 0)) as account_code,descr
from account
where isnumeric(account_code) = 1
) a
WHERE account_code between 503100 and 503105
Run Code Online (Sandbox Code Playgroud)
查询将返回错误
将数据类型varchar转换为数字时出错.
那里发生了什么?
如果account_code
有效,我已经转换为数字,但似乎查询仍在尝试处理无效的记录.
我需要BETWEEN
在查询中使用 子句.
Eri*_*ikE 29
SQL Server 2012及更高版本
只需使用Try_Convert
:
TRY_CONVERT获取传递给它的值并尝试将其转换为指定的data_type.如果转换成功,则TRY_CONVERT返回值作为指定的data_type; 如果发生错误,则返回null.但是,如果您请求明确不允许的转换,则TRY_CONVERT将失败并显示错误.
SQL Server 2008和早期版本
处理此问题的传统方法是使用case语句保护每个表达式,这样无论何时进行求值,它都不会产生错误,即使它在逻辑上似乎不需要CASE语句.像这样的东西:
SELECT
Account_Code =
Convert(
bigint, -- only gives up to 18 digits, so use decimal(20, 0) if you must
CASE
WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL
ELSE X.Account_Code
END
),
A.Descr
FROM dbo.Account A
WHERE
Convert(
bigint,
CASE
WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL
ELSE X.Account_Code
END
) BETWEEN 503100 AND 503205
Run Code Online (Sandbox Code Playgroud)
但是,我喜欢在SQL Server 2005及更高版本中使用此类策略:
SELECT
Account_Code = Convert(bigint, X.Account_Code),
A.Descr
FROM
dbo.Account A
OUTER APPLY (
SELECT A.Account_Code WHERE A.Account_Code NOT LIKE '%[^0-9]%'
) X
WHERE
Convert(bigint, X.Account_Code) BETWEEN 503100 AND 503205
Run Code Online (Sandbox Code Playgroud)
这样做的目的是在Account_Code
值不是数字时策略性地将值切换到表格NULL
内部X
.我最初使用CROSS APPLY
但是正如Mikael Eriksson恰如其分地指出的那样,这导致了同样的错误,因为查询解析器遇到了完全相同的问题,即优化了我强制表达顺序的尝试(谓词下推击败了它).通过切换到OUTER APPLY
它改变了操作的实际含义,因此X.Account_Code
可能包含NULL
外部查询中的值,因此需要适当的评估顺序.
您可能有兴趣阅读有关此评估订单问题的Erland Sommarskog的Microsoft Connect请求.他实际上称之为一个错误.
这里还有其他问题,但我现在无法解决.
PS我今天有头脑风暴.我建议的"传统方式"的替代方案是SELECT
带有外部引用的表达式,它也可以在SQL Server 2000中使用.(我注意到,自从学习以来,CROSS/OUTER APPLY
我已经使用旧的SQL Server版本改进了我的查询功能 - -as我得到更灵活的与"外部参考"功能SELECT
,ON
以及WHERE
条款!)
SELECT
Account_Code =
Convert(
bigint,
(SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%')
),
A.Descr
FROM dbo.Account A
WHERE
Convert(
bigint,
(SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%')
) BETWEEN 503100 AND 503205
Run Code Online (Sandbox Code Playgroud)
它比CASE
声明短得多.
无法保证SQL Server 在子句中运行过滤器之前不会尝试执行CONVERT
to .numeric(20,0)
WHERE
并且,即使它确实存在,ISNUMERIC
也是不够的,因为它识别£
并且1d4
是数字,它们都不能被转换为numeric(20,0)
.(*)
将其拆分为两个单独的查询,第一个查询过滤结果并将它们放在临时表或表变量中,第二个执行转换.(子查询和CTE不足以阻止优化器在过滤器之前尝试转换)
对于您的过滤器,可能使用account_code not like '%[^0-9]%'
而不是ISNUMERIC
.
(*)ISNUMERIC
回答了一个问题:没有人(据我所知)一直想问 - "这个字符串是否可以转换为任何数值数据类型 - 我不关心哪个?" - 显然,大多数人想问的是"这个字符串可以转换成x吗?" 其中x
是特定的目标数据类型.
如果您运行的是SQL Server 2012,还可以使用新的TRY_PARSE()函数:
返回表达式的结果(转换为请求的数据类型),如果在SQL Server 2012中转换失败,则返回null.仅将TRY_PARSE用于从字符串转换为日期/时间和数字类型.
归档时间: |
|
查看次数: |
220451 次 |
最近记录: |