这个简单的SQL更新是一个等待发生的错误吗?如何重写它?

这个简单的SQL更新是一个等待发生的错误吗?如何重写它?,sql,sql-server,Sql,Sql Server,我需要检查表1中的科目数值。如果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(A

我需要检查表1中的科目数值。如果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(ACCT_NUM, 4, LEN(ACCT_NUM) - 3)
这将导致计算的负值并导致SQL失败。当然,这不会失败,因为子字符串操作只应用于那些我们至少有3个字符长的记录,如果

LEFT(ACCT_NUM, 3) = 'GF0'
我们先申请。如果可能,我希望避免向表中添加新列。简化和减少开销的奖励积分:-)


如何重写此更新SQL以防止出现这种情况?

您有一个非常值得关注的问题,因为SQL Server将重新排列
WHERE
中表达式的求值顺序

保证SQL语句中操作顺序的唯一方法是使用
case
。我认为没有办法捕获对
substring()
的失败调用。没有类似于
try\u convert()
try\u substring()

因此:


这更难看。而且,可能有办法解决这个问题,比如使用通配符而不是字符串操作的
LIKE
。但是,
案例
将保证
子字符串()
只在足够长的字符串上运行,因此不会生成错误。

您有一个非常合理的担忧,因为SQL Server将重新排列
WHERE
中表达式的求值顺序

保证SQL语句中操作顺序的唯一方法是使用
case
。我认为没有办法捕获对
substring()
的失败调用。没有类似于
try\u convert()
try\u substring()

因此:


这更难看。而且,可能有办法解决这个问题,比如使用通配符而不是字符串操作的
LIKE
。但是,
案例将保证
子字符串()
只在足够长的字符串上运行,因此不会生成错误。

请尝试以下查询。 由于SQL
WHERE
子句中不存在短路
和或
,所以实现的唯一方法是通过
CASE
语法

我注意到您在
的不同部分有两个
不在
比较中,其中
合并为一个。 请注意,
CASE
条件是
=3
,而不是
>3
,因为允许
右(“”,x)
。 还要注意将
CASE
不在一起时的正确用法

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         
                ) 

请尝试下面的查询。 由于SQL
WHERE
子句中不存在短路
和或
,所以实现的唯一方法是通过
CASE
语法

我注意到您在
的不同部分有两个
不在
比较中,其中
合并为一个。 请注意,
CASE
条件是
=3
,而不是
>3
,因为允许
右(“”,x)
。 还要注意将
CASE
不在一起时的正确用法

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         
                ) 

正如其他人所说,你的担心是正确的

我将对你的查询做两个更改

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

SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3)
相当于:

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

顺便说一下,如果您的
ACCT_NUM
可能以空格结尾,它们将被
子字符串
版本修剪,因为
LEN
不计算尾随空格

2) 而不是

LEFT(ACCT_NUM, 3) = 'GF0'
使用:

如果在
ACCT\u 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         
    )

正如其他人所说,你的担心是正确的

我将对你的查询做两个更改

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

SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3)
相当于:

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

顺便说一下,如果您的
ACCT_NUM
可能以空格结尾,它们将被
子字符串
版本修剪,因为
LEN
不计算尾随空格

2) 而不是

LEFT(ACCT_NUM, 3) = 'GF0'
使用:

如果在
ACCT\u 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         
    )

比那更糟;在2012年之前的SQL版本中(甚至可能在更高版本中),即使是
CASE
在所有情况下都可能无法确定地短路:在这种情况下,使用
STUFF
而不是
SUBSTRING
,可以完全避免问题,正如我在回答中所解释的那样;在2012年之前的SQL版本中(甚至可能在更高的版本中),即使是
CASE
也不能在所有情况下都确定地短路:在这种情况下,可以使用
STUFF
而不是
子字符串来完全避免问题,正如我在回答中解释的那样。