克服SQL缺少AND条件的短路

时间:2014-01-27 16:34:13

标签: sql-server

我正在处理审核日志,并希望忽略NULL值更改为零(或保持为NULL)的条目。无论记录的字段类型如何,旧值和新值都保存在NVARCHAR字段中。为了将一个新值CAST为十进制,以确定它是否为零,我需要限制该字段的ISNUMERIC返回1的情况。

我已经使用了这个奇怪的SQL - 但确信必须有更好的方法。

WHERE MessageDesc LIKE 'poitem%'
AND NOT(OldValue IS NULL AND 0.0 =
    CASE
        WHEN ISNUMERIC(NewValue) = 1 THEN CAST(NewValue AS DECIMAL(18,4))
        WHEN NewValue IS NULL THEN 0.0
        ELSE 1.0
    END)

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

SQL Server 2012添加了一个Try_Convert函数,如果该值无法作为给定类型转换,则返回NULL。 http://technet.microsoft.com/en-us/library/hh230993.aspx

WHERE NOT (OldValue is Null AND 
    (NewValue is null OR try_convert(float, NewValue) = 0.0)
)

如果使用2012年之前的版本,请查看Damien_The_Unbeliever的回答:Check if a varchar is a number (TSQL) ...根据Aaron的评论,这在所有情况下都不起作用。

由于您使用的是SQL 2008,因此它出现了isnumeric的组合,并且上面链接中Damien的修改版本的答案将起作用。您在问题中的当前解决方案会出现“。”,“ - ”,货币符号($等)以及“1e4”等科学记数法等问题。

尝试使用SQL 2008(这里是带有测试用例的SQLFiddle:http://sqlfiddle.com/#!3/fc838/3):注意:如果文本有逗号(例如:1,000)或带有parens的会计符号,此解决方案不会将文本值转换为数字(例如:使用“(1)”表示“-1”),因为SQL Server在尝试强制转换为十进制时会抛出错误。

WHERE t.OldValue is null
AND 
(
    t.NewValue is null
    OR
    0.0 = 
    case 
        when isnumeric(t.NewValue) = 1 
            --Contains only characters that are numeric, negative sign, or decimal place. 
            --This is b/c isnumeric will return true for currency symbols, scientific notation, or '.'
            and not (t.NewValue like '%[^0-9.\-\+]%' escape '\')  
            --Not other single char values where isnumeric returns true.
            and t.NewValue not in ( '.', '-', '+')
        then cast(t.NewValue as decimal(18,4)) 
        else null --can't convert to a decimal type 
    end
)

答案 1 :(得分:0)

避免使用ISNUMERIC(),因为'。'。

会出现问题
-- Dot comes up as valid numeric
select 
  ISNUMERIC('.') as test1,
  ISNUMERIC('1A') as test2,
  ISNUMERIC('1') as test3,
  ISNUMERIC('A1') as test4

-- Results window (text)
test1       test2       test3       test4
----------- ----------- ----------- -----------
1           0           1           0

改用COALESCE()。

WHERE MessageDesc LIKE 'poitem%'
AND 
    NOT (OldValue IS NULL AND 
    CAST(COALESCE(NewValue, '0') AS DECIMAL(18,4)) = 0)