这个简单的UPDATE SQL是否等待发生错误?如何改写呢?

时间:2015-08-10 21:31:09

标签: sql sql-server

我需要检查ACC_1的ACCT_NUMS值。如果ACCT_NUM以“GF0”为前缀,那么我需要忽略“GF0”前缀并取出剩余字符串的最右边7个字符。如果在account_x_master或CW_CLIENT_STAGE中找不到此结果值,则该记录将被标记为错误。

以下似乎可以解决问题,但我有一个问题......

UPDATE
    table_1
SET
    Error_Ind = 'GW001'
WHERE
    LEFT(ACCT_NUM, 3) = 'GF0'
    AND RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7) NOT IN           
    (
        SELECT
            acct_num
        FROM
            account_x_master
    ) 
    AND RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7) NOT IN              
    (                                   
        SELECT                          
            CW_CLIENT_STAGE.AGS_NUM     
        FROM                            
            dbo.CW_CLIENT_STAGE         
    )     

我担心的是SQL Server可能会尝试执行SUBSTRING操作

SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3)

导致计算出的负值并导致SQL失败。当然,这不会失败是SUBSTRING操作只应用于那些我们至少3个字符长的记录,如果

那将永远如此
LEFT(ACCT_NUM, 3) = 'GF0'

首先应用。如果可能的话,我想避免在表格中添加新列。简化和减少开销的奖励点: - )

如何重写此UPDATE SQL以防止此问题?

3 个答案:

答案 0 :(得分:2)

您有一个非常有效的顾虑,因为SQL Server将重新排列WHERE中表达式的评估顺序。

保证SQL语句中操作顺序的唯一方法是使用case。我认为没有办法捕捉substring()失败的电话。 。 。没有try_substring()类似于try_convert()

所以:

WHERE
    LEFT(ACCT_NUM, 3) = 'GF0' AND
    (CASE WHEN LEN(ACCT_NUM) > 3 THEN RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7) END) NOT IN (SELECT acct_num
                      FROM account_x_master
                     ) AND
    (CASE WHEN LEN(ACCT_NUM) > 3 THEN RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7) END) NOT IN (SELECT CW_CLIENT_STAGE.AGS_NUM     
                      FROM dbo.CW_CLIENT_STAGE         
                     ) 

这是更加丑陋的。并且,可能存在各种方法,例如使用带有通配符的LIKE而不是字符串操作。但是,case将保证SUBSTRING()仅在字符串上运行足够长的时间,因此不会产生错误。

答案 1 :(得分:2)

正如其他人所说,你的担忧是有效的。

我会对您的查询进行两处更改。

1)为了避免在SUBSTRING参数中出现负值,我们可以使用STUFF重写它:

SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3)

相当于:

STUFF(ACCT_NUM, 1, 3, '')

我们不是提取字符串的尾部,而是用空字符串替换前三个字符。如果字符串短于3个字符,则结果为空字符串。

顺便说一句,如果您的ACCT_NUM可能以空格结尾,则SUBSTRING版本会对其进行裁剪,因为LEN不会计算尾随空格。

2)而不是

LEFT(ACCT_NUM, 3) = 'GF0'

使用:

ACCT_NUM LIKE 'GF0%'

如果您在ACCT_NUM上有索引且只有相对较少的行以GF0开头,则将使用索引。如果使用函数,例如LEFT,则无法使用索引。

因此,最终的查询变为:

UPDATE
    table_1
SET
    Error_Ind = 'GW001'
WHERE
    ACCT_NUM LIKE 'GF0%'
    AND RIGHT(STUFF(ACCT_NUM, 1, 3, ''), 7) NOT IN
    (
        SELECT
            acct_num
        FROM
            account_x_master
    ) 
    AND RIGHT(STUFF(ACCT_NUM, 1, 3, ''), 7) NOT IN
    (                                   
        SELECT                          
            CW_CLIENT_STAGE.AGS_NUM     
        FROM                            
            dbo.CW_CLIENT_STAGE         
    )

答案 2 :(得分:1)

请尝试以下查询。 由于SQL and or子句中没有短路WHERE,因此只能通过CASE语法实现。

我注意到您在NOT IN的不同部分进行了两次WHERE次比较,我将其合并为一次。 请注意,CASE条件为>=3而不是>3,因为RIGHT('',x)是允许的。 另请注意CASENOT IN

的正确使用
UPDATE     table_1
SET
    Error_Ind = 'GW001'
    select * from table_1
WHERE
    LEFT(ACCT_NUM, 3) = 'GF0'
     AND CASE 
            WHEN LEN(ACCT_NUM)>=3 
            THEN RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7)
            ELSE NULL END NOT IN           
                (
                SELECT  acct_num as num
                    FROM    account_x_master
                UNION 
                SELECT  CW_CLIENT_STAGE.AGS_NUM   as num
                    FROM   dbo.CW_CLIENT_STAGE         
                )